private static void WriteFluxes(BinaryWriter b, FluxTable fluxTable) { byte[] bytes; BinaryFormatter formatter = new BinaryFormatter(); using (MemoryStream stream = new MemoryStream()) { formatter.Serialize(stream, fluxTable); bytes = stream.ToArray(); } b.Write(bytes); }
/// <summary> /// Only used in unit tests. Resets all static variables /// </summary> public static void TestReset() { mx = 100; maxit = 20; i = j = k = m = ni = id = ip = nco1 = nco2 = ie = ii = jj = 0; nft = new int[2 + 1]; nfu = new int[2 + 1]; n = new int[2 + 1]; nfi = new int[2 + 1]; rerr = 1e-3; phi1max = dhe = v = vlast = dx = e = f = df = q1 = phialast = v1 = v2 = f1 = f2 = 0; he = new double[2 + 1]; phie = new double[2 + 1]; Ks = new double[2 + 1]; co = new double[4 + 1]; xval = new double[mx + 1]; phico1 = new double[mx + 1]; phico2 = new double[mx + 1]; hi = new double[3 * mx + 1]; phif = new double[mx + 1, 2 + 1]; phifi = new double[mx + 1, 2 + 1]; phii5 = new double[mx + 1, 2 + 1]; coq = new double[3 * mx + 1, 3 + 1]; co1 = new double[mx + 1, 4 + 1]; qp = new double[mx + 1, mx + 1]; qi1 = new double[mx + 1, mx + 1]; qi2 = new double[mx + 1, mx + 1]; qi3 = new double[mx + 1, mx + 1]; qi5 = new double[mx + 1, mx + 1]; h = new double[3 * mx + 1, 2 + 1]; phi = new double[3 * mx + 1, 2 + 1]; phii = new double[3 * mx + 1, 2 + 1]; qf = new double[mx + 1, mx + 1, 2 + 1]; co2 = new double[mx + 1, mx + 1, 4 + 1]; ftwo = new FluxTable(); ft = new FluxTable[] { new FluxTable(), new FluxTable() }; sp = new SoilProps[] { new SoilProps(), new SoilProps() }; }
public static FluxTable TwoTables(FluxTable ft1, SoilProps sp1, FluxTable ft2, SoilProps sp2) { return(new FluxTable()); }
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(); }
static void Main(string[] args) { 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 = false; SoilProps sp1, sp2; FluxTable ft1, ft2; //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++) { BinaryWriter b = new BinaryWriter(File.OpenWrite("soil" + soils[i].sid + ".dat")); 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); soils[i].sp = Soil.gensptbl(dzmin, soils[i], Kgiven); b.Write(soils[i].sid); WriteProps(b, soils[i].sp); b.Close(); for (j = 0; j < ndz[i]; j++) { Fluxes.FluxTable(dz[i, j], soils[i].sp); b = new BinaryWriter(File.OpenWrite("soil" + soils[i].sid + "dz" + dz[i, j] * 10)); b.Write(soils[i].sid); WriteFluxes(b, Fluxes.ft); b.Close(); } } //generate and write composite flux table for path with two soil types sp1 = ReadProps("soil103.dat"); sp2 = ReadProps("soil109.dat"); ft1 = ReadFluxes("soil103dz50.dat"); ft2 = ReadFluxes("soil103dz100.dat"); FluxTable ftwo = TwoFluxes.TwoTables(ft1, sp1, ft2, sp2); BinaryWriter bw = new BinaryWriter(File.OpenWrite("soil0103dz0050_soil0109dz0100.dat")); WriteFluxes(bw, ftwo); }
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); }