/// <summary> /// Solves the ADE from time ti to tf. Diffusion of solute ignored - dispersion /// coeff = dispersivity* abs(pore water velocity). /// </summary> /// <param name="ti">start time (h).</param> /// <param name="tf">finish time.</param> /// <param name="thi">initial layer water contents.</param> /// <param name="thf">initial layer water contents.</param> /// <param name="dwexs">water extracted from layers over period ti to tf.</param> /// <param name="win">water in at top of profile.</param> /// <param name="cin">solute concn in win.</param> /// <param name="n">no. of soil layers.</param> /// <param name="ns">no. of solutes.</param> /// <param name="nex">no. of water extraction streams.</param> /// <param name="dx">layer thicknesses.</param> /// <param name="jt">layer soil type numbers for solute.</param> /// <param name="dsmmax">max change in sm of any layer to aim for each time step; controls time step size.</param> /// <param name="sm">layer masses of solute per cc.</param> /// <param name="sdrn">cumulative solute drainage.</param> /// <param name="nssteps">cumulative no. of time steps for ADE soln.</param> /// <param name="c"></param> /// <param name="sex">cumulative solute extractions in water extraction streams.</param> /// <param name="extraction">bool indicating if solute extraction is enabled.</param> /// <param name="solProps">Solute properties.</param> private static void Solute(double ti, double tf, double[] thi, double[] thf, double[,] dwexs, double win, double[] cin, int n, int ns, int nex, double[] dx, int[] jt, double dsmmax, ref double[,] sm, ref double[] sdrn, ref int[] nssteps, ref double[,] c, ref double[,,] sex, bool extraction, SolProps solProps) { int itmax = 20; //max iterations for finding c from sm double eps = 0.00001; // for stopping int i, it, j, k; double dc, dm, dmax, dt=0, f=0, fc=0, r, rsig, rsigdt, sig, sigdt, t, tfin, th, v1, v2; double[] dz = new double[n - 1 + 1]; double[] coef1 = new double[n - 1 + 1]; double[] coef2 = new double[n - 1 + 1]; double[] csm = new double[n + 1]; double[] tht = new double[n + 1]; double[] dwex = new double[n + 1]; double[] qsex = new double[n + 1]; double[] qsexd = new double[n + 1]; double[,] qsexs = new double[nex + 1, n + 1]; double[,] qsexsd = new double[nex + 1, n + 1]; double[] aa = new double[n + 1]; // these are 0 based double[] bb = new double[n + 1]; double[] cc = new double[n + 1]; double[] dd = new double[n + 1]; double[] dy = new double[n + 1]; double[] ee = new double[n + 1]; double[] q = new double[n + 1]; double[] qw = new double[n + 1]; double[] qya = new double[n + 1]; double[] qyb = new double[n + 1]; sig = 0.5; rsig = 1.0 / sig; tfin = tf; for (int x = 2; x <= n; x++) dz[x - 1] = 0.5 * (dx[x - 1] + dx[x]); //get average water fluxes //dwex = sum(dwexs, 2) !total changes in sink water extraction since last call if (dwexs.GetLength(0) > 0 && dwexs.GetLength(1) > 0) { Matrix<double> dwexsM = Matrix<double>.Build.DenseOfArray(dwexs); for (int x = 0; x < dwexs.GetLength(1); x++) { dwex[x] = MathUtilities.Sum(dwexsM.Row(x)); } } r = 1.0 / (tf - ti); qw[0] = r * win; // Compounding errors due to float/double issues start here. May or may not be a problem. tht = MathUtilities.Multiply_Value(MathUtilities.Subtract(thf, thi), r); for (i = 1; i <= n; i++) qw[i] = qw[i - 1] - dx[i] * tht[i] - r * dwex[i]; //get constant coefficients for (i = 1; i <= n - 1; i++) { v1 = 0.5 * qw[i]; v2 = 0.5 * (solProps.dis[jt[i]] + solProps.dis[jt[i + 1]]) * Math.Abs(qw[i]) / dz[i]; coef1[i] = v1 + v2; coef2[i] = v1 - v2; } for (j = 1; j <= ns; j++) { t = ti; if (qw[0] > 0.0) q[0] = qw[0] * cin[j]; else q[0] = 0.0; qyb[0] = 0.0; while (t < tfin) //get fluxes { for (i = 1; i <= n; i++) { //get c and csm = dc / dsm(with theta constant) k = jt[i]; th = thi[i] + (t - ti) * tht[i]; if (solProps.isotype[j, k] == "no" || sm[j, i] < 0.0) //handle sm < 0 here { csm[i] = 1.0 / th; c[j, i] = csm[i] * sm[j, i]; } else if (solProps.isotype[j, k] == "li") { csm[i] = 1.0 / (th + solProps.bd[k] * solProps.isopar[k, j].ElementAt(1)); c[j, i] = csm[i] * sm[j, i]; } else { for (it = 1; it <= itmax; it++) //get c from sm using Newton's method and bisection { if (c[j, i] < 0.0) c[j, i] = 0.0; //c and sm are >= 0 solProps.Isosub(solProps.isotype[j, k], c[j, i], dsmmax, ref solProps.isopar[j,k], out f, out fc); csm[i] = 1.0 / (th + solProps.bd[k] * fc); dm = sm[j, i] - (solProps.bd[k] * f + th * c[j, i]); dc = dm * csm[i]; if (sm[j, i] >= 0.0 && c[j, i] + dc < 0.0) c[j, i] = 0.5 * c[j, i]; else c[j, i] = c[j, i] + dc; if (Math.Abs(dm) < eps * (sm[j, i] + 10.0 * dsmmax)) break; if (it == itmax) { Console.WriteLine("solute: too many iterations getting c"); Environment.Exit(1); } } } } for (int x = 1; x <= n - 1; x++) { q[x] = coef1[x] * c[j, x] + coef2[x] * c[j, x + 1]; qya[x] = coef1[x] * csm[x]; qyb[x] = coef2[x] * csm[x + 1]; } q[n] = qw[n] * c[j, n]; qya[n] = qw[n] * csm[n]; //get time step double[] absQ = new double[n+1]; for (int x = 1; x <= n; x++) absQ[x] = Math.Abs(q[x] - q[x - 1] / dx[x]); dmax = MathUtilities.Max(absQ); if (dmax == 0.0) dt = tfin - t; else if (dmax < 0.0) { Console.WriteLine("solute: errors in fluxes prevent continuation"); Environment.Exit(1); } else dt = dsmmax / dmax; if (t + 1.1 * dt > tfin) { dt = tfin - t; t = tfin; } else t = t + dt; sigdt = sig * dt; rsigdt = 1.0 / sigdt; //adjust q for change in theta for (int x = 1; x <= n - 1; x++) q[x] = q[x] - sigdt * (qya[x] * tht[x] * c[j, x] + qyb[x] * tht[x + 1] * c[j, x]); q[n] = q[n] - sigdt * qya[n] * tht[n] * c[j, n]; //get and solve eqns for (int x = 2; x <= n; x++) aa[x] = qya[x - 1]; for (int x = 1; x <= n - 1; x++) cc[x] = -qyb[x]; Matrix<double> qsexsM = Matrix<double>.Build.DenseOfArray(qsexs); Matrix<double> qsexsdM = Matrix<double>.Build.DenseOfArray(qsexsd); if (sex.GetLength(0) > 1) //get extraction { double[] ctemp = new double[n + 1]; for (int x = 1; x <= n; x++) ctemp[x] = c[j, x]; qsexs = qsexsM.ToArray(); qsexsd = qsexsdM.ToArray(); sink.Ssinks(t, ti, tf, j, dwexs, ctemp, ref qsexs, ref qsexsd); qsex = qsexsM.ColumnSums().ToArray(); qsexd = qsexsdM.ColumnSums().ToArray(); for (int x = 1; x <= n; x++) { bb[x] = qyb[x - 1] - qya[x] - qsexd[x] * csm[x] - dx[x] * rsigdt; dd[x] = -(q[x - 1] - q[x] - qsex[x]) * rsig; } } else { for (int x = 1; x <= n; x++) { bb[x] = qyb[x - 1] - qya[x] - dx[x] * rsigdt; dd[x] = -(q[x - 1] - q[x]) * rsig; } } Tri(1, n, aa, ref bb, cc, dd, ref ee, ref dy); //update unknowns Matrix<double> smM = Matrix<double>.Build.DenseOfArray(sm); qsexsM = Matrix<double>.Build.DenseOfArray(qsexs); qsexsdM = Matrix<double>.Build.DenseOfArray(qsexsd); sdrn[j] = sdrn[j] + (q[n] + sig * qya[n] * dy[n]) * dt; smM.SetRow(j, smM.Row(j) + Vector<double>.Build.DenseOfArray(dy.Slice(1, n))); sm = smM.ToArray(); if (sex.GetLength(0) > 1) // need to test, this will need to be transposed. { Matrix<double> sexM = Matrix<double>.Build.Dense(sex.GetLength(0), sex.GetLength(1)); for (int x = 0; x < sex.GetLength(0); x++) for (int y = 0; y < sex.GetLength(1); y++) sexM[x, y] = sex[x, y, j]; Vector<double> dysub = Vector<double>.Build.DenseOfArray(dy.Slice(1, n)); for (i = 1; i <= nex; i++) sexM.SetRow(i, sexM.Row(i) + (qsexsM.Row(i) + sig * qsexsdM.Row(i) * Vector<double>.Build.DenseOfArray(csm) * dysub) * dt); for (int x = 0; x < sex.GetLength(0); x++) for (int y = 0; y < sex.GetLength(1); y++) sex[j, y, x] = sexM[y, x]; } nssteps[j] = nssteps[j] + 1; } } }
/// <summary> /// Solves the equation of continuity from time ts to tfin. /// </summary> /// <param name="sol">solute properties.</param> /// <param name="sdata">soil data.</param> /// <param name="ts">start time (h).</param> /// <param name="tfin">finish time.</param> /// <param name="qprec">precipitation (or water input) rate (fluxes are in cm/h).</param> /// <param name="qevap">potl evaporation rate from soil surface.</param> /// <param name="nsol">no. of solutes.</param> /// <param name="nex">no. of water extraction streams.</param> /// <param name="h0">surface head, equal to depth of surface pond.</param> /// <param name="S">degree of saturation ("effective satn") of layers.</param> /// <param name="evap">cumulative evaporation from soil surface (cm, not initialised).</param> /// <param name="runoff">cumulative runoff.</param> /// <param name="infil">cumulative net infiltration (time integral of flux across surface).</param> /// <param name="drn">cumulative net drainage (time integral of flux across bottom).</param> /// <param name="nsteps">cumulative no. of time steps for RE soln.</param> /// <param name="jt">layer soil type numbers for solute.</param> /// <param name="cin">solute concns in water input (user's units/cc).</param> /// <param name="c0">solute concns in surface pond.</param> /// <param name="sm">solute (mass) concns in layers.</param> /// <param name="soff">cumulative solute runoff (user's units).</param> /// <param name="sinfil">cumulative solute infiltration.</param> /// <param name="sdrn">cumulative solute drainage.</param> /// <param name="nssteps">cumulative no. of time steps for ADE soln.</param> /// <param name="wex">cumulative water extractions from layers.</param> /// <param name="sex">cumulative solute extractions from layers.</param> public static void Solve(SolProps sol, SoilData sdata, double ts, double tfin, double qprec, double qevap, int nsol, int nex, ref double h0, ref double[] S, ref double evap, ref double runoff, ref double infil, ref double drn, ref int nsteps, int[] jt, double[] cin, ref double[] c0, ref double[,] sm, ref double[] soff, ref double[] sinfil, ref double[] sdrn, ref int[] nssteps, ref double[,] wex, ref double[,,] sex) { // Since S.Length is one greater in Fortran to account for the 0 based index, this line is included // so that the +1 nomenclature can be kept below to avoid (further) confusion. int sLength = S.Length - 1; bool again, extraction, initpond, maxpond; int i, iflux, ih0, iok, isatbot, itmp, ns, nsat, nsatlast, nsteps0; int[] isat = new int[sLength + 1]; double accel, dmax, dt, dwinfil, dwoff, fac, infili, qpme, qprec1, rsig, rsigdt, sig, t, ti, win, xtblbot; double[] dSdt = new double[sLength + 1]; double[] h = new double[sLength + 1]; double[] xtbl = new double[sLength + 1]; double[] thi = new double[sLength + 1]; double[] thf = new double[sLength + 1]; double[] qwex = new double[sLength + 1]; double[] qwexd = new double[sLength + 1]; double[,] dwexs = new double[sLength + 1, nex + 1]; double[,] qwexs = new double[sLength + 1, nex + 1]; double[,] qwexsd = new double[sLength + 1, nex + 1]; double[] aa = new double[sLength+1]; // these are 0 based double[] bb = new double[sLength+1]; double[] cc = new double[sLength + 1]; double[] dd = new double[sLength + 1]; double[] dy = new double[sLength + 1]; double[] ee = new double[sLength + 1]; double[] q = new double[sLength + 1]; double[] qya = new double[sLength + 1]; double[] qyb = new double[sLength + 1]; double[] cav = new double[nsol + 1]; double[] sinfili = new double[nsol + 1]; double[,] c; sd = sdata; c = new double[nsol + 1, sd.n + 1]; ISink sink = new SinkDripperDrain(); // Sink type can be changed here. /* ! The saturation status of a layer is stored as 0 or 1 in isat since S may be ! >1 (because of previous overshoot) when a layer desaturates. Fluxes at the ! beginning of a time step and their partial derivs wrt S or h of upper and ! lower layers or boundaries are stored in q, qya and qyb. */ isatbot = 0; xtblbot = 0; dt = 0.0; fac = 0.0; initpond = false; extraction = false; dwoff = 0.0; ti = 0.0; infili = 0.0; if (nex > 0) extraction = true; if (sLength != sd.n) { Console.WriteLine("solve: Size of S differs from table data."); Environment.Exit(1); } //-----set up for boundary conditions if (botbc == "constant head") // !h at bottom bdry specified if (hbot < sd.he[sd.n]) { isatbot = 0; xtblbot = Sbot; } else { isatbot = 1; xtblbot = hbot - sd.he[sd.n]; } //-----end set up for boundary conditions //-----initialise t = ts; nsteps0 = nsteps; nsat = 0; //initialise saturated regions for (int x = 1; x < sLength; x++) if (S[x] >= 1.0) { isat[x] = 1; h[x] = sd.he[x]; } else { isat[x] = 0; h[x] = sd.he[x] - 1.0; } if (nsol > 0) { //set solute info thi = MathUtilities.Multiply(sd.ths, S); //initial th dwexs.Populate2D(0); //initial water extracted from layers ti = t; infili = infil; sinfili = sinfil; double c0Temp = c0[1]; if (h0 > 0 && cin.Any(x => x != c0Temp)) // count(c0 /= cin) > 0) initpond = true; //initial pond with different solute concn else initpond = false; c.Populate2D(0); //temp storage for soln concns } //-----end initialise //-----solve until tfin while (t < tfin) { //-----take next time step for (iflux = 1; iflux <= 2; iflux++) //sometimes need twice to adjust h at satn { nsatlast = nsat; // for detecting onset of profile saturation nsat = isat.Sum(); sig = 0.5; if (nsat != 0) sig = 1.0; //time weighting sigma rsig = 1.0 / sig; //-----get fluxes and derivs // get table entries for (int x = 1; x < isat.Length; x++) if (isat[x] == 0) xtbl[x] = S[x]; else xtbl[x] = h[x] - sd.he[x]; //get surface flux qpme = qprec - qevap; //input rate at saturation qprec1 = qprec; //may change qprec1 to maintain pond if required if (h[1] <= 0 && h0 <= 0 && nsat < sd.n) //no ponding { ns = 1; //start index for eqns sd.GetQ(0, new [] {0, 0, isat[1] }, new [] {0, 0, xtbl[1] }, out q[0], out qya[0], out qyb[0]); if (q[0] < qpme) { q[0] = qpme; qyb[0] = 0; } maxpond = false; } else //ponding { ns = 0; sd.GetQ(0, new [] {0, 1, isat[1] }, new [] {0, h0 - sd.he[1], xtbl[1] }, out q[0], out qya[0], out qyb[0]); if (h0 >= h0max && qpme > q[0]) { maxpond = true; ns = 1; } else maxpond = false; } //get profile fluxes for (i = 1; i <= sd.n - 1; i++) sd.GetQ(i, new [] {0, isat[i], isat[i + 1] }, new [] {0, xtbl[i], xtbl[i + 1] }, out q[i], out qya[i], out qyb[i]); //get bottom flux switch (botbc) { case "constant head": sd.GetQ(sd.n, new [] {0, isat[sd.n], isatbot }, new [] {0, xtbl[sd.n], xtblbot }, out q[sd.n], out qya[sd.n], out qyb[sd.n]); break; case "0.0 flux": q[sd.n] = 0; qya[sd.n] = 0; break; case "free drainage": sd.GetK(sd.n, isat[sd.n], xtbl[sd.n], out q[sd.n], out qya[sd.n]); break; case "seepage": if (h[sd.n] <= -0.5 * sd.dx[sd.n]) { q[sd.n] = 0; qya[sd.n] = 0; } else sd.GetQ(sd.n, new int[] {0, isat[sd.n], 1 }, new double[] {0, xtbl[sd.n], -sd.he[sd.n] }, out q[sd.n], out qya[sd.n], out qyb[sd.n]); break; default: Console.Out.WriteLine("solve: illegal bottom boundary condn"); Environment.Exit(1); break; } if (extraction) //get rate of extraction { sink.Wsinks(t, isat, xtbl, sd.he, ref qwexs, ref qwexsd); for (int x = 1; x < qwexs.GetLength(1); x++) { qwex[x] = Matrix<double>.Build.DenseOfArray(qwexs).Column(x).Sum(); qwexd[x] = Matrix<double>.Build.DenseOfArray(qwexsd).Column(x).Sum(); } } again = false; //flag for recalcn of fluxes //-----end get fluxes and derivs //----estimate time step dt dmax = 0; dSdt = MathUtilities.CreateArrayOfValues(0, dSdt.Length); for (int x = 1; x <= sd.n; x++) { if (isat[x] == 0) dSdt[x] = Math.Abs(q[x] - q[x - 1] + (extraction ? qwex[x] : 0)) / (sd.ths[x] * sd.dx[x]); } dmax = MathUtilities.Max(dSdt); //Max derivative | dS / dt | if (dmax > 0) { dt = dSmax / dmax; // if pond going adjust dt if (h0 > 0 && (q[0] - qpme) * dt > h0) dt = (h0 - 0.5 * h0min) / (q[0] - qpme); } else //steady state flow { if (qpme >= q[sd.n]) //step to finish -but what if extraction varies with time ??? dt = tfin - t; else dt = -(h0 - 0.5 * h0min) / (qpme - q[sd.n]); //pond going so adjust dt } if (dt > dtmax) dt = dtmax; //user's limit // if initial step, improve h where S>= 1 if (nsteps == nsteps0 && nsat > 0 && iflux == 1) { again = true; dt = 1.0e-20 * (tfin - ts); } if (nsat == sd.n && nsatlast < sd.n && iflux == 1) { //profile has just become saturated so adjust h values again = true; dt = 1.0e-20 * (tfin - ts); } if (t + 1.1 * dt > tfin) //step to finish { dt = tfin - t; t = tfin; } else t = t + dt; //tentative update //-----end estimate time step dt //-----get and solve eqns rsigdt = 1.0 / (sig * dt); //aa, bb, cc and dd hold coeffs and rhs of tridiag eqn set for (int x = ns; x <= sd.n - 1; x++) { aa[x + 1] = qya[x]; cc[x] = -qyb[x]; } if (extraction) for (int x = 1; x <= sd.n; x++) dd[x] = -(q[x - 1] - q[x] - qwex[x]) * rsig; else for (int x = 1; x <= sd.n; x++) dd[x] = -(q[x - 1] - q[x]) * rsig; iok = 0; //flag for time step test itmp = 0; //counter to abort if not getting solution while (iok == 0) //keep reducing time step until all ok { itmp = itmp + 1; accel = 1.0 - 0.05 * Math.Min(10, Math.Max(0, itmp - 4)); //acceleration if (itmp > 20) { Console.Out.WriteLine("solve: too many iterations of equation solution"); Environment.Exit(1); } if (ns < 1) { bb[0] = -qya[0] - rsigdt; dd[0] = -(qpme - q[0]) * rsig; } for (int x = 1; x <= sd.n; x++) { bb[x] = qyb[x - 1] - qya[x]; if (isat[x]==0) bb[x] -= sd.ths[x] * sd.dx[x] * rsigdt; if (extraction) bb[x] -= qwexd[x]; } Tri(ns, sd.n, aa, ref bb, cc, dd, ref ee, ref dy); //dy contains dS or, for sat layers, h values iok = 1; if (!again) { //check if time step ok, if not then set fac to make it less iok = 1; for (i = 1; i <= sd.n; i++) { if (isat[i] == 0) //check change in S { if (Math.Abs(dy[i]) > dSfac * dSmax) { fac = Math.Max(0.5, accel * Math.Abs(dSmax / dy[i])); iok = 0; break; } if (-dy[i] > dSmaxr * S[i]) { fac = Math.Max(0.5, accel * dSmaxr * S[i] / (-dSfac * dy[i])); iok = 0; break; } if (S[i] < 1.0 && S[i] + dy[i] > Smax) { fac = accel * (0.5 * (1.0 + Smax) - S[i]) / dy[i]; iok = 0; break; } if (S[i] >= 1.0 && dy[i] > 0.5 * (Smax - 1.0)) { fac = 0.25 * (Smax - 1.0) / dy[i]; iok = 0; break; } } } if (iok == 1 && ns < 1 && h0 < h0max && h0 + dy[0] > h0max + dh0max) { //start of runoff fac = (h0max + 0.5 * dh0max - h0) / dy[0]; iok = 0; } if (iok == 1 && ns < 1 && h0 > 0.0 && h0 + dy[0] < h0min) { //pond going fac = -(h0 - 0.5 * h0min) / dy[0]; iok = 0; } if (iok == 0) //reduce time step { t = t - dt; dt = fac * dt; t = t + dt; rsigdt = 1.0 / (sig * dt); nless = nless + 1; //count step size reductions } if (isat[1] != 0 && iflux == 1 && h[1] < 0.0 && h[1] + dy[1] > 0.0) { //incipient ponding - adjust state of saturated regions t = t - dt; dt = 1.0e-20 * (tfin - ts); rsigdt = 1.0 / (sig * dt); again = true; iok = 0; } } } //end while //-----end get and solve eqns //-----update unknowns ih0 = 0; if (!again) { dwoff = 0.0; if (ns < 1) { h0 = h0 + dy[0]; if (h0 < 0.0 && dy[0] < 0.0) ih0 = 1; //pond g1.0 evap = evap + qevap * dt; //note that fluxes required are q at sigma of time step dwinfil = (q[0] + sig * (qya[0] * dy[0] + qyb[0] * dy[1])) * dt; } else { dwinfil = (q[0] + sig * qyb[0] * dy[1]) * dt; if (maxpond) { evap = evap + qevap * dt; if (qprec > qprecmax) // set input to maintain pond { qpme = q[0] + sig * qyb[0] * dy[1]; qprec1 = qpme + qevap; dwoff = 0.0; } else { dwoff = qpme * dt - dwinfil; } runoff = runoff + dwoff; } else evap = evap + qprec1 * dt - dwinfil; } infil = infil + dwinfil; if (nsol > 0) //get surface solute balance { if (initpond) //pond concn != cin { if (h0 > 0.0) { if (ns == 1) // if max pond depth dy[0] = 0.0; for (int x = 1; x < cav.Length; x++) cav[x] = ((2.0 * h0 - dy[0]) * c0[x] + qprec1 * dt * cin[x]) / (2.0 * h0 + dwoff + dwinfil); for (int x = 1; x < c0.Length; x++) c0[x] = 2.0 * cav[x] - c0[x]; //This needs to be tested from FORTRAN; no example in original code. } else { for (int x = 1; x < cav.Length; x++) cav[x] = ((h0 - dy[0]) * c0[x] + qprec1 * dt * cin[x]) / (dwoff + dwinfil); initpond = false; //pond gone c0 = cin; // for output if any pond at end } soff = MathUtilities.Add(soff, MathUtilities.Multiply_Value(cav, dwoff)); sinfil = MathUtilities.Add(sinfil, MathUtilities.Multiply_Value(cav, dwinfil)); } else { soff = MathUtilities.Add(soff, MathUtilities.Multiply_Value(cav, dwoff)); sinfil = MathUtilities.Add(sinfil, MathUtilities.Multiply_Value(cin, qprec1 * dt - dwoff)); } } // There's a condition based on botbc in the FORTRAN here, but both paths // resolve to the same equation. drn = drn + (q[sd.n] + sig * qya[sd.n] * dy[sd.n]) * dt; if (extraction) { Matrix<double> dwexsM = Matrix<double>.Build.DenseOfArray(dwexs); Matrix<double> qwexsM = Matrix<double>.Build.DenseOfArray(qwexs); Matrix<double> qwexsdM = Matrix<double>.Build.DenseOfArray(qwexsd); Matrix<double> wexM = Matrix<double>.Build.DenseOfArray(wex); Vector<double> wexV; Vector<double> dwexsV; Vector<double> qwexsV; Vector<double> qwexsdV; if (nsol > 0) { //dwexs = dwexs + (qwexs + sig * qwexsd * spread(dy(1:n), 2, nex)) * dt for (i = 1; i <= nex; i++) { dwexsV = dwexsM.Column(i); qwexsV = qwexsM.Column(i); qwexsdV = qwexsdM.Column(i); dwexsV = dwexsV + (qwexsV + sig * qwexsdV * Vector<double>.Build.DenseOfArray(dy.Slice(1, sd.n))) * dt; dwexsM.Column(i, dwexsV); } dwexs = dwexsM.ToArray(); } //wex = wex + (qwexs + sig * qwexsd * spread(dy(1:n), 2, nex)) * dt if (wex.GetLength(0) > 1) // analog for if (present(wex)) { for (i = 1; i <= nex; i++) { qwexsV = qwexsM.Column(i); qwexsdV = qwexsdM.Column(i); wexV = wexM.Column(i); wexV = wexV + (qwexsV + sig * qwexsdV * Vector<double>.Build.DenseOfArray(dy.Slice(1, sd.n))) * dt; wexM.Column(i, wexV); } wex = wexM.ToArray(); } } } for (i = 1; i <= sd.n; i++) { if (isat[i] == 0) { if (!again) { S[i] = S[i] + dy[i]; if (S[i] > 1.0 && dy[i] > 0.0) //saturation of layer { isat[i] = 1; h[i] = sd.he[i]; } } } else { h[i] = h[i] + dy[i]; if (i == 1 && ih0 != 0 && h[i] >= sd.he[i]) h[i] = sd.he[i] - 1.0; //pond gone if (h[i] < sd.he[i]) //desaturation of layer { isat[i] = 0; h[i] = sd.he[i]; } } } //-----end update unknowns if (!again) break; } if (dt <= dtmin) { Console.WriteLine("solve: time step = " + dt); Environment.Exit(1); } //-----end take next time step //remove negative h0 (optional) if (h0 < 0.0 && isat[1] == 0) { infil = infil + h0; S[1] = S[1] + h0 / (sd.ths[1] * sd.dx[1]); h0 = 0.0; } nsteps = nsteps + 1; //solve for solute transport if required if (nwsteps * (nsteps / nwsteps) == nsteps) { // This is function getsolute() in FORTRAN. Inline here as it uses a huge number of vars and is only used once. if (nsol > 0 && t > ti) { thf = MathUtilities.Multiply(sd.ths, S); //final th before call win = infil - infili; //water in at top over time interval cav = MathUtilities.Divide_Value(MathUtilities.Subtract(sinfil, sinfili), win); //average concn in win Solute(ti, t, thi, thf, dwexs, win, cav, sd.n, nsol, nex, sd.dx, jt, dsmmax, ref sm, ref sdrn, ref nssteps, ref c, ref sex, extraction, sol); ti = t; thi = thf; dwexs.Populate2D(0); infili = infil; sinfili = sinfil; // for next interval } } } //-----end solve until tfin //finalise solute transport if required }
public static List<object> TestSolute(double ti, double tf, double[] thi, double[] thf, double[,] dwexs, double win, double[] cin, int n, int ns, int nex, double[] dx, int[] jt, double dsmmax, double[,] sm, double[] sdrn, int[] nssteps, double[,] c, double[,,] sex, bool extraction, SolProps solProps) { List<object> ret = new List<object>(); Solute(ti, tf, thi, thf, dwexs, win, cin, n, ns, nex, dx, jt, dsmmax, ref sm, ref sdrn, ref nssteps, ref c, ref sex, extraction, solProps); ret.Add(sm); ret.Add(sdrn); ret.Add(nssteps); ret.Add(c); return ret; }
public static List <object> TestSolute(double ti, double tf, double[] thi, double[] thf, double[,] dwexs, double win, double[] cin, int n, int ns, int nex, double[] dx, int[] jt, double dsmmax, double[,] sm, double[] sdrn, int[] nssteps, double[,] c, double[,,] sex, bool extraction, SolProps solProps) { List <object> ret = new List <object>(); Solute(ti, tf, thi, thf, dwexs, win, cin, n, ns, nex, dx, jt, dsmmax, ref sm, ref sdrn, ref nssteps, ref c, ref sex, extraction, solProps); ret.Add(sm); ret.Add(sdrn); ret.Add(nssteps); ret.Add(c); return(ret); }
/// <summary> /// Solves the ADE from time ti to tf. Diffusion of solute ignored - dispersion /// coeff = dispersivity* abs(pore water velocity). /// </summary> /// <param name="ti">start time (h).</param> /// <param name="tf">finish time.</param> /// <param name="thi">initial layer water contents.</param> /// <param name="thf">initial layer water contents.</param> /// <param name="dwexs">water extracted from layers over period ti to tf.</param> /// <param name="win">water in at top of profile.</param> /// <param name="cin">solute concn in win.</param> /// <param name="n">no. of soil layers.</param> /// <param name="ns">no. of solutes.</param> /// <param name="nex">no. of water extraction streams.</param> /// <param name="dx">layer thicknesses.</param> /// <param name="jt">layer soil type numbers for solute.</param> /// <param name="dsmmax">max change in sm of any layer to aim for each time step; controls time step size.</param> /// <param name="sm">layer masses of solute per cc.</param> /// <param name="sdrn">cumulative solute drainage.</param> /// <param name="nssteps">cumulative no. of time steps for ADE soln.</param> /// <param name="c"></param> /// <param name="sex">cumulative solute extractions in water extraction streams.</param> /// <param name="extraction">bool indicating if solute extraction is enabled.</param> /// <param name="solProps">Solute properties.</param> private static void Solute(double ti, double tf, double[] thi, double[] thf, double[,] dwexs, double win, double[] cin, int n, int ns, int nex, double[] dx, int[] jt, double dsmmax, ref double[,] sm, ref double[] sdrn, ref int[] nssteps, ref double[,] c, ref double[,,] sex, bool extraction, SolProps solProps) { int itmax = 20; //max iterations for finding c from sm double eps = 0.00001; // for stopping int i, it, j, k; double dc, dm, dmax, dt = 0, f = 0, fc = 0, r, rsig, rsigdt, sig, sigdt, t, tfin, th, v1, v2; double[] dz = new double[n - 1 + 1]; double[] coef1 = new double[n - 1 + 1]; double[] coef2 = new double[n - 1 + 1]; double[] csm = new double[n + 1]; double[] tht = new double[n + 1]; double[] dwex = new double[n + 1]; double[] qsex = new double[n + 1]; double[] qsexd = new double[n + 1]; double[,] qsexs = new double[nex + 1, n + 1]; double[,] qsexsd = new double[nex + 1, n + 1]; double[] aa = new double[n + 1]; // these are 0 based double[] bb = new double[n + 1]; double[] cc = new double[n + 1]; double[] dd = new double[n + 1]; double[] dy = new double[n + 1]; double[] ee = new double[n + 1]; double[] q = new double[n + 1]; double[] qw = new double[n + 1]; double[] qya = new double[n + 1]; double[] qyb = new double[n + 1]; sig = 0.5; rsig = 1.0 / sig; tfin = tf; for (int x = 2; x <= n; x++) { dz[x - 1] = 0.5 * (dx[x - 1] + dx[x]); } //get average water fluxes //dwex = sum(dwexs, 2) !total changes in sink water extraction since last call if (dwexs.GetLength(0) > 0 && dwexs.GetLength(1) > 0) { Matrix <double> dwexsM = Matrix <double> .Build.DenseOfArray(dwexs); for (int x = 0; x < dwexs.GetLength(1); x++) { dwex[x] = MathUtilities.Sum(dwexsM.Row(x)); } } r = 1.0 / (tf - ti); qw[0] = r * win; // Compounding errors due to float/double issues start here. May or may not be a problem. tht = MathUtilities.Multiply_Value(MathUtilities.Subtract(thf, thi), r); for (i = 1; i <= n; i++) { qw[i] = qw[i - 1] - dx[i] * tht[i] - r * dwex[i]; } //get constant coefficients for (i = 1; i <= n - 1; i++) { v1 = 0.5 * qw[i]; v2 = 0.5 * (solProps.dis[jt[i]] + solProps.dis[jt[i + 1]]) * Math.Abs(qw[i]) / dz[i]; coef1[i] = v1 + v2; coef2[i] = v1 - v2; } for (j = 1; j <= ns; j++) { t = ti; if (qw[0] > 0.0) { q[0] = qw[0] * cin[j]; } else { q[0] = 0.0; } qyb[0] = 0.0; while (t < tfin) //get fluxes { for (i = 1; i <= n; i++) { //get c and csm = dc / dsm(with theta constant) k = jt[i]; th = thi[i] + (t - ti) * tht[i]; if (solProps.isotype[j, k] == "no" || sm[j, i] < 0.0) //handle sm < 0 here { csm[i] = 1.0 / th; c[j, i] = csm[i] * sm[j, i]; } else if (solProps.isotype[j, k] == "li") { csm[i] = 1.0 / (th + solProps.bd[k] * solProps.isopar[k, j].ElementAt(1)); c[j, i] = csm[i] * sm[j, i]; } else { for (it = 1; it <= itmax; it++) //get c from sm using Newton's method and bisection { if (c[j, i] < 0.0) { c[j, i] = 0.0; //c and sm are >= 0 } solProps.Isosub(solProps.isotype[j, k], c[j, i], dsmmax, ref solProps.isopar[j, k], out f, out fc); csm[i] = 1.0 / (th + solProps.bd[k] * fc); dm = sm[j, i] - (solProps.bd[k] * f + th * c[j, i]); dc = dm * csm[i]; if (sm[j, i] >= 0.0 && c[j, i] + dc < 0.0) { c[j, i] = 0.5 * c[j, i]; } else { c[j, i] = c[j, i] + dc; } if (Math.Abs(dm) < eps * (sm[j, i] + 10.0 * dsmmax)) { break; } if (it == itmax) { Console.WriteLine("solute: too many iterations getting c"); Environment.Exit(1); } } } } for (int x = 1; x <= n - 1; x++) { q[x] = coef1[x] * c[j, x] + coef2[x] * c[j, x + 1]; qya[x] = coef1[x] * csm[x]; qyb[x] = coef2[x] * csm[x + 1]; } q[n] = qw[n] * c[j, n]; qya[n] = qw[n] * csm[n]; //get time step double[] absQ = new double[n + 1]; for (int x = 1; x <= n; x++) { absQ[x] = Math.Abs(q[x] - q[x - 1] / dx[x]); } dmax = MathUtilities.Max(absQ); if (dmax == 0.0) { dt = tfin - t; } else if (dmax < 0.0) { Console.WriteLine("solute: errors in fluxes prevent continuation"); Environment.Exit(1); } else { dt = dsmmax / dmax; } if (t + 1.1 * dt > tfin) { dt = tfin - t; t = tfin; } else { t = t + dt; } sigdt = sig * dt; rsigdt = 1.0 / sigdt; //adjust q for change in theta for (int x = 1; x <= n - 1; x++) { q[x] = q[x] - sigdt * (qya[x] * tht[x] * c[j, x] + qyb[x] * tht[x + 1] * c[j, x]); } q[n] = q[n] - sigdt * qya[n] * tht[n] * c[j, n]; //get and solve eqns for (int x = 2; x <= n; x++) { aa[x] = qya[x - 1]; } for (int x = 1; x <= n - 1; x++) { cc[x] = -qyb[x]; } Matrix <double> qsexsM = Matrix <double> .Build.DenseOfArray(qsexs); Matrix <double> qsexsdM = Matrix <double> .Build.DenseOfArray(qsexsd); if (sex.GetLength(0) > 1) //get extraction { double[] ctemp = new double[n + 1]; for (int x = 1; x <= n; x++) { ctemp[x] = c[j, x]; } qsexs = qsexsM.ToArray(); qsexsd = qsexsdM.ToArray(); sink.Ssinks(t, ti, tf, j, dwexs, ctemp, ref qsexs, ref qsexsd); qsex = qsexsM.ColumnSums().ToArray(); qsexd = qsexsdM.ColumnSums().ToArray(); for (int x = 1; x <= n; x++) { bb[x] = qyb[x - 1] - qya[x] - qsexd[x] * csm[x] - dx[x] * rsigdt; dd[x] = -(q[x - 1] - q[x] - qsex[x]) * rsig; } } else { for (int x = 1; x <= n; x++) { bb[x] = qyb[x - 1] - qya[x] - dx[x] * rsigdt; dd[x] = -(q[x - 1] - q[x]) * rsig; } } Tri(1, n, aa, ref bb, cc, dd, ref ee, ref dy); //update unknowns Matrix <double> smM = Matrix <double> .Build.DenseOfArray(sm); qsexsM = Matrix <double> .Build.DenseOfArray(qsexs); qsexsdM = Matrix <double> .Build.DenseOfArray(qsexsd); sdrn[j] = sdrn[j] + (q[n] + sig * qya[n] * dy[n]) * dt; smM.SetRow(j, smM.Row(j) + Vector <double> .Build.DenseOfArray(dy.Slice(1, n))); sm = smM.ToArray(); if (sex.GetLength(0) > 1) // need to test, this will need to be transposed. { Matrix <double> sexM = Matrix <double> .Build.Dense(sex.GetLength(0), sex.GetLength(1)); for (int x = 0; x < sex.GetLength(0); x++) { for (int y = 0; y < sex.GetLength(1); y++) { sexM[x, y] = sex[x, y, j]; } } Vector <double> dysub = Vector <double> .Build.DenseOfArray(dy.Slice(1, n)); for (i = 1; i <= nex; i++) { sexM.SetRow(i, sexM.Row(i) + (qsexsM.Row(i) + sig * qsexsdM.Row(i) * Vector <double> .Build.DenseOfArray(csm) * dysub) * dt); } for (int x = 0; x < sex.GetLength(0); x++) { for (int y = 0; y < sex.GetLength(1); y++) { sex[j, y, x] = sexM[y, x]; } } } nssteps[j] = nssteps[j] + 1; } } }
static double[,] isopar; //2 params (nt,2) //public static void Setup() public static void Main(string[] args) { Soil.SoilProperties = new Dictionary <string, SoilProps>(); Fluxes.FluxTables = new Dictionary <string, FluxTable>(); Program.GenerateFlux(); c0 = new double[ns + 1]; cin = new double[ns + 1]; h = new double[n + 1]; S = new double[n + 1]; isotype = new string[nt + 1]; isopar = new double[nt + 1, 2 + 1]; soff = new double[ns + 1]; sdrn = new double[ns + 1]; sinfil = new double[ns + 1]; SoilData sd = new SoilData(); Flow.sink = new SinkDripperDrain(); //set the type of sink to use jt = new int[n + 1]; nssteps = new int[ns + 1]; sidx = new int[n + 1]; sm = new double[ns + 1, n + 1]; //set a list of soil layers(cm). x = new double[] { 0, 10.0, 20.0, 30.0, 40.0, 60.0, 80.0, 100.0, 120.0, 160.0, 200.0 }; for (int c = 1; c <= n; c++) { sidx[c] = c < 5 ? 103 : 109; //soil ident of layers } //set required soil hydraulic params sd.GetTables(n, sidx, x); SolProps solProps = new SolProps(nt, ns); bd = new double[] { 0, 1.3, 1.3 }; dis = new double[] { 0, 20.0, 20.0 }; //set isotherm type and params for solute 2 here isotype[1] = "Fr"; isotype[2] = "La"; isopar[1, 1] = 1.0; isopar[1, 2] = 1.0; isopar[2, 1] = 0.5; isopar[2, 2] = 0.01; Matrix <double> isoparM = Matrix <double> .Build.DenseOfArray(isopar); for (j = 1; j <= nt; j++) //set params { solProps.Solpar(j, bd[j], dis[j]); //set isotherm type and params solProps.Setiso(j, 2, isotype[j], isoparM.Column(j).ToArray()); } //initialise for run ts = 0.0; //start time //dSmax controls time step.Use 0.05 for a fast but fairly accurate solution. //Use 0.001 to get many steps to test execution time per step. Flow.dSmax = 0.01; //0.01 ensures very good accuracy for (int c = 1; c <= n; c++) { jt[c] = c < 5 ? 1 : 2; //!4 layers of type 1, rest of type2 } h0 = 0.0; //pond depth initially zero h1 = -1000.0; h2 = -400.0; //initial matric heads double Sh = 0; //not used for this call but required as C# does not have 'present' operator sd.Sofh(h1, 1, out S1, out Sh); //solve uses degree of satn sd.Sofh(h2, 5, out S2, out Sh); for (int c = 1; c <= n; c++) { S[c] = c < 5 ? S1 : S2; } wpi = MathUtilities.Sum(MathUtilities.Multiply(MathUtilities.Multiply(sd.ths, S), sd.dx)); //water in profile initially nsteps = 0; //no.of time steps for water soln(cumulative) win = 0.0; //water input(total precip) evap = 0.0; runoff = 0.0; infil = 0.0; drn = 0.0; for (int col = 1; col < sm.GetLength(0); col++) { sm[col, 1] = 1000.0 / sd.dx[1]; } //initial solute concn(mass units per cc of soil) //solute in profile initially spi = new [] { 1000.0, 1000.0 }; Flow.dsmmax = 0.1 * sm[1, 1]; //solute stepsize control param Flow.nwsteps = 10; MathUtilities.Zero(c0); MathUtilities.Zero(cin); //no solute input nssteps.Populate(0); //no.of time steps for solute soln(cumulative) MathUtilities.Zero(soff); MathUtilities.Zero(sinfil); MathUtilities.Zero(sdrn); qprec = 1.0; //precip at 1 cm / h for first 24 h ti = ts; qevap = 0.05; // potential evap rate from soil surface double[,] wex = new double[1, 1]; //unused option params in FORTRAN... must be a better way of doing this double[,,] sex = new double[1, 1, 1]; //timer here in FORTRAN, this basically runs the solution for 100 days for (j = 1; j <= 100; j++) { tf = ti + 24.0; Flow.Solve(solProps, sd, ti, tf, qprec, qevap, ns, Flow.sink.nex, ref h0, ref S, ref evap, ref runoff, ref infil, ref drn, ref nsteps, jt, cin, ref c0, ref sm, ref soff, ref sinfil, ref sdrn, ref nssteps, ref wex, ref sex); win = win + qprec * (tf - ti); if (j == 1) { ti = tf; } qprec = 0.0; } win = win + qprec * (tf - ti); wp = MathUtilities.Sum(MathUtilities.Multiply(MathUtilities.Multiply(sd.ths, S), sd.dx)); //!water in profile double hS = 0; //hS is not used used here, but is a required parameter for (j = 1; j <= n; j++) { sd.hofS(S[j], j, out h[j], out hS); } }
/// <summary> /// Solves the equation of continuity from time ts to tfin. /// </summary> /// <param name="sol">solute properties.</param> /// <param name="sdata">soil data.</param> /// <param name="ts">start time (h).</param> /// <param name="tfin">finish time.</param> /// <param name="qprec">precipitation (or water input) rate (fluxes are in cm/h).</param> /// <param name="qevap">potl evaporation rate from soil surface.</param> /// <param name="nsol">no. of solutes.</param> /// <param name="nex">no. of water extraction streams.</param> /// <param name="h0">surface head, equal to depth of surface pond.</param> /// <param name="S">degree of saturation ("effective satn") of layers.</param> /// <param name="evap">cumulative evaporation from soil surface (cm, not initialised).</param> /// <param name="runoff">cumulative runoff.</param> /// <param name="infil">cumulative net infiltration (time integral of flux across surface).</param> /// <param name="drn">cumulative net drainage (time integral of flux across bottom).</param> /// <param name="nsteps">cumulative no. of time steps for RE soln.</param> /// <param name="jt">layer soil type numbers for solute.</param> /// <param name="cin">solute concns in water input (user's units/cc).</param> /// <param name="c0">solute concns in surface pond.</param> /// <param name="sm">solute (mass) concns in layers.</param> /// <param name="soff">cumulative solute runoff (user's units).</param> /// <param name="sinfil">cumulative solute infiltration.</param> /// <param name="sdrn">cumulative solute drainage.</param> /// <param name="nssteps">cumulative no. of time steps for ADE soln.</param> /// <param name="wex">cumulative water extractions from layers.</param> /// <param name="sex">cumulative solute extractions from layers.</param> public static void Solve(SolProps sol, SoilData sdata, double ts, double tfin, double qprec, double qevap, int nsol, int nex, ref double h0, ref double[] S, ref double evap, ref double runoff, ref double infil, ref double drn, ref int nsteps, int[] jt, double[] cin, ref double[] c0, ref double[,] sm, ref double[] soff, ref double[] sinfil, ref double[] sdrn, ref int[] nssteps, ref double[,] wex, ref double[,,] sex) { // Since S.Length is one greater in Fortran to account for the 0 based index, this line is included // so that the +1 nomenclature can be kept below to avoid (further) confusion. int sLength = S.Length - 1; bool again, extraction, initpond, maxpond; int i, iflux, ih0, iok, isatbot, itmp, ns, nsat, nsatlast, nsteps0; int[] isat = new int[sLength + 1]; double accel, dmax, dt, dwinfil, dwoff, fac, infili, qpme, qprec1, rsig, rsigdt, sig, t, ti, win, xtblbot; double[] dSdt = new double[sLength + 1]; double[] h = new double[sLength + 1]; double[] xtbl = new double[sLength + 1]; double[] thi = new double[sLength + 1]; double[] thf = new double[sLength + 1]; double[] qwex = new double[sLength + 1]; double[] qwexd = new double[sLength + 1]; double[,] dwexs = new double[sLength + 1, nex + 1]; double[,] qwexs = new double[sLength + 1, nex + 1]; double[,] qwexsd = new double[sLength + 1, nex + 1]; double[] aa = new double[sLength + 1]; // these are 0 based double[] bb = new double[sLength + 1]; double[] cc = new double[sLength + 1]; double[] dd = new double[sLength + 1]; double[] dy = new double[sLength + 1]; double[] ee = new double[sLength + 1]; double[] q = new double[sLength + 1]; double[] qya = new double[sLength + 1]; double[] qyb = new double[sLength + 1]; double[] cav = new double[nsol + 1]; double[] sinfili = new double[nsol + 1]; double[,] c; sd = sdata; c = new double[nsol + 1, sd.n + 1]; ISink sink = new SinkDripperDrain(); // Sink type can be changed here. /* * ! The saturation status of a layer is stored as 0 or 1 in isat since S may be * ! >1 (because of previous overshoot) when a layer desaturates. Fluxes at the * ! beginning of a time step and their partial derivs wrt S or h of upper and * ! lower layers or boundaries are stored in q, qya and qyb. */ isatbot = 0; xtblbot = 0; dt = 0.0; fac = 0.0; initpond = false; extraction = false; dwoff = 0.0; ti = 0.0; infili = 0.0; if (nex > 0) { extraction = true; } if (sLength != sd.n) { Console.WriteLine("solve: Size of S differs from table data."); Environment.Exit(1); } //-----set up for boundary conditions if (botbc == "constant head") // !h at bottom bdry specified { if (hbot < sd.he[sd.n]) { isatbot = 0; xtblbot = Sbot; } else { isatbot = 1; xtblbot = hbot - sd.he[sd.n]; } } //-----end set up for boundary conditions //-----initialise t = ts; nsteps0 = nsteps; nsat = 0; //initialise saturated regions for (int x = 1; x < sLength; x++) { if (S[x] >= 1.0) { isat[x] = 1; h[x] = sd.he[x]; } else { isat[x] = 0; h[x] = sd.he[x] - 1.0; } } if (nsol > 0) { //set solute info thi = MathUtilities.Multiply(sd.ths, S); //initial th dwexs.Populate2D(0); //initial water extracted from layers ti = t; infili = infil; sinfili = sinfil; double c0Temp = c0[1]; if (h0 > 0 && cin.Any(x => x != c0Temp)) // count(c0 /= cin) > 0) { initpond = true; //initial pond with different solute concn } else { initpond = false; } c.Populate2D(0); //temp storage for soln concns } //-----end initialise //-----solve until tfin while (t < tfin) { //-----take next time step for (iflux = 1; iflux <= 2; iflux++) //sometimes need twice to adjust h at satn { nsatlast = nsat; // for detecting onset of profile saturation nsat = isat.Sum(); sig = 0.5; if (nsat != 0) { sig = 1.0; //time weighting sigma } rsig = 1.0 / sig; //-----get fluxes and derivs // get table entries for (int x = 1; x < isat.Length; x++) { if (isat[x] == 0) { xtbl[x] = S[x]; } else { xtbl[x] = h[x] - sd.he[x]; } } //get surface flux qpme = qprec - qevap; //input rate at saturation qprec1 = qprec; //may change qprec1 to maintain pond if required if (h[1] <= 0 && h0 <= 0 && nsat < sd.n) //no ponding { ns = 1; //start index for eqns sd.GetQ(0, new [] { 0, 0, isat[1] }, new [] { 0, 0, xtbl[1] }, out q[0], out qya[0], out qyb[0]); if (q[0] < qpme) { q[0] = qpme; qyb[0] = 0; } maxpond = false; } else //ponding { ns = 0; sd.GetQ(0, new [] { 0, 1, isat[1] }, new [] { 0, h0 - sd.he[1], xtbl[1] }, out q[0], out qya[0], out qyb[0]); if (h0 >= h0max && qpme > q[0]) { maxpond = true; ns = 1; } else { maxpond = false; } } //get profile fluxes for (i = 1; i <= sd.n - 1; i++) { sd.GetQ(i, new [] { 0, isat[i], isat[i + 1] }, new [] { 0, xtbl[i], xtbl[i + 1] }, out q[i], out qya[i], out qyb[i]); } //get bottom flux switch (botbc) { case "constant head": sd.GetQ(sd.n, new [] { 0, isat[sd.n], isatbot }, new [] { 0, xtbl[sd.n], xtblbot }, out q[sd.n], out qya[sd.n], out qyb[sd.n]); break; case "0.0 flux": q[sd.n] = 0; qya[sd.n] = 0; break; case "free drainage": sd.GetK(sd.n, isat[sd.n], xtbl[sd.n], out q[sd.n], out qya[sd.n]); break; case "seepage": if (h[sd.n] <= -0.5 * sd.dx[sd.n]) { q[sd.n] = 0; qya[sd.n] = 0; } else { sd.GetQ(sd.n, new int[] { 0, isat[sd.n], 1 }, new double[] { 0, xtbl[sd.n], -sd.he[sd.n] }, out q[sd.n], out qya[sd.n], out qyb[sd.n]); } break; default: Console.Out.WriteLine("solve: illegal bottom boundary condn"); Environment.Exit(1); break; } if (extraction) //get rate of extraction { sink.Wsinks(t, isat, xtbl, sd.he, ref qwexs, ref qwexsd); for (int x = 1; x < qwexs.GetLength(1); x++) { qwex[x] = Matrix <double> .Build.DenseOfArray(qwexs).Column(x).Sum(); qwexd[x] = Matrix <double> .Build.DenseOfArray(qwexsd).Column(x).Sum(); } } again = false; //flag for recalcn of fluxes //-----end get fluxes and derivs //----estimate time step dt dmax = 0; dSdt = MathUtilities.CreateArrayOfValues(0, dSdt.Length); for (int x = 1; x <= sd.n; x++) { if (isat[x] == 0) { dSdt[x] = Math.Abs(q[x] - q[x - 1] + (extraction ? qwex[x] : 0)) / (sd.ths[x] * sd.dx[x]); } } dmax = MathUtilities.Max(dSdt); //Max derivative | dS / dt | if (dmax > 0) { dt = dSmax / dmax; // if pond going adjust dt if (h0 > 0 && (q[0] - qpme) * dt > h0) { dt = (h0 - 0.5 * h0min) / (q[0] - qpme); } } else //steady state flow { if (qpme >= q[sd.n]) //step to finish -but what if extraction varies with time ??? { dt = tfin - t; } else { dt = -(h0 - 0.5 * h0min) / (qpme - q[sd.n]); //pond going so adjust dt } } if (dt > dtmax) { dt = dtmax; //user's limit } // if initial step, improve h where S>= 1 if (nsteps == nsteps0 && nsat > 0 && iflux == 1) { again = true; dt = 1.0e-20 * (tfin - ts); } if (nsat == sd.n && nsatlast < sd.n && iflux == 1) { //profile has just become saturated so adjust h values again = true; dt = 1.0e-20 * (tfin - ts); } if (t + 1.1 * dt > tfin) //step to finish { dt = tfin - t; t = tfin; } else { t = t + dt; //tentative update } //-----end estimate time step dt //-----get and solve eqns rsigdt = 1.0 / (sig * dt); //aa, bb, cc and dd hold coeffs and rhs of tridiag eqn set for (int x = ns; x <= sd.n - 1; x++) { aa[x + 1] = qya[x]; cc[x] = -qyb[x]; } if (extraction) { for (int x = 1; x <= sd.n; x++) { dd[x] = -(q[x - 1] - q[x] - qwex[x]) * rsig; } } else { for (int x = 1; x <= sd.n; x++) { dd[x] = -(q[x - 1] - q[x]) * rsig; } } iok = 0; //flag for time step test itmp = 0; //counter to abort if not getting solution while (iok == 0) //keep reducing time step until all ok { itmp = itmp + 1; accel = 1.0 - 0.05 * Math.Min(10, Math.Max(0, itmp - 4)); //acceleration if (itmp > 20) { Console.Out.WriteLine("solve: too many iterations of equation solution"); Environment.Exit(1); } if (ns < 1) { bb[0] = -qya[0] - rsigdt; dd[0] = -(qpme - q[0]) * rsig; } for (int x = 1; x <= sd.n; x++) { bb[x] = qyb[x - 1] - qya[x]; if (isat[x] == 0) { bb[x] -= sd.ths[x] * sd.dx[x] * rsigdt; } if (extraction) { bb[x] -= qwexd[x]; } } Tri(ns, sd.n, aa, ref bb, cc, dd, ref ee, ref dy); //dy contains dS or, for sat layers, h values iok = 1; if (!again) { //check if time step ok, if not then set fac to make it less iok = 1; for (i = 1; i <= sd.n; i++) { if (isat[i] == 0) //check change in S { if (Math.Abs(dy[i]) > dSfac * dSmax) { fac = Math.Max(0.5, accel * Math.Abs(dSmax / dy[i])); iok = 0; break; } if (-dy[i] > dSmaxr * S[i]) { fac = Math.Max(0.5, accel * dSmaxr * S[i] / (-dSfac * dy[i])); iok = 0; break; } if (S[i] < 1.0 && S[i] + dy[i] > Smax) { fac = accel * (0.5 * (1.0 + Smax) - S[i]) / dy[i]; iok = 0; break; } if (S[i] >= 1.0 && dy[i] > 0.5 * (Smax - 1.0)) { fac = 0.25 * (Smax - 1.0) / dy[i]; iok = 0; break; } } } if (iok == 1 && ns < 1 && h0 < h0max && h0 + dy[0] > h0max + dh0max) { //start of runoff fac = (h0max + 0.5 * dh0max - h0) / dy[0]; iok = 0; } if (iok == 1 && ns < 1 && h0 > 0.0 && h0 + dy[0] < h0min) { //pond going fac = -(h0 - 0.5 * h0min) / dy[0]; iok = 0; } if (iok == 0) //reduce time step { t = t - dt; dt = fac * dt; t = t + dt; rsigdt = 1.0 / (sig * dt); nless = nless + 1; //count step size reductions } if (isat[1] != 0 && iflux == 1 && h[1] < 0.0 && h[1] + dy[1] > 0.0) { //incipient ponding - adjust state of saturated regions t = t - dt; dt = 1.0e-20 * (tfin - ts); rsigdt = 1.0 / (sig * dt); again = true; iok = 0; } } } //end while //-----end get and solve eqns //-----update unknowns ih0 = 0; if (!again) { dwoff = 0.0; if (ns < 1) { h0 = h0 + dy[0]; if (h0 < 0.0 && dy[0] < 0.0) { ih0 = 1; //pond g1.0 } evap = evap + qevap * dt; //note that fluxes required are q at sigma of time step dwinfil = (q[0] + sig * (qya[0] * dy[0] + qyb[0] * dy[1])) * dt; } else { dwinfil = (q[0] + sig * qyb[0] * dy[1]) * dt; if (maxpond) { evap = evap + qevap * dt; if (qprec > qprecmax) // set input to maintain pond { qpme = q[0] + sig * qyb[0] * dy[1]; qprec1 = qpme + qevap; dwoff = 0.0; } else { dwoff = qpme * dt - dwinfil; } runoff = runoff + dwoff; } else { evap = evap + qprec1 * dt - dwinfil; } } infil = infil + dwinfil; if (nsol > 0) //get surface solute balance { if (initpond) //pond concn != cin { if (h0 > 0.0) { if (ns == 1) // if max pond depth { dy[0] = 0.0; } for (int x = 1; x < cav.Length; x++) { cav[x] = ((2.0 * h0 - dy[0]) * c0[x] + qprec1 * dt * cin[x]) / (2.0 * h0 + dwoff + dwinfil); } for (int x = 1; x < c0.Length; x++) { c0[x] = 2.0 * cav[x] - c0[x]; //This needs to be tested from FORTRAN; no example in original code. } } else { for (int x = 1; x < cav.Length; x++) { cav[x] = ((h0 - dy[0]) * c0[x] + qprec1 * dt * cin[x]) / (dwoff + dwinfil); } initpond = false; //pond gone c0 = cin; // for output if any pond at end } soff = MathUtilities.Add(soff, MathUtilities.Multiply_Value(cav, dwoff)); sinfil = MathUtilities.Add(sinfil, MathUtilities.Multiply_Value(cav, dwinfil)); } else { soff = MathUtilities.Add(soff, MathUtilities.Multiply_Value(cav, dwoff)); sinfil = MathUtilities.Add(sinfil, MathUtilities.Multiply_Value(cin, qprec1 * dt - dwoff)); } } // There's a condition based on botbc in the FORTRAN here, but both paths // resolve to the same equation. drn = drn + (q[sd.n] + sig * qya[sd.n] * dy[sd.n]) * dt; if (extraction) { Matrix <double> dwexsM = Matrix <double> .Build.DenseOfArray(dwexs); Matrix <double> qwexsM = Matrix <double> .Build.DenseOfArray(qwexs); Matrix <double> qwexsdM = Matrix <double> .Build.DenseOfArray(qwexsd); Matrix <double> wexM = Matrix <double> .Build.DenseOfArray(wex); Vector <double> wexV; Vector <double> dwexsV; Vector <double> qwexsV; Vector <double> qwexsdV; if (nsol > 0) { //dwexs = dwexs + (qwexs + sig * qwexsd * spread(dy(1:n), 2, nex)) * dt for (i = 1; i <= nex; i++) { dwexsV = dwexsM.Column(i); qwexsV = qwexsM.Column(i); qwexsdV = qwexsdM.Column(i); dwexsV = dwexsV + (qwexsV + sig * qwexsdV * Vector <double> .Build.DenseOfArray(dy.Slice(1, sd.n))) * dt; dwexsM.Column(i, dwexsV); } dwexs = dwexsM.ToArray(); } //wex = wex + (qwexs + sig * qwexsd * spread(dy(1:n), 2, nex)) * dt if (wex.GetLength(0) > 1) // analog for if (present(wex)) { for (i = 1; i <= nex; i++) { qwexsV = qwexsM.Column(i); qwexsdV = qwexsdM.Column(i); wexV = wexM.Column(i); wexV = wexV + (qwexsV + sig * qwexsdV * Vector <double> .Build.DenseOfArray(dy.Slice(1, sd.n))) * dt; wexM.Column(i, wexV); } wex = wexM.ToArray(); } } } for (i = 1; i <= sd.n; i++) { if (isat[i] == 0) { if (!again) { S[i] = S[i] + dy[i]; if (S[i] > 1.0 && dy[i] > 0.0) //saturation of layer { isat[i] = 1; h[i] = sd.he[i]; } } } else { h[i] = h[i] + dy[i]; if (i == 1 && ih0 != 0 && h[i] >= sd.he[i]) { h[i] = sd.he[i] - 1.0; //pond gone } if (h[i] < sd.he[i]) //desaturation of layer { isat[i] = 0; h[i] = sd.he[i]; } } } //-----end update unknowns if (!again) { break; } } if (dt <= dtmin) { Console.WriteLine("solve: time step = " + dt); Environment.Exit(1); } //-----end take next time step //remove negative h0 (optional) if (h0 < 0.0 && isat[1] == 0) { infil = infil + h0; S[1] = S[1] + h0 / (sd.ths[1] * sd.dx[1]); h0 = 0.0; } nsteps = nsteps + 1; //solve for solute transport if required if (nwsteps * (nsteps / nwsteps) == nsteps) { // This is function getsolute() in FORTRAN. Inline here as it uses a huge number of vars and is only used once. if (nsol > 0 && t > ti) { thf = MathUtilities.Multiply(sd.ths, S); //final th before call win = infil - infili; //water in at top over time interval cav = MathUtilities.Divide_Value(MathUtilities.Subtract(sinfil, sinfili), win); //average concn in win Solute(ti, t, thi, thf, dwexs, win, cav, sd.n, nsol, nex, sd.dx, jt, dsmmax, ref sm, ref sdrn, ref nssteps, ref c, ref sex, extraction, sol); ti = t; thi = thf; dwexs.Populate2D(0); infili = infil; sinfili = sinfil; // for next interval } } } //-----end solve until tfin //finalise solute transport if required }
public void Solve() { SolProps sol = new SolProps(10,2); SoilData sd = new SoilData(); Soil.SoilProperties = new Dictionary<string, SoilProps>(); Fluxes.FluxTables = new Dictionary<string, FluxTable>(); Program.GenerateFlux(); sd.GetTables(10, new [] { 0, 103, 103, 103, 103, 109, 109, 109, 109, 109, 109 }, new double[] { 0, 10, 20, 30, 40, 60, 80, 100, 120, 160, 200 }); sd.dx = new [] {0.0, 10.0, 10.0, 10.0, 10.0, 20.0, 20.0, 20.0, 20.0, 40.0, 40.0 }; sd.n = 10; sd.ths = new [] { 0, 0.400000006, 0.400000006, 0.400000006, 0.400000006, 0.600000024, 0.600000024, 0.600000024, 0.600000024, 0.600000024, 0.600000024 }; sd.he = new [] { 0.0, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2 }; Flow.sd = sd; double ts = 0.0; double tfin=24.0; double qprec=1.0; double qevap=0.05; int nsol=2; int nex=0; double h0=0; double[] S = {0, 0.208238059, 0.208238059, 0.208238059, 0.208238059, 0.698160143, 0.698160143, 0.698160143, 0.698160143, 0.698160143, 0.698160143 }; double evap=0.0; double runoff = 0.0; double infil = 0.0; double drn = 0.0; int nsteps=0; int[] jt= { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2 }; double[] cin = { 0.0, 0.0, 0.0 }; double[] c0 = { 0.0, 0.0, 0.0 }; double[,] sm = new double[3,11]; sm[1, 1] = 100; sm[2, 1] = 100; double[] soff = { 0.0, 0.0, 0.0 }; double[] sinfil = { 0.0, 0.0, 0.0 }; double[] sdrn = { 0.0, 0.0, 0.0 }; int[] nssteps = { 0,0,0 }; double[,] wex = new double[0, 0]; double[,,] sex = new double[0, 0, 0]; Flow.Solve(sol, sd, ts, tfin, qprec, qevap, nsol, nex, ref h0, ref S, ref evap, ref runoff, ref infil, ref drn, ref nsteps, jt, cin, ref c0, ref sm, ref soff, ref sinfil, ref sdrn, ref nssteps, ref wex, ref sex); }
public void Solute() { int nt = 2; int ns = 2; SolProps sp = new SolProps(nt, ns); double[] bd = new double[nt + 1]; double[] dis = new double[nt + 1]; string[] isotype = { "", "Fr", "La" }; double[,] isopar = { { 0, 0, 0 }, { 0, 1.0, 1.0 }, { 0, 0.5, 0.01 } }; Array.Copy(new [] { 0, 1.3, 1.3 }, bd, 3); Array.Copy(new [] { 0, 20.0, 20.0 }, dis, 3); for (int j = 1; j <= nt; j++) { sp.Solpar(j, bd[j], dis[j]); sp.Setiso(j, 2, isotype[j], Extensions.GetRowCol(isopar, j, true)); } int n = 10; int nex = 0; double ti = 0; double tf = 0.421148079; double[] thi = { 0, 8.32952248067425E-02, 8.32952248067425E-02, 8.32952248067425E-02, 8.32952248067425E-02, 4.18896102493218E-01, 4.18896102493218E-01, 4.18896102493218E-01, 4.18896102493218E-01, 4.18896102493218E-01, 4.18896102493218E-01 }; double[] thf = { 0, 1.23293862599989E-01, 8.33055911391525E-02, 8.32952276559487E-02, 8.33189852482353E-02, 4.18882017281567E-01, 4.18896097739775E-01, 4.18896102481653E-01, 4.18896102470398E-01, 4.18896050247009E-01, 4.18896158171224E-01}; double[,] dwexs = new double[nex, n]; double win = 0.400090675; double[] cin = { 0, 0, 0 }; double[] dx = { 0, 10, 10, 10, 10, 20, 20, 20, 20, 40, 40 }; int[] jt = { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2 }; int dsmmax = 10; double[,] sm = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; double[] sdrn = { 0, 0, 0 }; int[] nssteps = { 0, 0, 0 }; double[,] c = new double[ns + 1, n + 1]; double[,,] sex = new double[0, 0, 0]; double[,] smOUT = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 9.9976219E+01, 2.3781371E-02, 2.2604628E-08, 2.0517180E-14, 1.2161602E-18, 4.8670606E-24, 1.9519348E-29, 7.8282825E-35, 1.2209528E-40, 1.7083828E-46}, {0, 9.9983728E+01, 1.6271857E-02, 9.8469378E-11, 5.6908331E-19, 2.1476414E-25, 2.0945763E-31, 2.0471666E-37, 2.0008407E-43, 7.6050509E-50, 2.5932571E-56} }; double[] sdrnOUT = { 0, 9.10749016E-51, 3.36911468E-61 }; int[] nsstepsOUT = { 0, 1, 1 }; double[,] cOUT = { {0, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00}, {0, 1.20054901384821E+03, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00}, {0, 7.68024454834764E+02, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00} }; List<object> res = Flow.TestSolute(ti, tf, thi, thf, dwexs, win, cin, n, ns, nex, dx, jt, dsmmax, sm, sdrn, nssteps, c, sex, false, sp); double[,] smRes = res[0] as double[,]; for (int i = 1; i < smOUT.GetLength(0); i++) for (int j = 1; j < smOUT.GetLength(1); j++) Assert.AreEqual(smOUT[i, j], smRes[i, j], Math.Abs(smOUT[i, 1] * 1E-5)); //variations in sm at E-40... I don't think we really care at that point. Also probably outside of float range double[] sdrnRes = res[1] as double[]; for (int i = 1; i < sdrnOUT.Length; i++) Assert.AreEqual(sdrnOUT[i], sdrnRes[i], 1E-10); //same as above. We're working with -60 exponents here. No bearing to reality. int[] nsstepsRes = res[2] as int[]; for (int i = 1; i < nsstepsOUT.Length; i++) Assert.AreEqual(nsstepsOUT[i], nsstepsRes[i]); double[,] cRes = res[3] as double[,]; for (int i = 1; i < cOUT.GetLength(0); i++) for (int j = 1; j < cOUT.GetLength(1); j++) Assert.AreEqual(cOUT[i, j], cRes[i, j], Math.Abs(cOUT[i,j] * 1E-7)); }
public void SetIso() { SolProps sp = new SolProps(2, 10); int[] j = { 1, 2 }; int[] isol = { 2, 2 }; string[] isotypeji = { "Fr", "La" }; double[][] isoparji = { new [] { 0, 1, 0.5 }, new [] { 0, 1, 0.01 } }; double[][] isopar = { new [] { 0, 1, 0.5, 0, 0 }, new [] { 0, 1, 0.01 } }; for (int i = 0; i < j.Length; i++) { sp.Setiso(j[i], isol[i], isotypeji[i], isoparji[i]); for (int count = 1; count < isopar[i].Length; count++) { Assert.AreEqual(isopar[j[i] - 1][count], sp.isopar[isol[i], j[i]][count], Math.Abs(isopar[j[i] - 1][count] * 1E-5)); } } }
public void Isosub() { SolProps sp = new SolProps(2, 10); string iso = "Fr"; double c = 0; double dsmmax = 10; double[] p = { 0, 1, 0.5, 0, 0 }; double[] pOut = { 0, 1, 0.5, 0.01, 10 }; double f; double fd; double fOut = 0; double fdOut = 10; sp.Isosub(iso, c, dsmmax, ref p, out f, out fd); for (int i = 0; i < p.Length; i++) Assert.AreEqual(pOut[i], p[i], Math.Abs(pOut[i] * 1E-5)); Assert.AreEqual(fOut, f, Math.Abs(fOut * 1E-5)); Assert.AreEqual(fdOut, fd, Math.Abs(fdOut * 1E-5)); }
static double[,] sm; //(n, ns) #endregion Fields #region Methods //public static void Setup() public static void Main(string[] args) { Soil.SoilProperties = new Dictionary<string, SoilProps>(); Fluxes.FluxTables = new Dictionary<string, FluxTable>(); Program.GenerateFlux(); c0 = new double[ns + 1]; cin = new double[ns + 1]; h = new double[n + 1]; S = new double[n + 1]; isotype = new string[nt + 1]; isopar = new double[nt + 1, 2 + 1]; soff = new double[ns + 1]; sdrn = new double[ns + 1]; sinfil = new double[ns + 1]; SoilData sd = new SoilData(); Flow.sink = new SinkDripperDrain(); //set the type of sink to use jt = new int[n+1]; nssteps = new int[ns + 1]; sidx = new int[n+1]; sm = new double[ns + 1, n + 1]; //set a list of soil layers(cm). x = new double[] { 0, 10.0, 20.0, 30.0, 40.0, 60.0, 80.0, 100.0, 120.0, 160.0, 200.0 }; for (int c = 1; c <= n; c++) sidx[c] = c < 5 ? 103 : 109; //soil ident of layers //set required soil hydraulic params sd.GetTables(n, sidx, x); SolProps solProps = new SolProps(nt, ns); bd = new double[] {0, 1.3, 1.3 }; dis = new double[] {0, 20.0, 20.0 }; //set isotherm type and params for solute 2 here isotype[1] = "Fr"; isotype[2] = "La"; isopar[1, 1] = 1.0; isopar[1, 2] = 1.0; isopar[2, 1] = 0.5; isopar[2, 2] = 0.01; Matrix<double> isoparM = Matrix<double>.Build.DenseOfArray(isopar); for (j = 1; j <= nt; j++) //set params { solProps.Solpar(j, bd[j], dis[j]); //set isotherm type and params solProps.Setiso(j, 2, isotype[j], isoparM.Column(j).ToArray()); } //initialise for run ts = 0.0; //start time //dSmax controls time step.Use 0.05 for a fast but fairly accurate solution. //Use 0.001 to get many steps to test execution time per step. Flow.dSmax = 0.01; //0.01 ensures very good accuracy for (int c = 1; c <= n; c++) jt[c] = c < 5 ? 1 : 2; //!4 layers of type 1, rest of type2 h0 = 0.0; //pond depth initially zero h1 = -1000.0; h2 = -400.0; //initial matric heads double Sh = 0; //not used for this call but required as C# does not have 'present' operator sd.Sofh(h1, 1, out S1, out Sh); //solve uses degree of satn sd.Sofh(h2, 5, out S2, out Sh); for (int c = 1; c <= n; c++) S[c] = c < 5 ? S1 : S2; wpi = MathUtilities.Sum(MathUtilities.Multiply(MathUtilities.Multiply(sd.ths, S), sd.dx)); //water in profile initially nsteps = 0; //no.of time steps for water soln(cumulative) win = 0.0; //water input(total precip) evap = 0.0; runoff = 0.0; infil = 0.0; drn = 0.0; for (int col = 1; col < sm.GetLength(0); col++) sm[col, 1] = 1000.0 / sd.dx[1]; //initial solute concn(mass units per cc of soil) //solute in profile initially spi = new []{1000.0, 1000.0}; Flow.dsmmax = 0.1 * sm[1, 1]; //solute stepsize control param Flow.nwsteps = 10; MathUtilities.Zero(c0); MathUtilities.Zero(cin); //no solute input nssteps.Populate(0); //no.of time steps for solute soln(cumulative) MathUtilities.Zero(soff); MathUtilities.Zero(sinfil); MathUtilities.Zero(sdrn); qprec = 1.0; //precip at 1 cm / h for first 24 h ti = ts; qevap = 0.05;// potential evap rate from soil surface double[,] wex = new double[1,1]; //unused option params in FORTRAN... must be a better way of doing this double[,,] sex=new double[1,1,1]; //timer here in FORTRAN, this basically runs the solution for 100 days for (j = 1; j <= 100; j++) { tf = ti + 24.0; Flow.Solve(solProps, sd, ti, tf, qprec, qevap, ns, Flow.sink.nex, ref h0, ref S, ref evap, ref runoff, ref infil, ref drn, ref nsteps, jt, cin, ref c0, ref sm, ref soff, ref sinfil, ref sdrn, ref nssteps, ref wex,ref sex); win = win + qprec * (tf - ti); if (j == 1) ti = tf; qprec = 0.0; } win = win + qprec * (tf - ti); wp = MathUtilities.Sum(MathUtilities.Multiply(MathUtilities.Multiply(sd.ths,S), sd.dx)); //!water in profile double hS = 0; //hS is not used used here, but is a required parameter for (j = 1; j <= n; j++) sd.hofS(S[j], j, out h[j], out hS); }