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); }
public static FluxTable TwoTables(FluxTable ft1, SoilProps sp1, FluxTable ft2, SoilProps sp2) { return(new FluxTable()); }
// 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); }
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; }
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; }
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]; }
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; }
public static FluxTable TwoTables(FluxTable ft1, SoilProps sp1, FluxTable ft2, SoilProps sp2) { return new FluxTable(); }
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 }
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 }
/// <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; }
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 }
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 }
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); }
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); }