Пример #1
0
        private static void WriteProps(BinaryWriter b, SoilProps soilProps)
        {
            byte[]          bytes;
            BinaryFormatter formatter = new BinaryFormatter();

            using (MemoryStream stream = new MemoryStream())
            {
                formatter.Serialize(stream, soilProps);
                bytes = stream.ToArray();
            }
            b.Write(bytes);
        }
Пример #2
0
 private static void WriteProps(BinaryWriter b, SoilProps soilProps)
 {
     byte[] bytes;
     BinaryFormatter formatter = new BinaryFormatter();
     using (MemoryStream stream = new MemoryStream())
     {
         formatter.Serialize(stream, soilProps);
         bytes = stream.ToArray();
     }
     b.Write(bytes);
 }
Пример #3
0
 public static FluxTable TwoTables(FluxTable ft1, SoilProps sp1, FluxTable ft2, SoilProps sp2)
 {
     return(new FluxTable());
 }
Пример #4
0
        // The above parameters can be varied, but the defaults should usually be ok.

        //subroutine to generate the property values.
        public static SoilProps gensptbl(double dzmin, SoilParam sPar, bool Kgiven)
        {
            int nlimax = 220;
            int i, j, nli1, nc, nld;

            double[] h   = new double[nlimax + 3];
            double[] lhr = new double[nlimax + 3];
            double[] K   = new double[nlimax + 3];
            double[] phi = new double[nlimax + 3];
            double[] S   = new double[nlimax + 3];
            double[] cco = new double[4];

            //diags - timing starts here

            SoilProps sp = new SoilProps();

            sp.sid = sPar.sid; sp.ths = sPar.ths; sp.ks = sPar.ks;
            sp.he  = sPar.he; sp.phie = phie; sp.S = S; sp.n = sPar.layers;

            // Find start of significant fluxes.
            hdry   = sPar.hd;                 // normally -1e7 cm
            phidry = 0.0;
            hwet   = Math.Min(sPar.he, -1.0); // h/hwet used for log spacing

            if (Kgiven)
            {
                dlh    = Math.Log(10.0) / 3.0; // three points per decade
                x      = Math.Exp(-dlh);       // x*x*x=0.1
                h[0]   = hdry;
                K[0]   = MVG.Kofh(h[0]);
                phi[0] = 0.0;
                for (i = 1; i < nlimax; i++) // should exit well before nlimax
                {
                    h[i] = x * h[i - 1];
                    K[i] = MVG.Kofh(h[i]);
                    // Get approx. phi by integration using dln(-h).
                    phi[i] = phi[i - 1] - 0.5 * (K[i] * h[i] - K[i - 1] * h[i - 1]) * dlh;
                    if (phi[i] > qsmall * dzmin)
                    {
                        break; // max flux is approx (phi-0)/dzmin
                    }
                }
                if (i > nlimax)
                {
                    Console.WriteLine("gensptbl: start of significant fluxes not found");
                    Environment.Exit(1);
                }
                hdry   = h[i - 1];
                phidry = phi[i - 1];
            }
            else
            {
                // Calculate K and find start of significant fluxes.
                Props(ref sp, hdry, phidry, lhr, h, Kgiven);
                for (i = 2; i < sp.n; i++)
                {
                    if (phi[i] > qsmall * dzmin)
                    {
                        break;
                    }
                }
                if (i > sp.n)
                {
                    Console.WriteLine("gensptbl: start of significant fluxes not found");
                    Environment.Exit(1);
                }
                i      = i - 1;
                hdry   = h[i];
                phidry = phi[i];
            }

            // hdry and phidry are values where significant fluxes start.
            // Get props.
            sp.Kc = new double[sp.n];
            Props(ref sp, hdry, phidry, lhr, h, Kgiven);
            // Get ln(-h) and S values from dryness to approx -10000 cm.
            // These needed for vapour flux (rel humidity > 0.99 at -10000 cm).
            // To have complete S(h) coverage, bridge any gap between -10000 and h[1].
            x    = Math.Log(-Math.Max(vhmax, h[0]));
            lhd  = Math.Log(-sPar.hd);
            dlh  = Math.Log(10.0) / nhpd; // nhpd points per decade
            nli1 = (int)Math.Round((lhd - x) / dlh, 0);
            nld  = nli1 + 1;
            nc   = 1 + sp.n / 3; // n-1 has been made divisible by 3
            // fill out the rest of the structure.
            sp.nld   = nld; sp.nc = nc;
            sp.h     = h;
            sp.lnh   = new double[nld + 1];
            sp.Sd    = new double[nld + 1];
            sp.Kco   = new double[3 + 1, nc + 1];
            sp.phico = new double[3 + 1, nc + 1];
            sp.Sco   = new double[3 + 1, nc + 1];
            // Store Sd and lnh in sp.
            sp.lnh[1] = lhd;
            for (j = 2; j <= nld; j++)
            {
                sp.lnh[j] = lhd - dlh * (j - 1);
            }

            if (Kgiven)
            {
                sp.Sd[1] = MVG.Sofh(sPar.hd);
                for (j = 2; j <= nld; j++)
                {
                    x        = sp.lnh[j];
                    sp.Sd[j] = MVG.Sofh(-Math.Exp(x));
                }
            }
            else
            {
                MVG.Sdofh(sPar.hd, out x, out dSdh);
                sp.Sd[1] = x;
                for (j = 2; j <= nld; j++)
                {
                    x = sp.lnh[j];
                    MVG.Sdofh(-Math.Exp(x), out x, out dSdh);
                    sp.Sd[j] = x;
                }
            }

            // Get polynomial coefficients.
            j       = 0;
            sp.Sc   = new double[sp.n + 1];
            sp.hc   = new double[sp.n + 1];
            sp.phic = new double[sp.n + 1];
            Matrix <double> KcoM = Matrix <double> .Build.DenseOfArray(sp.Kco);

            Matrix <double> phicoM = Matrix <double> .Build.DenseOfArray(sp.phico);

            Matrix <double> ScoM = Matrix <double> .Build.DenseOfArray(sp.Sco);

            for (i = 1; i <= sp.n; i += 3)
            {
                j          = j + 1;
                sp.Sc[j]   = S[i];
                sp.hc[j]   = h[i];
                sp.Kc[j]   = sp.K[i];
                sp.phic[j] = sp.phi[i];
                if (i == sp.n)
                {
                    break;
                }

                cco = Cuco(sp.phi.Slice(i, i + 3), sp.K.Slice(i, i + 3));
                KcoM.SetColumn(j, cco.Slice(2, 4).ToArray());

                cco = Cuco(sp.S.Slice(i, i + 3), sp.phi.Slice(i, i + 3));
                phicoM.SetColumn(j, cco.Slice(2, 4).ToArray());

                cco = Cuco(sp.phi.Slice(i, i + 3), sp.S.Slice(i, i + 3));
                ScoM.SetColumn(j, cco.Slice(2, 4).ToArray());
            }
            sp.Kco   = KcoM.ToArray();
            sp.phico = phicoM.ToArray();
            sp.Sco   = ScoM.ToArray();
            return(sp);
        }
Пример #5
0
        private static void Props(ref SoilProps sp, double hdry, double phidry, double[] lhr, double[] h, bool Kgiven)
        {
            int i, j, nli;

            double[] g     = new double[201];
            double[] dSdhg = new double[201];

            j   = 2 * (nliapprox / 6); // an even number
            nli = 3 * j;               //nli divisible by 2 (for integrations) and 3 (for cubic coeffs)
            if (sp.he > hwet)
            {
                nli = 3 * (j + 1) - 1;           // to allow for extra points
            }
            dlhr = -Math.Log(hdry / hwet) / nli; // even spacing in log(-h)

            // lhr(1:nli + 1) = (/ (-i * dlhr,i = nli,0,-1)/)
            double[] slice = lhr.Slice(1, nli + 1);
            for (int idx = nli; idx > 0; idx--)
            {
                slice[idx] = -idx * dlhr;
            }
            Array.Reverse(slice);
            Array.Copy(slice, 0, lhr, 0, slice.Length);
            for (int idx = 1; idx <= nli + 1; idx++)
            {
                h[idx] = hwet * Math.Exp(lhr[idx]);
            }

            if (sp.he > hwet)  // add extra points
            {
                sp.n        = nli + 3;
                h[sp.n - 1] = 0.5 * (sp.he + hwet);
                h[sp.n]     = sp.he;
            }
            else
            {
                sp.n = nli + 1;
            }

            sp.K   = new double[sp.n + 1];
            sp.Kc  = new double[sp.n + 1];
            sp.phi = new double[sp.n + 1];

            if (Kgiven)
            {
                for (i = 1; i <= sp.n; i++)
                {
                    sp.S[i] = MVG.Sofh(h[i]);
                    sp.K[i] = MVG.KofhS(h[i], sp.S[i]);
                }
                sp.S[sp.n] = MVG.Sofh(h[sp.n]);
            }
            else // calculate relative K by integration using dln(-h)
            {
                for (i = 1; i <= sp.n; i++)
                {
                    MVG.Sdofh(h[i], out sp.S[i], out dSdhg[i]);
                }

                g[1] = 0;

                for (i = 2; i <= nli; i += 2)  // integrate using Simpson's rule
                {
                    g[i + 1] = g[i - 1] + dlhr * (dSdhg[i - 1] + 4.0 * dSdhg[i] + dSdhg[i + 1]) / 3.0;
                }

                g[2] = 0.5 * (g[0] + g[2]);

                for (i = 3; i <= nli - 1; i += 2)
                {
                    g[i + 1] = g[i - 1] + dlhr * (dSdhg[i - 1] + 4.0 * dSdhg[i] + dSdhg[i + 1]) / 3.0;
                }

                if (sp.he > hwet)
                {
                    g[sp.n]     = g[sp.n - 2] + (h[sp.n] - h[sp.n - 1]) * (dSdhg[sp.n - 2] / h[sp.n - 2] + 4.0 * dSdhg[sp.n - 1] / h[sp.n - 1]) / 3.0;
                    g[sp.n - 1] = g[sp.n]; // not accurate, but K[sp.n-1] will be discarded
                }

                for (i = 1; i <= sp.n; i++)
                {
                    sp.K[i] = sp.ks * Math.Pow(sp.S[i], MVG.GetP()) * Math.Pow(g[i] / g[sp.n], 2);
                }
            }

            // Calculate phi by integration using dln(-h).
            sp.phi[1] = phidry;

            for (i = 2; i <= nli; i += 2) // integrate using Simpson's rule
            {
                sp.phi[i + 1] = sp.phi[i - 1] + dlhr * (sp.K[i - 1] * h[i - 1] + 4.0 * sp.K[i] * h[i] + sp.K[i + 1] * h[i + 1]) / 3.0;
            }

            sp.phi[2] = 0.5 * (sp.phi[1] + sp.phi[3]);

            for (i = 3; i <= nli - 1; i += 2)
            {
                sp.phi[i + 1] = sp.phi[i - 1] + dlhr * (sp.K[i - 1] * h[i - 1] + 4.0 * sp.K[i] * h[i] + sp.K[i + 1] * h[i + 1]) / 3.0;
            }

            if (sp.he > hwet)  // drop unwanted point
            {
                sp.phi[sp.n - 1] = sp.phi[sp.n - 2] + (h[sp.n] - h[sp.n - 1]) * (sp.K[sp.n - 2] + 4.0 * sp.K[sp.n - 1] + sp.K[sp.n]) / 3.0;
                h[sp.n - 1]      = h[sp.n];
                sp.S[sp.n - 1]   = sp.S[sp.n];
                sp.K[sp.n - 1]   = sp.K[sp.n];
                sp.n             = sp.n - 1;
            }
            phie    = sp.phi[sp.n];
            sp.phie = phie;
        }
Пример #6
0
        static double vhmax = -10000; // for vapour - rel humidity > 0.99 at vhmax

        #endregion Fields

        #region Methods

        // The above parameters can be varied, but the defaults should usually be ok.
        //subroutine to generate the property values.
        public static SoilProps gensptbl(double dzmin, SoilParam sPar, bool Kgiven)
        {
            int nlimax = 220;
            int i, j, nli1, n, nc, nld;
            double[] h= new double[nlimax + 3];
            double[] lhr= new double[nlimax + 3];
            double[] K = new double[nlimax + 3];
            double[] phi = new double[nlimax + 3];
            double[] S = new double[nlimax + 3];
            double[] cco = new double[4];

            //diags - timing starts here

            SoilProps sp = new SoilProps();
            sp.sid = sPar.sid; sp.ths = sPar.ths; sp.ks = sPar.ks;
            sp.he = sPar.he; sp.phie = phie; sp.S = S; sp.n = sPar.layers;
            n = sp.n;

            // Find start of significant fluxes.
            hdry = sPar.hd; // normally -1e7 cm
            phidry = 0.0;
            hwet = Math.Min(sPar.he, -1.0); // h/hwet used for log spacing

            if (Kgiven)
            {
                dlh = Math.Log(10.0) / 3.0; // three points per decade
                x = Math.Exp(-dlh); // x*x*x=0.1
                h[0] = hdry;
                K[0] = MVG.Kofh(h[0]);
                phi[1] = 0.0;
                for (i = 1; i < nlimax; i++) // should exit well before nlimax
                {
                    h[i] = x * h[i - 1];
                    K[i] = MVG.Kofh(h[i]);
                    // Get approx. phi by integration using dln(-h).
                    phi[i] = phi[i - 1] - 0.5 * (K[i] * h[i] - K[i - 1] * h[i - 1]) * dlh;
                    if (phi[i] > qsmall * dzmin)
                        break; // max flux is approx (phi-0)/dzmin
                }
                if (i > nlimax)
                {
                    Console.WriteLine("gensptbl: start of significant fluxes not found");
                    Environment.Exit(1);
                }
                hdry = h[i - 1];
                phidry = phi[i - 1];
            }
            else
            {
                // Calculate K and find start of significant fluxes.
                Props(sp, hdry, phidry, lhr, h, Kgiven);
                for (i = 2; i < n; i++)
                    if (phi[i] > qsmall * dzmin)
                        break;
                if (i > n)
                {
                    Console.WriteLine("gensptbl: start of significant fluxes not found");
                    Environment.Exit(1);
                }
                i = i - 1;
                hdry = h[i];
                phidry = phi[i];
            }

            // hdry and phidry are values where significant fluxes start.
            // Get props.
            sp.Kc = new double[sp.n];
            Props(sp, hdry, phidry, lhr,h,Kgiven);
            // Get ln(-h) and S values from dryness to approx -10000 cm.
            // These needed for vapour flux (rel humidity > 0.99 at -10000 cm).
            // To have complete S(h) coverage, bridge any gap between -10000 and h[1].
            x = Math.Log(-Math.Max(vhmax, h[1]));
            lhd = Math.Log(-sPar.hd);
            dlh = Math.Log(10.0) / nhpd; // nhpd points per decade
            nli1 = (int)Math.Round((lhd - x) / dlh, 0);
            nld = nli1 + 1;
            nc = 1 + n / 3; // n-1 has been made divisible by 3
            // fill out the rest of the structure.
             sp.nld = nld; sp.nc = nc;
             sp.h = h; sp.K = K; sp.phi = phi;
             sp.lnh = new double[nld];
             sp.Sd = new double[nld];
             sp.Kco = new double[3, nc - 1];
             sp.phico = new double[3, nc - 1];
             sp.Sco = new double[3, nc - 1];

            // Store Sd and lnh in sp.
            sp.lnh[0] = lhd;
            for (j = 1; j < nli1; j++)
                sp.lnh[j] = lhd - dlh * j;

            if (Kgiven)
            {
                sp.Sd[0] = MVG.Sofh(sPar.hd);
                for (j = 1; j < nld; j++)
                {
                    x = sp.lnh[j];
                    sp.Sd[j] = MVG.Sofh(-Math.Exp(x));
                }
            }
            else
            {
                MVG.Sdofh(sPar.hd, out x, out dSdh);
                sp.Sd[0] = x;
                for (j = 1; j < nld; j++)
                {
                    x = sp.lnh[j];
                    MVG.Sdofh(-Math.Exp(x), out x, out dSdh);
                    sp.Sd[j] = x;
                }
            }

            // Get polynomial coefficients.
            j = 0;
            sp.Sc = new double[sp.n];
            sp.hc = new double[sp.n];
            sp.phic = new double[sp.n];
            for (i = 0; i < n; i += 3)
            {
                j = j + 1;
                sp.Sc[j] = S[i];
                sp.hc[j] = h[i];
                sp.Kc[j] = K[i];
                sp.phic[j] = phi[i];
                if (i == n)
                    break;

                cco = Cuco(sp.phi.Skip(i).Take(4).ToArray(), sp.K.Skip(i).Take(4).ToArray());
                for (int row = 0; row < sp.Kco.GetLength(0); row++)
                    for (int col = 0; col < 2; col++)
                        sp.Kco[row, col] = cco[col + 2];

                cco = Cuco(sp.S.Skip(i).Take(4).ToArray(), sp.phi.Skip(i).Take(4).ToArray());
                for (int row = 0; row < sp.Kco.GetLength(0); row++)
                    for (int col = 0; col < 2; col++)
                        sp.phico[row, col] = cco[col + 2];

                cco = Cuco(sp.phi.Skip(i).Take(4).ToArray(), sp.S.Skip(i).Take(4).ToArray());
                for (int row = 0; row < sp.Kco.GetLength(0); row++)
                    for (int col = 0; col < 2; col++)
                        sp.Sco[row, col] = cco[col + 2];
            }
            // diags - end timing
            return sp;
        }
Пример #7
0
        private static void Props(SoilProps sp, double hdry, double phidry, double[] lhr, double[] h, bool Kgiven)
        {
            int i, j, nli;
            double[] g = new double[200];
            double[] dSdhg = new double[200];

            j = 2 * (nliapprox / 6); // an even number
            nli = 3 * j; //nli divisible by 2 (for integrations) and 3 (for cubic coeffs)
            if (sp.he > hwet) nli = 3 * (j + 1) - 1; // to allow for extra points
            dlhr = -Math.Log(hdry / hwet) / nli; // even spacing in log(-h)
            for (int idx = nli; idx >= 0; idx--)    //
                lhr[idx] = -idx * dlhr;              // will need to check this, fortran syntax is unknown: lhr(1:nli+1)=(/(-i*dlhr,i=nli,0,-1)/)
            lhr = lhr.Reverse().ToArray();       //
            for (int idx = 0; idx < nli + 1; idx++)
                h[idx] = hwet * Math.Exp(lhr[idx]);

            if (sp.he > hwet)  // add extra points
            {
                sp.n = nli + 3;
                h[nli - 1] = 0.5 * (sp.he + hwet);
                h[sp.n] = sp.he;
            }
            else
                sp.n = nli + 1;

            sp.K = new double[sp.n];
            sp.phi = new double[sp.n];

            if (Kgiven)
                for (i = 0; i < sp.n; i++)
                {

                    sp.S[i] = MVG.Sofh(h[i]);
                    sp.K[i] = MVG.KofhS(h[i], sp.S[i]);
                }
            else // calculate relative K by integration using dln(-h)
            {

                for (i = 0; i < sp.n; i++)
                    MVG.Sdofh(h[i], out sp.S[i], out dSdhg[i]);

                g[0] = 0;

                for (i = 1; i < nli; i += 2)  // integrate using Simpson's rule
                    g[i + 1] = g[i - 1] + dlhr * (dSdhg[i - 1] + 4.0 * dSdhg[i] + dSdhg[i + 1]) / 3.0;

                g[1] = 0.5 * (g[0] + g[2]);

                for (i = 2; i < nli - 1; i += 2)
                    g[i + 1] = g[i - 1] + dlhr * (dSdhg[i - 1] + 4.0 * dSdhg[i] + dSdhg[i + 1]) / 3.0;

                if (sp.he > hwet)
                {
                    g[sp.n] = g[sp.n - 2] + (h[sp.n] - h[sp.n - 1]) * (dSdhg[sp.n - 2] / h[sp.n - 2] + 4.0 * dSdhg[sp.n - 1] / h[sp.n - 1]) / 3.0;
                    g[sp.n - 1] = g[sp.n]; // not accurate, but K[sp.n-1] will be discarded
                }

                for (i = 0; i < sp.n; i++)
                    sp.K[i] = sp.ks * Math.Pow(sp.S[i], MVG.GetP()) * Math.Pow(g[i] / g[sp.n], 2);
            }

            // Calculate phi by integration using dln(-h).
            sp.phi[1] = phidry;

            for (i = 1; i < nli; i += 2) // integrate using Simpson's rule
                sp.phi[i + 1] = sp.phi[i - 1] + dlhr * (sp.K[i - 1] * h[i - 1] + 4.0 * sp.K[i] * h[i] + sp.K[i + 1] * h[i + 1]) / 3.0;

            sp.phi[2] = 0.5 * (sp.phi[1] + sp.phi[3]);

            for (i = 2; i < nli - 1; i += 2)
                sp.phi[i + 1] = sp.phi[i - 1] + dlhr * (sp.K[i - 1] * h[i - 1] + 4.0 * sp.K[i] * h[i] + sp.K[i + 1] * h[i + 1]) / 3.0;

            if (sp.he > hwet)  // drop unwanted point
            {
                sp.phi[sp.n - 1] = sp.phi[sp.n - 2] + (h[sp.n] - h[sp.n - 1]) * (sp.K[sp.n - 2] + 4.0 * sp.K[sp.n - 1] + sp.K[sp.n]) / 3.0;
                h[sp.n - 1] = h[sp.n];
                sp.S[sp.n - 1] = sp.S[sp.n];
                sp.K[sp.n - 1] = sp.K[sp.n];
                sp.n = sp.n - 1;
            }
            phie = sp.phi[sp.n-1];
        }
Пример #8
0
        public static FluxTable TwoTables(FluxTable ft1, SoilProps sp1, FluxTable ft2, SoilProps sp2)
        {
            /*Generates a composite flux table from two uniform ones.
              Sets up quadratic interpolation table to get phi at interface for lower path
              from phi at interface for upper path.
              Sets up cubic interpolation tables to get fluxes from phi at interface for
              upper and lower paths.
              Solves for phi at interface in upper path that gives same fluxes in upper
              and lower paths, for all phi at upper and lower ends of composite path.
              Increases no. of fluxes in table by quadratic interpolation.
              ft1, ft2 - flux tables for upper and lower paths.
              sp1, sp2 - soil prop tables for upper and lower paths. -PR

              Note that arrays are one indexed; 0 index is not used.
              This is done as a number of calculations use the index as an input.
              Exception to this is flux/soil arrays where array index is not used in calculations. -JF
            */

            // Set up required pointers and data
            if (ft1.fend[0].sid != ft1.fend[1].sid || ft2.fend[0].sid != ft2.fend[1].sid)
            {
                Console.WriteLine("Flux table not for uniform soil.");
                Environment.Exit(1);
            }
            ft1.ftable = Matrix<double>.Build.DenseOfArray(ft1.ftable).Transpose().ToArray();
            ft2.ftable = Matrix<double>.Build.DenseOfArray(ft2.ftable).Transpose().ToArray();
            ft[0] = ft1;
            ft[1] = ft2;
            sp[0] = sp1;
            sp[1] = sp2;
            for (i = 1; i <= 2; i++)
            {
                n[i] = sp[i - 1].n;
                he[i] = sp[i - 1].he;
                phie[i] = sp[i - 1].phie;
                Ks[i] = sp[i - 1].ks;
                for (int x = 1; x <= n[i]; x++)
                {
                    h[x, i] = sp[i - 1].h[x];
                    phi[x, i] = sp[i - 1].phi[x];
                }
            }

            // Discard unwanted input - use original uninterpolated values only.
            for (i = 1; i <= 2; i++)
            {
                m = ft[i - 1].fend[0].nft; //should be odd
                j = 1 + m / 2;
                double[] tempPhif = new double[j + 1];
                Array.Copy(ft[i - 1].fend[0].phif.Where((x, it) => it % 2 == 1).ToArray(), 0, tempPhif, 1, j);
                for (int x = 1; x <= j; x++)
                    phif[x, i] = tempPhif[x]; //discard every second
                nft[i] = j;
                nfu[i] = 1 + ft[i - 1].fend[1].nfu / 2; //ft[i].fend[1].nfu should be odd

                double[,] tempFt = new double[m + 1, m + 1];
                for (int a = 1; a <= m; a += 2)
                    for (int b = 1; b <= m; b += 2)
                        tempFt[a / 2 + 1, b / 2 + 1] = ft[i - 1].ftable[a, b];

                for (int a = 1; a <= m; a++)
                    for (int b = 1; b <= m; b++)
                        qf[b, a, i] = tempFt[a, b];
            }

            // Extend phi2 and h2 if he1>he2, or vice-versa.
            dhe = Math.Abs(he[1] - he[2]);
            if (dhe > 0.001)
            {
                if (he[1] > he[2])
                {
                    i = 1;
                    j = 2;
                }
                else
                {
                    i = 2;
                    j = 1;
                }
                double[] hFind = new double[n[i] + 1];
                for (int x = 1; x <= n[i] + 1; x++)
                    hFind[x - n[i] + 1] = h[i, x];
                ii = Find(he[j], hFind);
                for (k = 1; k <= n[i] - ii; k++)
                {
                    h[j, n[j] + k] = h[i, ii + k];//test these
                    phi[j, n[j] + k] = phie[j] + Ks[j] * (h[i, ii + k] - he[j]);
                }
                n[j] = n[j] + n[i] - ii;
            }
            phi1max = phi[n[1], 1];

            // Get phi for same h.
            if (h[1, 1] > h[1, 2])
            {
                i = 1;
                j = 2;
            }
            else
            {
                i = 2;
                j = 1;
            }

            Matrix<double> hm = Matrix<double>.Build.DenseOfArray(h); //test
            double[] absh = hm.Column(j).ToArray();
            absh = absh.Slice(1, n[j]);
            absh = MathUtilities.Subtract_Value(absh, h[1, i]);
            for (int x = 0; x < absh.Length; x++)
                absh[x] = Math.Abs(absh[x]);
            id = MinLoc(absh);
            if (h[id, j] >= h[1, 1])
                id--;

            //phii(j,:) for soil j will match h(i,:) from soil i and h(j, 1:id) from soil j.
            //phii(i,:) for soil i will match h(i,:) and fill in for h(j, 1:id).
            for (int iid = 1; iid <= id; iid++)
                phii[iid, j] = phi[iid, j]; // keep these values

            // But interpolate to match values that start at greater h.
            jj = id + 1; //h(j,id+1) to be checked first
            phii[id + n[i], j] = phi[n[j], j]; // last h values match
            for (ii = 1; ii <= n[i] - 1; ii++)
            {
                while (true) //get place of h(i,ii) in h array for soil j
                {
                    if (jj > n[j])
                    {
                        Console.WriteLine("twotbls: h[j,n[j]] <= h[i,ii]; i, j, ii, n[j] = " + i + " " + j + " " + ii + " " + n[j]);
                        Environment.Exit(1);
                    }

                    if (h[jj, j] > h[ii, i])
                        break;
                    jj += 1;
                }

                k = jj - 1; //first point for cubic interp
                if (jj + 2 > n[j])
                    k = n[j] - 3;

                double[] hCuco = new double[5];
                double[] phiCuco = new double[5];
                for (int x = k; x <= k + 3; x++)
                {
                    hCuco[x - k + 1] = h[x, j];
                    phiCuco[x - k + 1] = phi[x, j];
                }

                co = Soil.Cuco(hCuco, phiCuco); // get cubic coeffs
                v = h[ii, i] - h[k, j];
                phii[id + ii, j] = co[1] + v * (co[2] + v * (co[3] + v * co[4]));
            }
            ni = id + n[i];

            // Generate sensible missing values using quadratic extrapolation.
            co = Fluxes.Quadco(new double[4] { 0, phii[1, j], phii[id + 1, j], phii[id + 2, j] }, new double[4] { 0, 0, phi[1, i], phi[2, i] });
            if (co[2] > 0) // +ve slope at zero - ok
            {
                for (int x = 1; x <= id; x++)
                {
                    xval[x] = phii[x, j] - phii[1, j];
                    phii[x, i] = co[i] + xval[x] * (co[2] + xval[x] * co[3]);
                }
            }
            else // -ve slope at zero, use quadratic with zero slope at zero
            {
                co[3] = phi[1, i] / Math.Pow(phii[id + 1, j], 2);
                for (int x = 1; x <= id; x++)
                    phii[x, i] = co[3] * Math.Pow(phii[x, j], 2);
            }

            // phii(i,id+1:ni)=phi(i,1:n(i))
            double[] phin = new double[ni - id + 1];
            for (int x = 1; x <= n[i]; x++)
                phin[x] = phi[x, i];
            for (int x = id + 1; x <= ni; x++)
                phii[x, i] = phin[x - (id + 1) + 1];

            //hi(1:id) = h(j, 1:id)
            for (int x = 1; x <= id; x++)
                hi[x] = h[x, j];

            // hi(id+1:ni)=h(i,1:n(i))
            for (int x = 1; x <= n[i]; x++)
                hi[id + x] = h[x, i];

            /* hi(1:ni) are h values for the interface tables.
             * phii(1,1:ni) are corresponding interface phi values for upper layer.
             * phii(2,1:ni) are corresponding interface phi values for lower layer.
             * Set up quadratic interpolation coeffs to get phii2 given phii1.
             */
            Matrix<double> coqM = Matrix<double>.Build.DenseOfArray(coq);
            Vector<double>[] quadcoV = new Vector<double>[ni - 2 + 1];
            double[] tmpx;
            double[] tmpy;
            for (i = 1; i <= ni - 2; i++)
            {
                tmpx = new [] { 0, phii[i, 1], phii[i + 1, 1], phii[i + 2, 1] };
                tmpy = new [] { 0, phii[i, 2], phii[i + 1, 2], phii[i + 2, 2] };
                quadcoV[i] = Vector<double>.Build.DenseOfArray(Fluxes.Quadco(tmpx, tmpy));
                coqM.SetRow(i, quadcoV[i]);
            }
            Vector<double> lincoV = Vector<double>.Build.DenseOfArray(Linco(new [] { 0, phii[ni - 1, 1], phii[ni, 1] }, new [] { 0, phii[ni - 1, 2], phii[ni, 2] }));
            coqM.SetRow(ni - 1, lincoV);
            coq = coqM.ToArray();
            coq[ni - 1, 3] = 0;

            double[,] getco = new double[20, 20];

            // Set up cubic coeffs to get fluxes q given phi.
            for (j = 1; j <= nft[2]; j++)
            {
                k = 1;
                ip = 1;
                while (true)
                {
                    phico2[k] = phif[ip, 2];
                    double[] co2co = Soil.Cuco(new double[5] { 0, phif[ip, 2], phif[ip + 1, 2], phif[ip + 2, 2], phif[ip + 3, 2] },
                                               new double[5] { 0, qf[j, ip, 2], qf[j, ip + 1, 2], qf[j, ip + 2, 2], qf[j, ip + 3, 2] });
                    for (int x = 1; x < co2co.Length; x++)
                        co2[j, k, x] = co2co[x];
                    ip += 3;
                    if (ip == nft[2])
                        break;
                    if (ip > nft[2])
                        ip = nft[2] - 3;
                    k++;
                }
                for (int x = 1; x < 20; x++)
                    for (int y = 1; y < 20; y++)
                        getco[y, x] = co2[y, x, 1];
            }

            nco2 = k;

            // Get fluxes
            for (i = 1; i <= nft[1]; i++) //step through top phis
            {
                vlast = phif[i, 1];
                k = 1;
                ip = 1;
                Matrix<double> co1M = Matrix<double>.Build.DenseOfArray(co1);
                while (true)
                {
                    phico1[k] = phif[ip, 1];
                    co1M.SetRow(k, Soil.Cuco(new double[5] { 0, phif[ip, 1], phif[ip + 1, 1], phif[ip + 2, 1], phif[ip + 3, 1] },
                                                new double[5] { 0, qf[ip, i, 1], qf[ip + 1, i, 1], qf[ip + 2, i, 1], qf[ip + 3, i, 1] }));
                    ip += 3;
                    if (ip == nft[1])
                        break;
                    if (ip > nft[1])
                        ip = nft[1] - 3;
                    k++;
                }
                co1 = co1M.ToArray();
                nco1 = k;
                for (j = 1; j <= nft[2]; j++) // bottom phis
                {
                    v = vlast;
                    for (k = 1; k <= maxit; k++) // solve for upper interface phi giving same fluxes
                    {
                        fd(v, out f, out df, out q1);
                        dx = f / df; // Newton's method - almost always works
                        v = Math.Min(10.0 * phif[nft[1], 1], Math.Max(phii[1, 1], v - dx));
                        e = Math.Abs(f / q1);
                        if (e < rerr)
                            break;
                        vlast = v;
                    }
                    if (k > maxit) //failed - bracket q and use bisection
                    {
                        v1 = phii[1, 1];
                        fd(v1, out f1, out df, out q1);
                        if (f1 <= 0.0) // answer is off table - use end value
                        {
                            qp[j, i] = q1;
                            continue;
                        }
                        v2 = phii[ni, 1];
                        fd(v2, out f2, out df, out q1);
                        for (k = 1; k <= maxit; k++)
                        {
                            if (f1 * f2 < 0.0)
                                break;
                            v1 = v2;
                            f1 = f2;
                            v2 = 2.0 * v1;
                            fd(v2, out f2, out df, out q1);
                        }
                        if (k > maxit)
                        {
                            Console.WriteLine(v1 + " " + v2 + " " + f1 + " " + f2);
                            v1 = phii[1, 1];
                            fd(v1, out f1, out df, out q1);
                            Console.WriteLine(v1 + " " + f1);
                            Console.WriteLine("twotbls: too many iterations at i, j = " + i + " " + j);
                            Environment.Exit(1);
                        }
                        for (k = 1; k <= maxit; k++)
                        {
                            v = 0.5 * (v1 + v2);
                            fd(v, out f, out df, out q1);
                            e = Math.Abs(f / q1);
                            if (e < rerr)
                                break;
                            if (f > 0.0)
                            {
                                v1 = v;
                                f1 = f;
                            }
                            else
                            {
                                v2 = v;
                                f2 = f;
                            }
                        }
                        vlast = v;
                        if (k > maxit)
                        {
                            Console.WriteLine("twotbls: too many iterations at i, j = " + i + " " + j);
                            Environment.Exit(1);
                        }
                    }
                    // Solved
                    qp[j, i] = q1;
                } //end j
            } //end i

            //interpolate extra fluxes
            for (i = 1; i <= 2; i++)
            {
                nfi[i] = nft[i] - 1;
                for (int x = 1; x <= nfi[i]; x++)
                    phifi[x, i] = 0.5 * (phif[x, i] + phif[x + 1, i]);
            }

            Matrix<double> phifM = Matrix<double>.Build.DenseOfArray(phif);
            Matrix<double> phifiM = Matrix<double>.Build.DenseOfArray(phifi);
            Matrix<double> qpM = Matrix<double>.Build.DenseOfArray(qp);
            Matrix<double> qi1M = Matrix<double>.Build.DenseOfArray(qi1);
            Matrix<double> qi2M = Matrix<double>.Build.DenseOfArray(qi2);
            Matrix<double> qi3M = Matrix<double>.Build.DenseOfArray(qi3);

            for (i = 1; i <= nft[1]; i++)
            {
                qi1M.SetColumn(i, Fluxes.Quadinterp(phifM.Column(2).ToArray(), qpM.Column(i).ToArray(), nft[2], phifiM.Column(2).ToArray()));
            }
            for (j = 1; j <= nft[2]; j++)
            {
                qi2M.SetRow(j, Fluxes.Quadinterp(phifM.Column(1).ToArray(), qpM.Row(j).ToArray(), nft[1], phifiM.Column(1).ToArray()));
            }
            for (j = 1; j <= nfi[2]; j++)
            {
                qi3M.SetRow(j, Fluxes.Quadinterp(phifM.Column(1).ToArray(), qi1M.Row(j).ToArray(), nft[1], phifiM.Column(1).ToArray()));
            }

            qi1 = qi1M.ToArray();
            qi2 = qi2M.ToArray();
            qi3 = qi3M.ToArray();

            // Put all the fluxes together
            i = nft[1] + nfi[1];
            j = nft[2] + nfi[2];

            // qi5(1:i:2,1:j:2)=qp(1:nft[1],1:nft[2])
            for (int a = 1; a <= mx; a += 2)
                for (int b = 1; b <= mx; b += 2)
                    qi5[b, a] = qp[b / 2 + 1, a / 2 + 1];

            // qi5(1:a: 2, 2:b: 2) = qi1(1:nft[1], 1:nfi[2])
            for (int a = 1; a <= mx; a += 2)
                for (int b = 2; b <= mx; b += 2)
                    qi5[b, a] = qi1[(b-1) / 2 + 1, a / 2 + 1];

            // qi5(2:a:2,1:b:2)=qi2(1:nfi[1],1:nft[2])
            for (int a = 2; a <= mx; a += 2)
                for (int b = 1; b <= mx; b += 2)
                    qi5[b, a] = qi2[b / 2 + 1, (a - 1) / 2 + 1];

            // qi5(2:a:2,2:b:2)=qi3(1:nfi[1],1:nfi[2])
            for (int a = 2; a <= mx; a += 2)
                for (int b = 2; b <= mx; b += 2)
                    qi5[b, a] = qi3[(b - 1) / 2 + 1, (a - 1) / 2 + 1];

            // phii5(1, 1:a: 2) = phif(1, 1:nft[1])
            for (int a = 1; a <= mx; a += 2)
                phii5[a, 1] = phif[a / 2 + 1, 1];

            // phii5(1,2:a:2)=phifi(1,1:nfi[1])
            for (int a = 2; a < 101; a += 2)
                phii5[a, 1] = phifi[(a - 1) / 2 + 1, 1];

            // phii5(2,1:b:2)=phif(2,1:nft[2])
            for (int a = 1; a <= mx; a += 2)
                phii5[a, 2] = phif[a / 2 + 1, 2];

            // phii5(2,2:b:2)=phifi(2,1:nfi[2])
            for (int a = 2; a <= mx; a += 2)
                phii5[a, 2] = phifi[(a-1) / 2 + 1, 2];

            // Assemble flux table
            ftwo.fend = new FluxEnd[2];
            for (ie = 0; ie < 2; ie++)
            {
                ftwo.fend[ie].sid = sp[ie].sid;
                ftwo.fend[ie].nfu = ft[ie].fend[1].nfu;
                ftwo.fend[ie].nft = ft[ie].fend[1].nft;
                ftwo.fend[ie].dz = ft[ie].fend[1].dz;
                ftwo.fend[ie].phif = ft[ie].fend[1].phif;
            }

            double[,] qi5Slice = new double[i + 1, j + 1];
            for (int x = 1; x <= i; x++)
                for (int y = 1; y <= j; y++)
                    qi5Slice[x, y] = qi5[x, y];
            ftwo.ftable = qi5Slice;

            return ftwo;
        }
Пример #9
0
 public static FluxTable TwoTables(FluxTable ft1, SoilProps sp1, FluxTable ft2, SoilProps sp2)
 {
     return new FluxTable();
 }
Пример #10
0
        public static void FluxTable(double dz, SoilProps sp)
        {
            // Generates a flux table for use by other programs.
            // Assumes soil props available in sp of module soil.
            // dz - path length.


            //diags - timer start here

            nu   = sp.nc;
            ah   = sp.hc; aK = sp.Kc; aphi = sp.phic; aS = sp.Sc;
            aKco = sp.Kco; aphico = sp.phico;
            he   = sp.he; Ks = sp.ks;

            // Get K values for Simpson's integration rule in subroutine odef.
            for (i = 0; i < nu - 2; i++)
            {
                x      = 0.5 * (aphi[i + 1] - aphi[i]);
                hpK[i] = aK[i] + x * (aKco[0, i] + x * (aKco[1, i] + x * aKco[2, i]));
            }

            // Get fluxes aq(1,:) for values aphi[i] at bottom (wet), aphi(1) at top (dry).
            // These are used to select suitable phi values for flux table.
            nit      = 0;
            aq[1, 1] = aK[1];                            // q=K here because dphi/dz=0
            dh       = 2.0;                              // for getting phi in saturated region
            q1       = (aphi[1] - aphi[2]) / dz;         // q1 is initial estimate
            aq[1, 2] = ssflux(1, 2, dz, q1, 0.1 * rerr); // get accurate flux
            for (j = 2; j < nu + 20; j++)                // 20*dh should be far enough for small curvature in (phi,q)
            {
                if (j > nu)                              // part satn - set h, K and phi
                {
                    ah[j]   = ah[j - 1] + dh * (j - nu);
                    aK[j]   = Ks;
                    aphi[j] = aphi[j - 1] + Ks * dh * (j - nu);
                }
            }

            // get approx q from linear extrapolation
            q1       = aq[1, j - 1] + (aphi[j] - aphi[j - 1]) * (aq[1, j - 1] - aq[1, j - 2]) / (aphi[j - 1] - aphi[j - 2]);
            aq[1, j] = ssflux(1, j, dz, q1, 0.1 * rerr); // get accurate q
            nt       = j;
            ns       = nt - nu;
            if (j > nu)
            {
                if (-(aphi[j] - aphi[j - 1]) / (aq[1, j] - aq[1, j - 1]) < (1 + rerr) * dz)
                {
                    Environment.Exit(0);
                }
            }

            // Get phi values phif for flux table using curvature of q vs phi.
            // rerr and cfac determine spacings of phif.
            Vector <double> aphiV = Vector <double> .Build.DenseOfArray(aphi);

            Matrix <double> aqM = Matrix <double> .Build.DenseOfArray(aq);

            i  = nonlin(nu, aphiV.SubVector(0, nu).ToArray(), aqM.Column(0).SubVector(0, nu).ToArray(), rerr);
            re = curv(nu, aphiV.SubVector(0, nu).ToArray(), aqM.Column(0).SubVector(0, nu).ToArray());// for unsat phi
            indices(nu - 2, re.Take(nu - 3).Reverse().ToArray(), 1 + nu - i, cfac, out nphif, ref iphif);
            int[] iphifReverse = iphif.Take(nphif).Reverse().ToArray();
            for (int idx = 0; idx < nphif; idx++)
            {
                iphif[idx] = 1 + nu - iphifReverse[idx];                                                               // locations of phif in aphi
            }
            aphiV = Vector <double> .Build.DenseOfArray(aphi);                                                         //may not need to do this; haevn't check in aphi has changed since last use.

            aqM = Matrix <double> .Build.DenseOfArray(aq);                                                             //as above

            re = curv(1 + ns, aphiV.SubVector(nu, nt - nu).ToArray(), aqM.Column(1).SubVector(nu, nt - nu).ToArray()); // for sat phi
            indices(ns - 1, re, ns, cfac, out nfs, ref ifs);

            for (int idx = nphif; i < nphif + nfs - 2; idx++)
            {
                iphif[idx] = nu - 1 + ifs[idx];
            }
            nfu   = nphif; // no. of unsat phif
            nphif = nphif + nfs - 1;
            for (int idx = 0; i < nphif; idx++)
            {
                phif[idx]  = aphi[iphif[idx]];
                qf[0, idx] = aq[0, iphif[idx]];
            }
            // Get rest of fluxes
            // First for lower end wetter
            for (j = 1; j < nphif; j++)
            {
                for (i = 1; i < j; i++)
                {
                    q1 = qf[i - 1, j];
                    if (ah[iphif[j]] - dz < ah[iphif[i]])
                    {
                        q1 = 0.0; // improve?
                    }
                    qf[i, j] = ssflux(iphif[i], iphif[j], dz, q1, 0.1 * rerr);
                }
            }
            // Then for upper end wetter
            for (i = 1; i < nphif; i++)
            {
                for (j = i - 1; j > 1; j--)
                {
                    q1 = qf[i, j + 1];
                    if (j + 1 == i)
                    {
                        q1 = q1 + (aphi[iphif[i]] - aphi[iphif[j]]) / dz;
                    }
                    qf[i, j] = ssflux(iphif[i], iphif[j], dz, q1, 0.1 * rerr);
                }
            }
            // Use of flux table involves only linear interpolation, so gain accuracy
            // by providing fluxes in between using quadratic interpolation.
            ni = nphif - 1;
            for (int idx = 0; idx < ni; idx++)
            {
                phii[idx] = 0.5 * (phif[idx] + phif[idx + 1]);
            }

            Matrix <double> qi1M = Matrix <double> .Build.DenseOfArray(qi1);

            Matrix <double> qfM = Matrix <double> .Build.DenseOfArray(qf);

            double[] qi1Return;
            double[] qi2Return;
            double[] qi3Return;

            for (i = 0; i < nphif; i++)
            {
                qi1Return = quadinterp(phif, qfM.Row(i).ToArray(), nphif, phii);
                for (int idx = 0; idx < qi1Return.Length; idx++)
                {
                    qi1[i, idx] = qi1Return[idx];
                }
            }

            for (j = 0; j < nphif; j++)
            {
                qi2Return = quadinterp(phif, qfM.Column(j).ToArray(), nphif, phii);
                for (int idx = 0; idx < qi2Return.Length; idx++)
                {
                    qi2[idx, i] = qi2Return[idx];
                }
            }

            for (j = 0; j < ni; j++)
            {
                qi1M = Matrix <double> .Build.DenseOfArray(qi1);

                qi3Return = quadinterp(phif, qi1M.Column(j).ToArray(), nphif, phii);
                for (int idx = 0; idx < qi3Return.Length; idx++)
                {
                    qi3[idx, i] = qi3Return[idx];
                }
            }

            // Put all the fluxes together.
            i = nphif + ni;
            for (int iidx = 0; iidx < i; i += 2)
            {
                for (int npidx = 0; npidx < nphif; npidx++)
                {
                    for (int niidx = 0; niidx < ni; niidx++)
                    {
                        qi5[iidx, iidx]         = qf[npidx, npidx];
                        qi5[iidx, iidx + 1]     = qi1[npidx, niidx];
                        qi5[iidx + 1, iidx]     = qi2[niidx, npidx];
                        qi5[iidx + 1, iidx + 1] = qi3[niidx, niidx];
                    }
                }
            }

            // Get accurate qi5(j,j)=Kofphi(phii(ip))
            ip = 0;
            for (j = 1; j < i; j += 2)
            {
                ip = ip + 1;
                ii = iphif[ip + 1] - 1;
                for (int idx = 0; idx < aphi.Length; idx++) // Search down to locate phii position for cubic.
                {
                    if (aphi[ii] <= phii[ip])
                    {
                        break;
                    }
                    ii = ii - 1;
                }
                x         = phii[ip] - aphi[ii];
                qi5[j, j] = aK[ii] + x * (aKco[1, ii] + x * (aKco[2, ii] + x * aKco[3, ii]));
            }

            for (int idx = 0; idx < i; i++)
            {
                phii5[idx * 2]     = phif[idx];
                phii5[idx * 2 + 1] = phii[idx];
            }

            // diags - end timer here


            // Assemble flux table
            j = 2 * nfu - 1;
            for (ie = 0; ie < 2; ie++)
            {
                pe      = ft.fend[ie];
                pe.phif = new double[phif.Length];
                pe.sid  = sp.sid;
                pe.nfu  = j;
                pe.nft  = i;
                pe.dz   = dz;
                pe.phif = phii5; //(1:i) assume it's the whole array
            }
            ft.ftable = qi5;     // (1:i,1:i) as above
        }
Пример #11
0
        public static void FluxTable(double dz, SoilProps props)
        {
            // Generates a flux table for use by other programs.
            // Assumes soil props available in sp of module soil.
            // dz - path length.

            sp = props;
            ft.fend = new FluxEnd[2];
            nu = sp.nc;
            he = sp.he; Ks = sp.ks;
            for (i = 1; i <= nu - 1; i++)
                for (j = 1; j < sp.Kco.GetLength(0); j++)
                    aKco[i, j] = sp.Kco[j, i];

            // Get K values for Simpson's integration rule in subroutine odef.
            for (i = 1; i <= nu - 1; i++)
            {
                x = 0.5 * (sp.phic[i + 1] - sp.phic[i]);
                hpK[i] = sp.Kc[i] + x * (aKco[i, 1] + x * (aKco[i, 2] + x * aKco[i, 3]));
            }

            // Get fluxes aq(1,:) for values aphi[i] at bottom (wet), aphi(1) at top (dry).
            // These are used to select suitable phi values for flux table.
            // Note that due to the complexity of array indexing in the FORTRAN,
            // we're keeping aq 1 indexed.
            nit = 0;
            aq[1, 1] = sp.Kc[1]; // q=K here because dphi/dz=0
            dh = 2.0; // for getting phi in saturated region
            q1 = (sp.phic[1] - sp.phic[2]) / dz; // q1 is initial estimate
            aq[2, 1] = ssflux(1, 2, dz, q1, 0.1 * rerr); // get accurate flux
            for (j = 3; j <= nu + 20; j++) // 20*dh should be far enough for small curvature in (phi,q)
            {
                if (j > nu) // part satn - set h, K and phi
                {
                    sp.hc[j] = sp.hc[j - 1] + dh * (j - nu);
                    sp.Kc[j] = Ks;
                    sp.phic[j] = sp.phic[j - 1] + Ks * dh * (j - nu);
                }

                // get approx q from linear extrapolation
                q1 = aq[j - 1, 1] + (sp.phic[j] - sp.phic[j - 1]) * (aq[j - 1, 1] - aq[j - 2, 1]) / (sp.phic[j - 1] - sp.phic[j - 2]);
                aq[j, 1] = ssflux(1, j, dz, q1, 0.1 * rerr); // get accurate q
                nt = j;
                ns = nt - nu;
                if (j > nu)
                    if (-(sp.phic[j] - sp.phic[j - 1]) / (aq[j, 1] - aq[j - 1, 1]) < (1 + rerr) * dz)
                        break;
            }

            // Get phi values phif for flux table using curvature of q vs phi.
            // rerr and cfac determine spacings of phif.
            Matrix<double> aqM = Matrix<double>.Build.DenseOfArray(aq);
            i = nonlin(nu, sp.phic.Slice(1, nu), aqM.Column(1).ToArray().Slice(1, nu), rerr);
            re = curv(nu, sp.phic.Slice(1, nu), aqM.Column(1).ToArray().Slice(1, nu));// for unsat phi
            double[] rei = new double[nu - 2 + 1];
            Array.Copy(re.Slice(1, nu - 2).Reverse().ToArray(), 0, rei, 1, re.Slice(1, nu - 2).Reverse().ToArray().Length - 1); //need to 1-index slice JF
            Indices(nu - 2, rei, 1 + nu - i, cfac, out nphif, out iphif);
            int[] iphifReverse = iphif.Skip(1).Take(nphif).Reverse().ToArray();
            int[] iphifReversei = new int[iphifReverse.Length + 1];
            Array.Copy(iphifReverse, 0, iphifReversei, 1, iphifReverse.Length); // again, need to 1-index JF
            for (int idx = 1; idx < nphif; idx++)
                iphif[idx] = 1 + nu - iphifReversei[idx]; // locations of phif in aphi
            aqM = Matrix<double>.Build.DenseOfArray(aq); //as above
            re = curv(1 + ns, sp.phic.Slice(nu, nt), aqM.Column(1).ToArray().Slice(nu, nt)); // for sat phi
            Indices(ns - 1, re, ns, cfac, out nfs, out ifs);

            int[] ifsTemp = ifs.Slice(2, nfs);
            for (int idx = nphif + 1; idx <= nphif + nfs - 1; idx++)
                iphif[idx] = nu - 1 + ifsTemp[idx - nphif];
            nfu = nphif; // no. of unsat phif
            nphif = nphif + nfs - 1;
            for (int idx = 1; idx <= nphif; idx++)
            {
                phif[idx] = sp.phic[iphif[idx]];
                qf[idx, 1] = aq[iphif[idx], 1];
            }

            // Get rest of fluxes
            // First for lower end wetter
            for (j = 2; j <= nphif; j++)
                for (i = 2; i <= j; i++)
                {
                    q1 = qf[j, i - 1];
                    if (sp.hc[iphif[j]] - dz < sp.hc[iphif[i]])
                        q1 = 0.0; // improve?
                    qf[j, i] = ssflux(iphif[i], iphif[j], dz, q1, 0.1 * rerr);
                }
            // Then for upper end wetter
            for (i = 2; i <= nphif; i++)
                for (j = i - 1; j >= 1; j--)
                {
                    q1 = qf[j + 1, i];
                    if (j + 1 == i)
                        q1 = q1 + (sp.phic[iphif[i]] - sp.phic[iphif[j]]) / dz;
                    qf[j, i] = ssflux(iphif[i], iphif[j], dz, q1, 0.1 * rerr);
                }
            // Use of flux table involves only linear interpolation, so gain accuracy
            // by providing fluxes in between using quadratic interpolation.
            ni = nphif - 1;
            for (int idx = 1; idx <= ni; idx++)
                phii[idx] = 0.5 * (phif[idx] + phif[idx + 1]);

            Matrix<double> qi1M = Matrix<double>.Build.DenseOfArray(qi1);
            Matrix<double> qfM = Matrix<double>.Build.DenseOfArray(qf);
            double[] qi1Return;
            double[] qi2Return;
            double[] qi3Return;

            for (i = 1; i <= nphif; i++)
            {
                qi1Return = Quadinterp(phif, qfM.Column(i).ToArray(), nphif, phii);
                for (int idx = 1; idx < qi1Return.Length; idx++)
                    qi1[idx, i] = qi1Return[idx];
            }

            for (j = 1; j <= nphif; j++)
            {
                qi2Return = Quadinterp(phif, qfM.Row(j).ToArray(), nphif, phii);
                for (int idx = 1; idx < qi2Return.Length; idx++)
                    qi2[j, idx] = qi2Return[idx];
            }

            for (j = 1; j <= ni; j++)
            {
                qi1M = Matrix<double>.Build.DenseOfArray(qi1);
                qi3Return = Quadinterp(phif, qi1M.Row(j).ToArray(), nphif, phii);
                for (int idx = 1; idx < qi3Return.Length; idx++)
                    qi3[j, idx] = qi3Return[idx];
            }

            // Put all the fluxes together.
            i = nphif + ni;
            for (int row = 1; row <= i; row += 2)
                for (int col = 1; col <= i; col += 2)
                {
                    qi5[col, row] = qf[col / 2 + 1, row / 2 + 1];
                    qi5[col+1, row] = qi1[col / 2 + 1, row / 2 + 1];
                    qi5[col, row + 1] = qi2[col / 2 + 1, row / 2 + 1];
                    qi5[col + 1, row + 1] = qi3[col / 2 + 1, row / 2 + 1];
                }

            // Get accurate qi5(j,j)=Kofphi(phii(ip))
            ip = 0;
            for (j = 2; j <= i; j += 2)
            {
                ip = ip + 1;
                ii = iphif[ip + 1] - 1;
               // if (ii >= sp.Kco.GetLength(1))
               //     ii = sp.Kco.GetLength(1) - 1;
                while (true) // Search down to locate phii position for cubic.
                {
                    if (sp.phic[ii] <= phii[ip])
                        break;
                    ii = ii - 1;
                }
                x = phii[ip] - sp.phic[ii];
                qi5[j, j] = sp.Kc[ii] + x * (aKco[ii, 1] + x * (aKco[ii, 2] + x * aKco[ii, 3]));
            }

            double[] phii51 = phif.Slice(1, nphif);
            double[] phii52 = phii.Slice(1, ni);
            for (int a = 1; a <= nphif;a++)
            {
                phii5[a * 2 - 1] = phii51[a];
            }

            for (int a = 1; a <= ni; a++)
            {
                phii5[a * 2] = phii52[a];
            }

            // Assemble flux table
            j = 2 * nfu - 1;
            for (ie = 0; ie < 2; ie++)
            {
                ft.fend[ie].phif = new double[phif.Length];
                ft.fend[ie].sid = sp.sid;
                ft.fend[ie].nfu = j;
                ft.fend[ie].nft = i;
                ft.fend[ie].dz = dz;
                ft.fend[ie].phif = phii5; //(1:i) assume it's the whole array
            }
            ft.ftable = qi5; // (1:i,1:i) as above
        }
Пример #12
0
 /// <summary>
 /// Public accessor to set a SoilProps object.
 /// Only required for unit testing.
 /// </summary>
 /// <param name="setsp"></param>
 /// <param name="setnu"></param>
 /// <param name="sethpK"></param>
 public static void SetupSsflux(SoilProps setsp, int setnu, double[] sethpK)
 {
     sp = setsp;
     nu = setnu;
     hpK = sethpK;
 }
Пример #13
0
        public static void FluxTable(double dz, SoilProps sp)
        {
            // Generates a flux table for use by other programs.
            // Assumes soil props available in sp of module soil.
            // dz - path length.

            //diags - timer start here

            nu = sp.nc;
            ah = sp.hc; aK = sp.Kc; aphi = sp.phic; aS = sp.Sc;
            aKco = sp.Kco; aphico = sp.phico;
            he = sp.he; Ks = sp.ks;

            // Get K values for Simpson's integration rule in subroutine odef.
            for (i = 0; i < nu - 2; i++)
            {
                x = 0.5 * (aphi[i + 1] - aphi[i]);
                hpK[i] = aK[i] + x * (aKco[0, i] + x * (aKco[1, i] + x * aKco[2, i]));
            }

            // Get fluxes aq(1,:) for values aphi[i] at bottom (wet), aphi(1) at top (dry).
            // These are used to select suitable phi values for flux table.
            nit = 0;
            aq[1, 1] = aK[1]; // q=K here because dphi/dz=0
            dh = 2.0; // for getting phi in saturated region
            q1 = (aphi[1] - aphi[2]) / dz; // q1 is initial estimate
            aq[1, 2] = ssflux(1, 2, dz, q1, 0.1 * rerr); // get accurate flux
            for (j = 2; j < nu + 20; j++) // 20*dh should be far enough for small curvature in (phi,q)
                if (j > nu) // part satn - set h, K and phi
                {
                    ah[j] = ah[j - 1] + dh * (j - nu);
                    aK[j] = Ks;
                    aphi[j] = aphi[j - 1] + Ks * dh * (j - nu);
                }

            // get approx q from linear extrapolation
            q1 = aq[1, j - 1] + (aphi[j] - aphi[j - 1]) * (aq[1, j - 1] - aq[1, j - 2]) / (aphi[j - 1] - aphi[j - 2]);
            aq[1, j] = ssflux(1, j, dz, q1, 0.1 * rerr); // get accurate q
            nt = j;
            ns = nt - nu;
            if (j > nu)
                if (-(aphi[j] - aphi[j - 1]) / (aq[1, j] - aq[1, j - 1]) < (1 + rerr) * dz)
                    Environment.Exit(0);

            // Get phi values phif for flux table using curvature of q vs phi.
            // rerr and cfac determine spacings of phif.
            Vector<double> aphiV = Vector<double>.Build.DenseOfArray(aphi);
            Matrix<double> aqM = Matrix<double>.Build.DenseOfArray(aq);
            i = nonlin(nu, aphiV.SubVector(0, nu).ToArray(), aqM.Column(0).SubVector(0, nu).ToArray(), rerr);
            re = curv(nu, aphiV.SubVector(0, nu).ToArray(), aqM.Column(0).SubVector(0, nu).ToArray());// for unsat phi
            indices(nu - 2, re.Take(nu - 3).Reverse().ToArray(), 1 + nu - i, cfac, out nphif, ref iphif);
            int[] iphifReverse = iphif.Take(nphif).Reverse().ToArray();
            for (int idx = 0; idx < nphif; idx++)
                iphif[idx] = 1 + nu - iphifReverse[idx]; // locations of phif in aphi
            aphiV = Vector<double>.Build.DenseOfArray(aphi); //may not need to do this; haevn't check in aphi has changed since last use.
            aqM = Matrix<double>.Build.DenseOfArray(aq); //as above
            re = curv(1 + ns, aphiV.SubVector(nu, nt - nu).ToArray(), aqM.Column(1).SubVector(nu, nt - nu).ToArray()); // for sat phi
            indices(ns - 1, re, ns, cfac, out nfs, ref ifs);

            for (int idx = nphif; i < nphif + nfs - 2; idx++)
                iphif[idx] = nu - 1 + ifs[idx];
            nfu = nphif; // no. of unsat phif
            nphif = nphif + nfs - 1;
            for (int idx = 0; i < nphif; idx++)
            {
                phif[idx] = aphi[iphif[idx]];
                qf[0, idx] = aq[0, iphif[idx]];
            }
            // Get rest of fluxes
            // First for lower end wetter
            for (j = 1; j < nphif; j++)
                for (i = 1; i < j; i++)
                {
                    q1 = qf[i - 1, j];
                    if (ah[iphif[j]] - dz < ah[iphif[i]])
                        q1 = 0.0; // improve?
                    qf[i, j] = ssflux(iphif[i], iphif[j], dz, q1, 0.1 * rerr);
                }
            // Then for upper end wetter
            for (i = 1; i < nphif; i++)
                for (j = i - 1; j > 1; j--)
                {
                    q1 = qf[i, j + 1];
                    if (j + 1 == i)
                        q1 = q1 + (aphi[iphif[i]] - aphi[iphif[j]]) / dz;
                    qf[i, j] = ssflux(iphif[i], iphif[j], dz, q1, 0.1 * rerr);
                }
            // Use of flux table involves only linear interpolation, so gain accuracy
            // by providing fluxes in between using quadratic interpolation.
            ni = nphif - 1;
            for (int idx = 0; idx < ni; idx++)
                phii[idx] = 0.5 * (phif[idx] + phif[idx + 1]);

            Matrix<double> qi1M = Matrix<double>.Build.DenseOfArray(qi1);
            Matrix<double> qfM = Matrix<double>.Build.DenseOfArray(qf);
            double[] qi1Return;
            double[] qi2Return;
            double[] qi3Return;

            for (i = 0; i < nphif; i++)
            {
                qi1Return = quadinterp(phif, qfM.Row(i).ToArray(), nphif, phii);
                for (int idx = 0; idx < qi1Return.Length; idx++)
                    qi1[i, idx] = qi1Return[idx];
            }

            for (j = 0; j < nphif; j++)
            {
                qi2Return = quadinterp(phif, qfM.Column(j).ToArray(), nphif, phii);
                for (int idx = 0; idx < qi2Return.Length; idx++)
                    qi2[idx, i] = qi2Return[idx];
            }

            for (j = 0; j < ni; j++)
            {
                qi1M = Matrix<double>.Build.DenseOfArray(qi1);
                qi3Return = quadinterp(phif, qi1M.Column(j).ToArray(), nphif, phii);
                for (int idx = 0; idx < qi3Return.Length; idx++)
                    qi3[idx, i] = qi3Return[idx];
            }

            // Put all the fluxes together.
            i = nphif + ni;
            for (int iidx = 0; iidx < i; i += 2)
                for (int npidx = 0; npidx < nphif; npidx++)
                    for (int niidx = 0; niidx < ni; niidx++)
                    {
                        qi5[iidx, iidx] = qf[npidx, npidx];
                        qi5[iidx, iidx + 1] = qi1[npidx, niidx];
                        qi5[iidx + 1, iidx] = qi2[niidx, npidx];
                        qi5[iidx + 1, iidx + 1] = qi3[niidx, niidx];
                    }

            // Get accurate qi5(j,j)=Kofphi(phii(ip))
            ip = 0;
            for (j = 1; j < i; j += 2)
            {
                ip = ip + 1;
                ii = iphif[ip + 1] - 1;
                for (int idx = 0; idx < aphi.Length; idx++) // Search down to locate phii position for cubic.
                {
                    if (aphi[ii] <= phii[ip])
                        break;
                    ii = ii - 1;
                }
                x = phii[ip] - aphi[ii];
                qi5[j, j] = aK[ii] + x * (aKco[1, ii] + x * (aKco[2, ii] + x * aKco[3, ii]));
            }

            for (int idx = 0; idx < i; i++)
            {
                phii5[idx * 2] = phif[idx];
                phii5[idx * 2 + 1] = phii[idx];
            }

            // diags - end timer here

            // Assemble flux table
            j = 2 * nfu - 1;
            for (ie = 0; ie < 2; ie++)
            {
                pe = ft.fend[ie];
                pe.phif = new double[phif.Length];
                pe.sid = sp.sid;
                pe.nfu = j;
                pe.nft = i;
                pe.dz = dz;
                pe.phif = phii5; //(1:i) assume it's the whole array
            }
            ft.ftable = qi5; // (1:i,1:i) as above
        }
Пример #14
0
        public static void FluxTable(double dz, SoilProps props)
        {
            // Generates a flux table for use by other programs.
            // Assumes soil props available in sp of module soil.
            // dz - path length.

            sp      = props;
            ft.fend = new FluxEnd[2];
            nu      = sp.nc;
            he      = sp.he; Ks = sp.ks;
            for (i = 1; i <= nu - 1; i++)
            {
                for (j = 1; j < sp.Kco.GetLength(0); j++)
                {
                    aKco[i, j] = sp.Kco[j, i];
                }
            }

            // Get K values for Simpson's integration rule in subroutine odef.
            for (i = 1; i <= nu - 1; i++)
            {
                x      = 0.5 * (sp.phic[i + 1] - sp.phic[i]);
                hpK[i] = sp.Kc[i] + x * (aKco[i, 1] + x * (aKco[i, 2] + x * aKco[i, 3]));
            }

            // Get fluxes aq(1,:) for values aphi[i] at bottom (wet), aphi(1) at top (dry).
            // These are used to select suitable phi values for flux table.
            // Note that due to the complexity of array indexing in the FORTRAN,
            // we're keeping aq 1 indexed.
            nit      = 0;
            aq[1, 1] = sp.Kc[1];                         // q=K here because dphi/dz=0
            dh       = 2.0;                              // for getting phi in saturated region
            q1       = (sp.phic[1] - sp.phic[2]) / dz;   // q1 is initial estimate
            aq[2, 1] = ssflux(1, 2, dz, q1, 0.1 * rerr); // get accurate flux
            for (j = 3; j <= nu + 20; j++)               // 20*dh should be far enough for small curvature in (phi,q)
            {
                if (j > nu)                              // part satn - set h, K and phi
                {
                    sp.hc[j]   = sp.hc[j - 1] + dh * (j - nu);
                    sp.Kc[j]   = Ks;
                    sp.phic[j] = sp.phic[j - 1] + Ks * dh * (j - nu);
                }

                // get approx q from linear extrapolation
                q1       = aq[j - 1, 1] + (sp.phic[j] - sp.phic[j - 1]) * (aq[j - 1, 1] - aq[j - 2, 1]) / (sp.phic[j - 1] - sp.phic[j - 2]);
                aq[j, 1] = ssflux(1, j, dz, q1, 0.1 * rerr); // get accurate q
                nt       = j;
                ns       = nt - nu;
                if (j > nu)
                {
                    if (-(sp.phic[j] - sp.phic[j - 1]) / (aq[j, 1] - aq[j - 1, 1]) < (1 + rerr) * dz)
                    {
                        break;
                    }
                }
            }

            // Get phi values phif for flux table using curvature of q vs phi.
            // rerr and cfac determine spacings of phif.
            Matrix <double> aqM = Matrix <double> .Build.DenseOfArray(aq);

            i  = nonlin(nu, sp.phic.Slice(1, nu), aqM.Column(1).ToArray().Slice(1, nu), rerr);
            re = curv(nu, sp.phic.Slice(1, nu), aqM.Column(1).ToArray().Slice(1, nu));                                          // for unsat phi
            double[] rei = new double[nu - 2 + 1];
            Array.Copy(re.Slice(1, nu - 2).Reverse().ToArray(), 0, rei, 1, re.Slice(1, nu - 2).Reverse().ToArray().Length - 1); //need to 1-index slice JF
            Indices(nu - 2, rei, 1 + nu - i, cfac, out nphif, out iphif);
            int[] iphifReverse  = iphif.Skip(1).Take(nphif).Reverse().ToArray();
            int[] iphifReversei = new int[iphifReverse.Length + 1];
            Array.Copy(iphifReverse, 0, iphifReversei, 1, iphifReverse.Length); // again, need to 1-index JF
            for (int idx = 1; idx < nphif; idx++)
            {
                iphif[idx] = 1 + nu - iphifReversei[idx];                                    // locations of phif in aphi
            }
            aqM = Matrix <double> .Build.DenseOfArray(aq);                                   //as above

            re = curv(1 + ns, sp.phic.Slice(nu, nt), aqM.Column(1).ToArray().Slice(nu, nt)); // for sat phi
            Indices(ns - 1, re, ns, cfac, out nfs, out ifs);

            int[] ifsTemp = ifs.Slice(2, nfs);
            for (int idx = nphif + 1; idx <= nphif + nfs - 1; idx++)
            {
                iphif[idx] = nu - 1 + ifsTemp[idx - nphif];
            }
            nfu   = nphif; // no. of unsat phif
            nphif = nphif + nfs - 1;
            for (int idx = 1; idx <= nphif; idx++)
            {
                phif[idx]  = sp.phic[iphif[idx]];
                qf[idx, 1] = aq[iphif[idx], 1];
            }

            // Get rest of fluxes
            // First for lower end wetter
            for (j = 2; j <= nphif; j++)
            {
                for (i = 2; i <= j; i++)
                {
                    q1 = qf[j, i - 1];
                    if (sp.hc[iphif[j]] - dz < sp.hc[iphif[i]])
                    {
                        q1 = 0.0; // improve?
                    }
                    qf[j, i] = ssflux(iphif[i], iphif[j], dz, q1, 0.1 * rerr);
                }
            }
            // Then for upper end wetter
            for (i = 2; i <= nphif; i++)
            {
                for (j = i - 1; j >= 1; j--)
                {
                    q1 = qf[j + 1, i];
                    if (j + 1 == i)
                    {
                        q1 = q1 + (sp.phic[iphif[i]] - sp.phic[iphif[j]]) / dz;
                    }
                    qf[j, i] = ssflux(iphif[i], iphif[j], dz, q1, 0.1 * rerr);
                }
            }
            // Use of flux table involves only linear interpolation, so gain accuracy
            // by providing fluxes in between using quadratic interpolation.
            ni = nphif - 1;
            for (int idx = 1; idx <= ni; idx++)
            {
                phii[idx] = 0.5 * (phif[idx] + phif[idx + 1]);
            }

            Matrix <double> qi1M = Matrix <double> .Build.DenseOfArray(qi1);

            Matrix <double> qfM = Matrix <double> .Build.DenseOfArray(qf);

            double[] qi1Return;
            double[] qi2Return;
            double[] qi3Return;

            for (i = 1; i <= nphif; i++)
            {
                qi1Return = Quadinterp(phif, qfM.Column(i).ToArray(), nphif, phii);
                for (int idx = 1; idx < qi1Return.Length; idx++)
                {
                    qi1[idx, i] = qi1Return[idx];
                }
            }

            for (j = 1; j <= nphif; j++)
            {
                qi2Return = Quadinterp(phif, qfM.Row(j).ToArray(), nphif, phii);
                for (int idx = 1; idx < qi2Return.Length; idx++)
                {
                    qi2[j, idx] = qi2Return[idx];
                }
            }

            for (j = 1; j <= ni; j++)
            {
                qi1M = Matrix <double> .Build.DenseOfArray(qi1);

                qi3Return = Quadinterp(phif, qi1M.Row(j).ToArray(), nphif, phii);
                for (int idx = 1; idx < qi3Return.Length; idx++)
                {
                    qi3[j, idx] = qi3Return[idx];
                }
            }

            // Put all the fluxes together.
            i = nphif + ni;
            for (int row = 1; row <= i; row += 2)
            {
                for (int col = 1; col <= i; col += 2)
                {
                    qi5[col, row]         = qf[col / 2 + 1, row / 2 + 1];
                    qi5[col + 1, row]     = qi1[col / 2 + 1, row / 2 + 1];
                    qi5[col, row + 1]     = qi2[col / 2 + 1, row / 2 + 1];
                    qi5[col + 1, row + 1] = qi3[col / 2 + 1, row / 2 + 1];
                }
            }

            // Get accurate qi5(j,j)=Kofphi(phii(ip))
            ip = 0;
            for (j = 2; j <= i; j += 2)
            {
                ip = ip + 1;
                ii = iphif[ip + 1] - 1;
                // if (ii >= sp.Kco.GetLength(1))
                //     ii = sp.Kco.GetLength(1) - 1;
                while (true) // Search down to locate phii position for cubic.
                {
                    if (sp.phic[ii] <= phii[ip])
                    {
                        break;
                    }
                    ii = ii - 1;
                }
                x         = phii[ip] - sp.phic[ii];
                qi5[j, j] = sp.Kc[ii] + x * (aKco[ii, 1] + x * (aKco[ii, 2] + x * aKco[ii, 3]));
            }

            double[] phii51 = phif.Slice(1, nphif);
            double[] phii52 = phii.Slice(1, ni);
            for (int a = 1; a <= nphif; a++)
            {
                phii5[a * 2 - 1] = phii51[a];
            }

            for (int a = 1; a <= ni; a++)
            {
                phii5[a * 2] = phii52[a];
            }

            // Assemble flux table
            j = 2 * nfu - 1;
            for (ie = 0; ie < 2; ie++)
            {
                ft.fend[ie].phif = new double[phif.Length];
                ft.fend[ie].sid  = sp.sid;
                ft.fend[ie].nfu  = j;
                ft.fend[ie].nft  = i;
                ft.fend[ie].dz   = dz;
                ft.fend[ie].phif = phii5; //(1:i) assume it's the whole array
            }
            ft.ftable = qi5;              // (1:i,1:i) as above
        }
Пример #15
0
        public static FluxTable TwoTables(FluxTable ft1, SoilProps sp1, FluxTable ft2, SoilProps sp2)
        {
            /*Generates a composite flux table from two uniform ones.
             * Sets up quadratic interpolation table to get phi at interface for lower path
             * from phi at interface for upper path.
             * Sets up cubic interpolation tables to get fluxes from phi at interface for
             * upper and lower paths.
             * Solves for phi at interface in upper path that gives same fluxes in upper
             * and lower paths, for all phi at upper and lower ends of composite path.
             * Increases no. of fluxes in table by quadratic interpolation.
             * ft1, ft2 - flux tables for upper and lower paths.
             * sp1, sp2 - soil prop tables for upper and lower paths. -PR
             *
             * Note that arrays are one indexed; 0 index is not used.
             * This is done as a number of calculations use the index as an input.
             * Exception to this is flux/soil arrays where array index is not used in calculations. -JF
             */


            // Set up required pointers and data
            if (ft1.fend[0].sid != ft1.fend[1].sid || ft2.fend[0].sid != ft2.fend[1].sid)
            {
                Console.WriteLine("Flux table not for uniform soil.");
                Environment.Exit(1);
            }
            ft1.ftable = Matrix <double> .Build.DenseOfArray(ft1.ftable).Transpose().ToArray();

            ft2.ftable = Matrix <double> .Build.DenseOfArray(ft2.ftable).Transpose().ToArray();

            ft[0] = ft1;
            ft[1] = ft2;
            sp[0] = sp1;
            sp[1] = sp2;
            for (i = 1; i <= 2; i++)
            {
                n[i]    = sp[i - 1].n;
                he[i]   = sp[i - 1].he;
                phie[i] = sp[i - 1].phie;
                Ks[i]   = sp[i - 1].ks;
                for (int x = 1; x <= n[i]; x++)
                {
                    h[x, i]   = sp[i - 1].h[x];
                    phi[x, i] = sp[i - 1].phi[x];
                }
            }

            // Discard unwanted input - use original uninterpolated values only.
            for (i = 1; i <= 2; i++)
            {
                m = ft[i - 1].fend[0].nft; //should be odd
                j = 1 + m / 2;
                double[] tempPhif = new double[j + 1];
                Array.Copy(ft[i - 1].fend[0].phif.Where((x, it) => it % 2 == 1).ToArray(), 0, tempPhif, 1, j);
                for (int x = 1; x <= j; x++)
                {
                    phif[x, i] = tempPhif[x]; //discard every second
                }
                nft[i] = j;
                nfu[i] = 1 + ft[i - 1].fend[1].nfu / 2; //ft[i].fend[1].nfu should be odd


                double[,] tempFt = new double[m + 1, m + 1];
                for (int a = 1; a <= m; a += 2)
                {
                    for (int b = 1; b <= m; b += 2)
                    {
                        tempFt[a / 2 + 1, b / 2 + 1] = ft[i - 1].ftable[a, b];
                    }
                }

                for (int a = 1; a <= m; a++)
                {
                    for (int b = 1; b <= m; b++)
                    {
                        qf[b, a, i] = tempFt[a, b];
                    }
                }
            }

            // Extend phi2 and h2 if he1>he2, or vice-versa.
            dhe = Math.Abs(he[1] - he[2]);
            if (dhe > 0.001)
            {
                if (he[1] > he[2])
                {
                    i = 1;
                    j = 2;
                }
                else
                {
                    i = 2;
                    j = 1;
                }
                double[] hFind = new double[n[i] + 1];
                for (int x = 1; x <= n[i] + 1; x++)
                {
                    hFind[x - n[i] + 1] = h[i, x];
                }
                ii = Find(he[j], hFind);
                for (k = 1; k <= n[i] - ii; k++)
                {
                    h[j, n[j] + k]   = h[i, ii + k];//test these
                    phi[j, n[j] + k] = phie[j] + Ks[j] * (h[i, ii + k] - he[j]);
                }
                n[j] = n[j] + n[i] - ii;
            }
            phi1max = phi[n[1], 1];

            // Get phi for same h.
            if (h[1, 1] > h[1, 2])
            {
                i = 1;
                j = 2;
            }
            else
            {
                i = 2;
                j = 1;
            }

            Matrix <double> hm = Matrix <double> .Build.DenseOfArray(h); //test

            double[] absh = hm.Column(j).ToArray();
            absh = absh.Slice(1, n[j]);
            absh = MathUtilities.Subtract_Value(absh, h[1, i]);
            for (int x = 0; x < absh.Length; x++)
            {
                absh[x] = Math.Abs(absh[x]);
            }
            id = MinLoc(absh);
            if (h[id, j] >= h[1, 1])
            {
                id--;
            }

            //phii(j,:) for soil j will match h(i,:) from soil i and h(j, 1:id) from soil j.
            //phii(i,:) for soil i will match h(i,:) and fill in for h(j, 1:id).
            for (int iid = 1; iid <= id; iid++)
            {
                phii[iid, j] = phi[iid, j]; // keep these values
            }
            // But interpolate to match values that start at greater h.
            jj = id + 1;                       //h(j,id+1) to be checked first
            phii[id + n[i], j] = phi[n[j], j]; // last h values match
            for (ii = 1; ii <= n[i] - 1; ii++)
            {
                while (true) //get place of h(i,ii) in h array for soil j
                {
                    if (jj > n[j])
                    {
                        Console.WriteLine("twotbls: h[j,n[j]] <= h[i,ii]; i, j, ii, n[j] = " + i + " " + j + " " + ii + " " + n[j]);
                        Environment.Exit(1);
                    }

                    if (h[jj, j] > h[ii, i])
                    {
                        break;
                    }
                    jj += 1;
                }

                k = jj - 1; //first point for cubic interp
                if (jj + 2 > n[j])
                {
                    k = n[j] - 3;
                }

                double[] hCuco   = new double[5];
                double[] phiCuco = new double[5];
                for (int x = k; x <= k + 3; x++)
                {
                    hCuco[x - k + 1]   = h[x, j];
                    phiCuco[x - k + 1] = phi[x, j];
                }

                co = Soil.Cuco(hCuco, phiCuco); // get cubic coeffs
                v  = h[ii, i] - h[k, j];
                phii[id + ii, j] = co[1] + v * (co[2] + v * (co[3] + v * co[4]));
            }
            ni = id + n[i];

            // Generate sensible missing values using quadratic extrapolation.
            co = Fluxes.Quadco(new double[4] {
                0, phii[1, j], phii[id + 1, j], phii[id + 2, j]
            }, new double[4] {
                0, 0, phi[1, i], phi[2, i]
            });
            if (co[2] > 0) // +ve slope at zero - ok
            {
                for (int x = 1; x <= id; x++)
                {
                    xval[x]    = phii[x, j] - phii[1, j];
                    phii[x, i] = co[i] + xval[x] * (co[2] + xval[x] * co[3]);
                }
            }
            else // -ve slope at zero, use quadratic with zero slope at zero
            {
                co[3] = phi[1, i] / Math.Pow(phii[id + 1, j], 2);
                for (int x = 1; x <= id; x++)
                {
                    phii[x, i] = co[3] * Math.Pow(phii[x, j], 2);
                }
            }

            // phii(i,id+1:ni)=phi(i,1:n(i))
            double[] phin = new double[ni - id + 1];
            for (int x = 1; x <= n[i]; x++)
            {
                phin[x] = phi[x, i];
            }
            for (int x = id + 1; x <= ni; x++)
            {
                phii[x, i] = phin[x - (id + 1) + 1];
            }

            //hi(1:id) = h(j, 1:id)
            for (int x = 1; x <= id; x++)
            {
                hi[x] = h[x, j];
            }

            // hi(id+1:ni)=h(i,1:n(i))
            for (int x = 1; x <= n[i]; x++)
            {
                hi[id + x] = h[x, i];
            }

            /* hi(1:ni) are h values for the interface tables.
             * phii(1,1:ni) are corresponding interface phi values for upper layer.
             * phii(2,1:ni) are corresponding interface phi values for lower layer.
             * Set up quadratic interpolation coeffs to get phii2 given phii1.
             */
            Matrix <double> coqM = Matrix <double> .Build.DenseOfArray(coq);

            Vector <double>[] quadcoV = new Vector <double> [ni - 2 + 1];
            double[]          tmpx;
            double[]          tmpy;
            for (i = 1; i <= ni - 2; i++)
            {
                tmpx       = new [] { 0, phii[i, 1], phii[i + 1, 1], phii[i + 2, 1] };
                tmpy       = new [] { 0, phii[i, 2], phii[i + 1, 2], phii[i + 2, 2] };
                quadcoV[i] = Vector <double> .Build.DenseOfArray(Fluxes.Quadco(tmpx, tmpy));

                coqM.SetRow(i, quadcoV[i]);
            }
            Vector <double> lincoV = Vector <double> .Build.DenseOfArray(Linco(new [] { 0, phii[ni - 1, 1], phii[ni, 1] }, new [] { 0, phii[ni - 1, 2], phii[ni, 2] }));

            coqM.SetRow(ni - 1, lincoV);
            coq            = coqM.ToArray();
            coq[ni - 1, 3] = 0;

            double[,] getco = new double[20, 20];

            // Set up cubic coeffs to get fluxes q given phi.
            for (j = 1; j <= nft[2]; j++)
            {
                k  = 1;
                ip = 1;
                while (true)
                {
                    phico2[k] = phif[ip, 2];
                    double[] co2co = Soil.Cuco(new double[5] {
                        0, phif[ip, 2], phif[ip + 1, 2], phif[ip + 2, 2], phif[ip + 3, 2]
                    },
                                               new double[5] {
                        0, qf[j, ip, 2], qf[j, ip + 1, 2], qf[j, ip + 2, 2], qf[j, ip + 3, 2]
                    });
                    for (int x = 1; x < co2co.Length; x++)
                    {
                        co2[j, k, x] = co2co[x];
                    }
                    ip += 3;
                    if (ip == nft[2])
                    {
                        break;
                    }
                    if (ip > nft[2])
                    {
                        ip = nft[2] - 3;
                    }
                    k++;
                }
                for (int x = 1; x < 20; x++)
                {
                    for (int y = 1; y < 20; y++)
                    {
                        getco[y, x] = co2[y, x, 1];
                    }
                }
            }

            nco2 = k;

            // Get fluxes
            for (i = 1; i <= nft[1]; i++) //step through top phis
            {
                vlast = phif[i, 1];
                k     = 1;
                ip    = 1;
                Matrix <double> co1M = Matrix <double> .Build.DenseOfArray(co1);

                while (true)
                {
                    phico1[k] = phif[ip, 1];
                    co1M.SetRow(k, Soil.Cuco(new double[5] {
                        0, phif[ip, 1], phif[ip + 1, 1], phif[ip + 2, 1], phif[ip + 3, 1]
                    },
                                             new double[5] {
                        0, qf[ip, i, 1], qf[ip + 1, i, 1], qf[ip + 2, i, 1], qf[ip + 3, i, 1]
                    }));
                    ip += 3;
                    if (ip == nft[1])
                    {
                        break;
                    }
                    if (ip > nft[1])
                    {
                        ip = nft[1] - 3;
                    }
                    k++;
                }
                co1  = co1M.ToArray();
                nco1 = k;
                for (j = 1; j <= nft[2]; j++) // bottom phis
                {
                    v = vlast;
                    for (k = 1; k <= maxit; k++) // solve for upper interface phi giving same fluxes
                    {
                        fd(v, out f, out df, out q1);
                        dx = f / df; // Newton's method - almost always works
                        v  = Math.Min(10.0 * phif[nft[1], 1], Math.Max(phii[1, 1], v - dx));
                        e  = Math.Abs(f / q1);
                        if (e < rerr)
                        {
                            break;
                        }
                        vlast = v;
                    }
                    if (k > maxit) //failed - bracket q and use bisection
                    {
                        v1 = phii[1, 1];
                        fd(v1, out f1, out df, out q1);
                        if (f1 <= 0.0) // answer is off table - use end value
                        {
                            qp[j, i] = q1;
                            continue;
                        }
                        v2 = phii[ni, 1];
                        fd(v2, out f2, out df, out q1);
                        for (k = 1; k <= maxit; k++)
                        {
                            if (f1 * f2 < 0.0)
                            {
                                break;
                            }
                            v1 = v2;
                            f1 = f2;
                            v2 = 2.0 * v1;
                            fd(v2, out f2, out df, out q1);
                        }
                        if (k > maxit)
                        {
                            Console.WriteLine(v1 + " " + v2 + " " + f1 + " " + f2);
                            v1 = phii[1, 1];
                            fd(v1, out f1, out df, out q1);
                            Console.WriteLine(v1 + " " + f1);
                            Console.WriteLine("twotbls: too many iterations at i, j = " + i + " " + j);
                            Environment.Exit(1);
                        }
                        for (k = 1; k <= maxit; k++)
                        {
                            v = 0.5 * (v1 + v2);
                            fd(v, out f, out df, out q1);
                            e = Math.Abs(f / q1);
                            if (e < rerr)
                            {
                                break;
                            }
                            if (f > 0.0)
                            {
                                v1 = v;
                                f1 = f;
                            }
                            else
                            {
                                v2 = v;
                                f2 = f;
                            }
                        }
                        vlast = v;
                        if (k > maxit)
                        {
                            Console.WriteLine("twotbls: too many iterations at i, j = " + i + " " + j);
                            Environment.Exit(1);
                        }
                    }
                    // Solved
                    qp[j, i] = q1;
                } //end j
            }     //end i

            //interpolate extra fluxes
            for (i = 1; i <= 2; i++)
            {
                nfi[i] = nft[i] - 1;
                for (int x = 1; x <= nfi[i]; x++)
                {
                    phifi[x, i] = 0.5 * (phif[x, i] + phif[x + 1, i]);
                }
            }

            Matrix <double> phifM = Matrix <double> .Build.DenseOfArray(phif);

            Matrix <double> phifiM = Matrix <double> .Build.DenseOfArray(phifi);

            Matrix <double> qpM = Matrix <double> .Build.DenseOfArray(qp);

            Matrix <double> qi1M = Matrix <double> .Build.DenseOfArray(qi1);

            Matrix <double> qi2M = Matrix <double> .Build.DenseOfArray(qi2);

            Matrix <double> qi3M = Matrix <double> .Build.DenseOfArray(qi3);

            for (i = 1; i <= nft[1]; i++)
            {
                qi1M.SetColumn(i, Fluxes.Quadinterp(phifM.Column(2).ToArray(), qpM.Column(i).ToArray(), nft[2], phifiM.Column(2).ToArray()));
            }
            for (j = 1; j <= nft[2]; j++)
            {
                qi2M.SetRow(j, Fluxes.Quadinterp(phifM.Column(1).ToArray(), qpM.Row(j).ToArray(), nft[1], phifiM.Column(1).ToArray()));
            }
            for (j = 1; j <= nfi[2]; j++)
            {
                qi3M.SetRow(j, Fluxes.Quadinterp(phifM.Column(1).ToArray(), qi1M.Row(j).ToArray(), nft[1], phifiM.Column(1).ToArray()));
            }

            qi1 = qi1M.ToArray();
            qi2 = qi2M.ToArray();
            qi3 = qi3M.ToArray();

            // Put all the fluxes together
            i = nft[1] + nfi[1];
            j = nft[2] + nfi[2];

            // qi5(1:i:2,1:j:2)=qp(1:nft[1],1:nft[2])
            for (int a = 1; a <= mx; a += 2)
            {
                for (int b = 1; b <= mx; b += 2)
                {
                    qi5[b, a] = qp[b / 2 + 1, a / 2 + 1];
                }
            }

            // qi5(1:a: 2, 2:b: 2) = qi1(1:nft[1], 1:nfi[2])
            for (int a = 1; a <= mx; a += 2)
            {
                for (int b = 2; b <= mx; b += 2)
                {
                    qi5[b, a] = qi1[(b - 1) / 2 + 1, a / 2 + 1];
                }
            }

            // qi5(2:a:2,1:b:2)=qi2(1:nfi[1],1:nft[2])
            for (int a = 2; a <= mx; a += 2)
            {
                for (int b = 1; b <= mx; b += 2)
                {
                    qi5[b, a] = qi2[b / 2 + 1, (a - 1) / 2 + 1];
                }
            }

            // qi5(2:a:2,2:b:2)=qi3(1:nfi[1],1:nfi[2])
            for (int a = 2; a <= mx; a += 2)
            {
                for (int b = 2; b <= mx; b += 2)
                {
                    qi5[b, a] = qi3[(b - 1) / 2 + 1, (a - 1) / 2 + 1];
                }
            }

            // phii5(1, 1:a: 2) = phif(1, 1:nft[1])
            for (int a = 1; a <= mx; a += 2)
            {
                phii5[a, 1] = phif[a / 2 + 1, 1];
            }

            // phii5(1,2:a:2)=phifi(1,1:nfi[1])
            for (int a = 2; a < 101; a += 2)
            {
                phii5[a, 1] = phifi[(a - 1) / 2 + 1, 1];
            }

            // phii5(2,1:b:2)=phif(2,1:nft[2])
            for (int a = 1; a <= mx; a += 2)
            {
                phii5[a, 2] = phif[a / 2 + 1, 2];
            }

            // phii5(2,2:b:2)=phifi(2,1:nfi[2])
            for (int a = 2; a <= mx; a += 2)
            {
                phii5[a, 2] = phifi[(a - 1) / 2 + 1, 2];
            }


            // Assemble flux table
            ftwo.fend = new FluxEnd[2];
            for (ie = 0; ie < 2; ie++)
            {
                ftwo.fend[ie].sid  = sp[ie].sid;
                ftwo.fend[ie].nfu  = ft[ie].fend[1].nfu;
                ftwo.fend[ie].nft  = ft[ie].fend[1].nft;
                ftwo.fend[ie].dz   = ft[ie].fend[1].dz;
                ftwo.fend[ie].phif = ft[ie].fend[1].phif;
            }

            double[,] qi5Slice = new double[i + 1, j + 1];
            for (int x = 1; x <= i; x++)
            {
                for (int y = 1; y <= j; y++)
                {
                    qi5Slice[x, y] = qi5[x, y];
                }
            }
            ftwo.ftable = qi5Slice;

            return(ftwo);
        }
Пример #16
0
 /// <summary>
 /// Public accessor to set a SoilProps object.
 /// Only required for unit testing.
 /// </summary>
 /// <param name="setsp"></param>
 /// <param name="setnu"></param>
 /// <param name="sethpK"></param>
 public static void SetupSsflux(SoilProps setsp, int setnu, double[] sethpK)
 {
     sp  = setsp;
     nu  = setnu;
     hpK = sethpK;
 }
Пример #17
0
        public static void GenerateFlux()
        {
            MVG.TestParams(103, 9.0, 0.99670220130280185, 9.99999999999998460E-003);
            SoilProps sp = Soil.gensptbl(1.0, new SoilParam(10, 103, 0.4, 2.0, -2.0, -10.0, 1.0 / 3.0, 1.0), true);

            Fluxes.FluxTable(5.0, sp);
            FluxTable ft = Fluxes.ft;

            string output = string.Empty;

            //define test soils
            SoilParam[] soils = new SoilParam[2];
            soils[0] = new SoilParam(10, 103, 0.4, 2.0, -2.0, -10.0, 1.0 / 3.0, 1.0);
            soils[1] = new SoilParam(10, 109, 0.6, 0.2, -2.0, -40.0, 1.0 / 9.0, 1.0);

            string[] ftname = new string[2];
            int[]    sidx;
            int      i, j;

            int[]  ndz;
            double dzmin;

            double[] x;
            double[,] dz = new double[2, 10]; //only for testing? if not will need to change hardcoded dimensions.
            bool Kgiven = true;

            //define soil profile
            x     = new double[] { 10, 20, 30, 40, 60, 80, 100, 120, 160, 200 };    //length = num soil layers
            sidx  = new int[] { 103, 103, 103, 103, 109, 109, 109, 109, 109, 109 }; //soil ident of layers
            dzmin = 1.0;                                                            // smallest likely path length
            ndz   = new int[] { 2, 4 };                                             // for the two soil types - gives six flux tables
            //can be done in loops, but clearer this way and will only be used for testing
            dz[0, 0] = 5;
            dz[0, 1] = 10;
            dz[1, 0] = 10;
            dz[1, 1] = 20;
            dz[1, 2] = 30;
            dz[1, 4] = 40;
            for (i = 0; i < 2; i++)
            {
                MVG.Params(soils[i].sid, soils[i].ths, soils[i].ks, soils[i].he, soils[i].hd, soils[i].p, soils[i].hg, soils[i].em, soils[i].en); //set MVG params
                soils[i].sp = Soil.gensptbl(dzmin, soils[i], Kgiven);                                                                             // generate soil props
                Soil.SoilProperties.Add("soil" + soils[i].sid, soils[i].sp);
                for (j = 0; j <= ndz[i]; j++)
                {
                    Fluxes.FluxTable(dz[i, j], soils[i].sp);     // generate flux tables
                    using (MemoryStream ms = new MemoryStream()) // make copies of the tables or they get overwritten
                    {
                        BinaryFormatter fm = new BinaryFormatter();
                        fm.Serialize(ms, Fluxes.ft);
                        ms.Position = 0;
                        Fluxes.FluxTables.Add("soil" + soils[i].sid + "dz" + (dz[i, j] * 10), (FluxTable)fm.Deserialize(ms));
                    }
                }
            }
            SoilProps sp1  = Soil.ReadProps("soil103");
            SoilProps sp2  = Soil.ReadProps("soil109");
            FluxTable ft1  = Fluxes.ReadFluxTable("soil103dz50");
            FluxTable ft2  = Fluxes.ReadFluxTable("soil109dz100");
            FluxTable ftwo = TwoFluxes.TwoTables(ft1, sp1, ft2, sp2);

            Fluxes.FluxTables.Add("soil103dz50_soil109dz100", ftwo);
        }