classdef Model < MAxSym.Check % class to build and run axi-symmetric model MAxSym % % 1. create MAxSym.Model object: m = MAxSym.Model; % 2. define stress periods and time steps: m.settime(dt,ndt); % 3. define model grid: m.setgrid(rb,D,confined); % 4. define hydraulic parameters: % - radial conductivity: m.par.kr = ...; % - radial resistance: m.par.cr = ...; % - vertical conductivity: m.par.kz = ...; % - vertical resistance: m.par.cz = ...; % - specific elastic storage coefficient: m.par.ss = ...; % - specific yield: m.par.sy = ...; % 5. define inactive and/or constant head rings if any: % - inactive rings: m.par.inactive = ...; % - constant head rings: m.par.constant = ...; % 6. define stresses for each stress period k: % - discharge: m.stress(k).q = ...; % - initial head change: m.stress(k).s0 = ...; % 7. define solver: m.setsolver(delta,mni,nparm); % 8. run model: m.run; % 9. check flow and volumetric budget terms: % - radial flow terms: m.qr % - vertical flow terms: m.qz % - storage change terms: m.qs % - volumetric budget terms: m.bud % - total volumetric budget: m.totbud % 10. get drawdown matrix: m.s % 11. apply linear interpolation to get drawdown at arbitrary distances and times: % - linear interpolation in dimension of log(r): s = m.interp1r(ilay,r,it,rw); % - linear interpolation in dimension of log(t): s = m.interp1t(ilay,ir,t); % - bilinear interpolation in dimensions of log(r) and log(t): s = m.interp2(ilay,r,t,rw); % % % Copyright (c) 2011, Ghent University (Andy Louwyck), Belgium % All rights reserved. % Redistribution and use in source and binary forms, with or without % modification, are permitted provided that the following conditions are met: % * Redistributions of source code must retain the above copyright % notice, this list of conditions and the following disclaimer. % * Redistributions in binary form must reproduce the above copyright % notice, this list of conditions and the following disclaimer in the % documentation and/or other materials provided with the distribution. % * Neither the name of the Ghent University nor the % names of its contributors may be used to endorse or promote products % derived from this software without specific prior written permission. % % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND % ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED % WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE % DISCLAIMED. IN NO EVENT SHALL GHENT UNIVERSITY BE LIABLE FOR ANY % DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES % (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; % LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND % ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT % (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS % SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. % input properties (SetAccess = protected) time = MAxSym.TimeSteps.empty; % time steps grid = MAxSym.Grid.empty; % model grid par = MAxSym.Parameters.empty; % hydraulic parameters stress = MAxSym.Stresses.empty; % stresses solver = MAxSym.Solver.empty; % solver end % output properties (SetAccess = protected) s % drawdown [L] - nz x nr x nt array, where nt is the number of simulation times niter % number of iterations for each time step - ndt vector, where ndt is the number of time steps end % output properties (Dependent) qr % radial flow [L³/T] - nz x (nr+1) x nt array, where nt is the number of simulation times qz % vertical flow [L³/T] - (nz+1) x nr x nt array, where nt is the number of simulation times, empty if nz = 1 qs % storage change [L³/T] - nz x nr x (nt-1) array, where nt is the number of simulation times, empty if steady state bud % volumetric budget [L³/T] - nz x nr x (nt-1) array, where nt is the number of simulation times totbud % total volumetric budget [L³/T] - (nt-1) x 2 array: column 1 refers to variable head rings, column 2 to constant head rings end % flow constants properties (Access = protected) qrc % radial conductances [L²/T] - nz x nr-1 matrix qzc % vertical conductances [L²/T] - nz-1 x nr matrix - empty if nz = 1 qssc % storage change terms [L²] - nz x nr matrix - empty if steady state qsyc % specific yield terms [L²] - nr vector - empty if steady state or confined hskz % correction terms for vertical flow between top layers [L³/T] - nr vector - empty if nz = 1 or confined end methods function settime(obj,varargin) % define time steps % % if transient simulation: % syntax: obj.settime(dt,ndt) % dt is time step vector [T] % time steps must be infinite and strictly positive % ndt is vector with number of time steps for each stress period % note that length(dt) must be equal to sum(ndt) % ndt is only required if more than 1 stress period % % if steady state simulation: % syntax: obj.settime(0) % % settime creates a TimeSteps object, which is saved in obj.time % if obj.time is already set, its contents is replaced by the new TimeSteps object % if a grid is defined, settime creates a Parameters object, which is saved in obj.par % also, a Stresses object is defined for each stress period, and the resulting Stresses vector is stored in obj.stress % if obj.par is already set, it is reset if the simulation state (steady or unsteady) was modified % if obj.stress is already set, it is reset if the number of stress periods was changed if ~isempty(obj.time) steady = obj.time.steady; else steady = obj.equal(varargin{1}(1),0); end obj.time = MAxSym.TimeSteps(varargin{:}); if ~isempty(obj.grid) if isempty(obj.par) || (steady && ~obj.time.steady) || (~steady && obj.time.steady) obj.setpar; end if isempty(obj.stress) || length(obj.stress) ~= obj.time.nper obj.setstress; end end end function setgrid(obj,varargin) % define model grid % % syntax: obj.setgrid(rb,D,confined) % rb is vector with radii of ring boundaries [L] % rb must be strictly monotonically increasing % rb must contain finite, strictly positive elements % rb must contain two elements at least % D is vector with layer thicknesses [L] % D must contain finite, strictly positive elements % D must contain one element at least % D(1) is top layer thickness, D(end) is bottom layer thickness % confined is logical indicating whether the aquifer system is confined (true) or not (false) % % setgrid creates a Grid object, which is saved in obj.grid % if obj.grid is already set, its contents is replaced by the new Grid object % if time steps are defined, setgrid creates a Parameters object, which is saved in obj.par % also, a Stresses object is defined for each stress period, and the resulting Stresses vector is stored in obj.stress % if obj.par or obj.stress are already set, they are reset if different grid dimensions [nz nr] were defined obj.grid = MAxSym.Grid(varargin{:}); if ~isempty(obj.time) if isempty(obj.par) || obj.grid.nr ~= obj.par.nr || obj.grid.nz ~= obj.par.nz || ... xor(obj.grid.confined,obj.par.confined) obj.setpar; end if isempty(obj.stress) || obj.stress(1).nr ~= obj.grid.nr || obj.stress(1).nz ~= obj.grid.nz obj.setstress; end end end function setsolver(obj,varargin) % define solver % % syntax: obj.setsolver(delta,mni,nparm) % delta is criterion for convergence [L] % mni is maximum number of iterations for one time step % nparm (optional) is number of SIP iteration parameters (5 is sufficient in most cases) % if nparm is given and greater than 0, SIP is used, otherwise, ADI is used % % setsolver creates a Solver object, which is stored in obj.solver obj.solver = MAxSym.Solver(varargin{:}); end function run(obj) % run model % % syntax: obj.run % check input obj.checkinput; % set flow constants obj.setflowconstants; % solve equations obj.solve; end function s = interp1r(obj,ilay,r,it,rw) % interpolate drawdown in dimension of log(r) % % if simulation is transient: % syntax: s = obj.interp1r(ilay,r,it,rw) % ilay is vector with indices of considered layers % if ilay is empty, all layers are considered or ilay = 1:obj.grid.nz % r is vector with radial distances at which drawdown is interpolated % radial distances must be between zero and obj.grid.r(end) % it is vector with indices of considered simulation times % if it is empty, all simulation times are considered or it = 1:length(obj.time.nt) % rw (optional) is a vector of length(ilay) with well radius for each layer % if rw is empty or not given, the well radius is assumed to coincide with the first nodal circle % if rw is a scalar, all layers are assumed to have the same well radius rw % for distances r smaller than or equal to rw, drawdown s is not interpolated, % but equal to drawdown in the first ring or: if r <= rw(n), then s = obj.s(ilay(n),1,:) % s is the interpolated drawdown array % size(s) = [length(ilay), length(r), length(it)] % % if simulation is steady state % syntax: s = obj.interp1r(ilay,r,rw) % ilay is vector with indices of considered layers % if ilay is empty, all layers are considered or ilay = 1:obj.grid.nz % r is vector with radial distances at which drawdown is interpolated % radial distances must be between zero and obj.grid.r(end) % rw (optional) is a vector of length(ilay) with well radius for each layer % if rw is empty or not given, the well radius is assumed to coincide with the first nodal circle % if rw is a scalar, all layers are assumed to have the same well radius rw % for distances r smaller than or equal to rw, drawdown s is not interpolated, % but equal to drawdown in the first ring or: if r <= rw(n), then s = obj.s(ilay(n),1) % s is the interpolated drawdown matrix % size(s) = [length(ilay), length(r)] % check number of input arguments if obj.time.steady if ~any(nargin == 3:4) error('Method ''interp1r'' requires 3 or 4 input arguments (Model object included) if model is steady state!') end elseif ~any(nargin == 4:5) error('Method ''interp1r'' requires 4 or 5 input arguments (Model object included) if model is transient!') end % discretization parameters nz = obj.grid.nz; nt = size(obj.s,3); % input ilay if ~(obj.vector(ilay,true,true) && obj.integer(ilay) && obj.between(ilay,[1 nz],[true true])) error(['''ilay'' must be integer vector with elements between 1 and ' int2str(nz) '!']) end if isempty(ilay) ilay = 1:nz; end % input r if ~(obj.vector(r,true,false) && isnumeric(r) && obj.between(r,[0 obj.grid.r(end)],[true true])) error('''r'' must be numeric vector with elements between 0 and radius of outer nodal circle!') end % input it if ~obj.time.steady if ~(obj.vector(it,true,true) && obj.integer(it) && obj.between(it,[1 nt],[true true])) error(['''it'' must be integer vector with elements between 1 and ' int2str(nt) '!']) end if isempty(it) it = 1:nt; end else it = 1; end % input rw if (obj.time.steady && nargin == 4) || nargin == 5 if ~(obj.vector(rw,true,true) && isnumeric(rw) && obj.length(rw,length(ilay),true,true) && ... obj.between(rw,[0 obj.grid.r(end)],[true true])) error(['''rw'' must be numeric vector of length ' int2str(length(ilay)) ' with elements between 0 and radius of outer nodal circle!']) end rw = obj.repmat(rw,size(ilay)); else rw = obj.grid.r(1) * ones(size(ilay)); end % allocate s s = NaN(length(r),length(ilay),length(it)); % interpolate if any(r > min(rw)) s = interp1(log10(obj.grid.r),permute(obj.s(ilay,:,it),[2 1 3]),log10(r)); end % permute s = permute(s,[2 1 3]); % well radius for n = 1:length(ilay) b = r <= rw(n); if any(b) s(n,b,:) = repmat(obj.s(ilay(n),1,it),[1 sum(b) 1]); end end end function s = interp1t(obj,ilay,ir,t) % interpolate drawdown in dimension of log(t) % % syntax: s = obj.interp1t(ilay,ir,t) % ilay is vector with indices of considered layers % if ilay is empty, all layers are considered or ilay = 1:obj.grid.nz % ir is vector with indices of considered rings % if ir is empty, all rings are considered or ir = 1:obj.grid.nr % t is vector with times at which drawdown is interpolated % times must be between zero and obj.time.t(end) % for times t smaller than obj.time.t(2), drawdown s is not interpolated, % but equal to initial drawdown or: if t < obj.time.t(2), then s = obj.stress(1).s0 % s is the interpolated drawdown array % size(s) = [length(ilay), length(ir), length(t)] % % remark: method interp1t can only be used if the model is transient % check if not steady state if obj.time.steady error('Applying linear interpolation in time dimension is useless if model is steady state!') end % discretization parameters nz = obj.grid.nz; nr = obj.grid.nr; % input ilay if ~(obj.vector(ilay,true,true) && obj.integer(ilay) && obj.between(ilay,[1 nz],[true true])) error(['''ilay'' must be integer vector with elements between 1 and ' int2str(nz) '!']) end if isempty(ilay) ilay = 1:nz; end % input ir if ~(obj.vector(ir,true,true) && obj.integer(ir) && obj.between(ir,[1 nr],[true true])) error(['''ir'' must be integer vector with elements between 1 and ' int2str(nr) '!']) end if isempty(ir) ir = 1:nr; end % input t if ~(obj.vector(t,true,false) && isnumeric(t) && obj.between(t,[0 obj.time.t(end)],[true true])) error('''t'' must be numeric vector with elements between 0 and last simulation time!') end % allocate s s = NaN(length(t),length(ilay),length(ir)); t0 = obj.time.t(2); % instantaneous head changes [b,ti,si] = obj.times4s0(t); if any(b) s(b,:,:) = interp1(log10(ti),permute(si(ilay,ir,:),[3 1 2]),log10(t(b))); end % interpolate j = t < t0; b = ~(b|j); if any(b) s(b,:,:) = interp1(log10(obj.time.t(2:end)),permute(obj.s(ilay,ir,2:end),[3 1 2]),log10(t(b))); end % permute s = permute(s,[2 3 1]); % initial times t < t(2) if any(j) s(:,:,j) = repmat(obj.s(ilay,ir,1),[1 1 sum(j)]); end end function s = interp2(obj,ilay,r,t,rw) % interpolate drawdown in dimension of log(r) and dimension of log(t) % % syntax: s = obj.interp2(ilay,r,t,rw) % ilay is vector with indices of considered layers % if ilay is empty, all layers are considered or ilay = 1:obj.grid.nz % r is vector with radial distances at which drawdown is interpolated % radial distances must be between zero and obj.grid.r(end) % t is vector with times at which drawdown is interpolated % times must be between zero and obj.time.t(end) % for times t smaller than obj.time.t(2), drawdown s is not interpolated, % but equal to initial drawdown or: if t < obj.time.t(2), then s = obj.stress(1).s0 % rw (optional) is a vector of length(ilay) with well radius for each layer % if rw is empty or not given, the well radius is assumed to coincide with the first nodal circle % if rw is a scalar, all layers are assumed to have the same well radius rw % for distances r smaller than or equal to rw, drawdown s is not interpolated, % but equal to drawdown in the first ring or: if r <= rw(n), then s = obj.s(ilay(n),1,:) % s is the interpolated drawdown array % size(s) = [length(ilay), length(r), length(t)] % % remark: method interp2 can only be used if the model is transient % discretization parameters nz = obj.grid.nz; % input ilay if ~(obj.vector(ilay,true,true) && obj.integer(ilay) && obj.between(ilay,[1 nz],[true true])) error(['''ilay'' must be integer vector with elements between 1 and ' int2str(nz) '!']) end if isempty(ilay) ilay = 1:nz; end % input r if ~(obj.vector(r,true,false) && isnumeric(r) && obj.between(r,[0 obj.grid.r(end)],[true true])) error('''r'' must be numeric vector with elements between 0 and radius of outer nodal circle!') end % input t if ~(obj.vector(t,true,false) && isnumeric(t) && obj.between(t,[0 obj.time.t(end)],[true true])) error('''t'' must be numeric vector with elements between 0 and last simulation time!') end % input rw if nargin > 4 if ~(obj.vector(rw,true,true) && isnumeric(rw) && obj.length(rw,length(ilay),true,true) && ... obj.between(rw,[0 obj.grid.r(end)],[true true])) error(['''rw'' must be numeric vector of length ' int2str(length(ilay)) ' with elements between 0 and radius of outer nodal circle!']) end rw = obj.repmat(rw,size(ilay)); else rw = obj.grid.r(1) * ones(size(ilay)); end % initialize variables s = NaN(length(t),length(r),length(ilay)); j = t < obj.time.t(2); [bt,ti,si] = obj.times4s0(t); [rax,tax] = deal(log10(obj.grid.r),log10(obj.time.t(2:end))); [r,t] = deal(log10(r(:)'),log10(t(:))); rw = log10(rw); % instantaneous head changes if any(bt) ti = log10(ti); for n = 1:length(ilay) s(bt,:,n) = interp2(rax,ti,permute(si(ilay(n),:,:),[3 2 1]),r,t(bt)); end end % initial times t < t(2) if any(j) tmp = interp1(rax,permute(obj.s(ilay,:,1),[2 1 3]),r); s(j,:,:) = permute(repmat(tmp,[1 1 sum(j)]),[3 1 2]); end % interpolate bt = ~(j|bt); for n = 1:length(ilay) b = r > rw(n); % points outside well if any(b) && any(bt) s(bt,b,n) = interp2(rax,tax,permute(obj.s(ilay(n),:,2:end),[3 2 1]),r(b),t(bt)); end % points inside well b = ~b; if any(b) if any(~j) s(~j,b,n) = repmat(interp1(tax,squeeze(obj.s(ilay(n),1,2:end)),t(~j)),[1 sum(b)]); end if any(j) s(j,b,n) = s(ilay(n),1,1); end end end % permute s = permute(s,[3 2 1]); end function qr = get.qr(obj) if isempty(obj.s) qr = []; else nt = size(obj.s,3); qr = repmat(obj.qrc,[1 1 nt]); if ~obj.grid.confined qr(1,:,:) = qr(1,:,:) .* (1 + (obj.s(1,1:end-1,:)+obj.s(1,2:end,:))/2/obj.grid.D(1)); end qr = qr .* -diff(obj.s,1,2); z = zeros(obj.par.nz,1,nt); qr = cat(2,z,qr,z); qr(isnan(qr)) = 0; end end function qz = get.qz(obj) if isempty(obj.s) || obj.par.nz == 1 qz = []; else nt = size(obj.s,3); qz = repmat(obj.qzc,[1 1 nt]); if ~obj.grid.confined b = obj.hskz > 0; if any(b) qz(1,b,:) = 1 ./ (1./qz(1,b,:) + obj.s(1,b,:)./repmat(obj.hskz(b),[1 1 nt])); end end qz = qz .* diff(obj.s,1,1); z = zeros(1,obj.par.nr,nt); qz = cat(1,z,qz,z); qz(isnan(qz)) = 0; end end function qs = get.qs(obj) % no storage if isempty(obj.s) || obj.time.steady qs = []; % transient simulation else nt = size(obj.s,3) - 1; [nz,nr] = deal(obj.par.nz,obj.par.nr); qs = repmat(obj.qssc,[1 1 nt]); if ~obj.grid.confined qs(1,:,:) = repmat(obj.qsyc,[1 1 nt]) + qs(1,:,:) .* (1 + obj.s(1,:,2:end)/obj.grid.D(1)); end qs = qs .* diff(obj.s,1,3) ./ repmat(reshape(obj.time.dt,[1 1 nt]),nz,nr); % take into account initial head changes nt = cumsum(obj.time.ndt(:)); for k = 1:length(nt)-1 s0 = obj.stress(k+1).s0; if ~isempty(s0) && any(s0(:)) dsdt = obj.repmat(s0,[nz,nr])/obj.time.dt(nt(k)); qs(:,:,nt(k)) = qs(:,:,nt(k)) - obj.qssc .* dsdt; if ~obj.grid.confined qs(1,:,nt(k)) = qs(1,:,nt(k)) - obj.qsyc .* dsdt(1,:); end end end qs(isnan(qs)) = 0; end end function bud = get.bud(obj) % flow components [nz,nr] = deal(obj.par.nz,obj.par.nr); qr = obj.qr; bud = qr(:,2:end,:)-qr(:,1:end-1,:); if nz > 1 qz = obj.qz; bud = bud - qz(2:end,:,:)+qz(1:end-1,:,:); end % variable head rings b = true(nz,nr); if ~isempty(obj.par.inactive) b = b & ~obj.repmat(obj.par.inactive,[nz,nr]); end if ~isempty(obj.par.constant) b = b & ~obj.repmat(obj.par.constant,[nz,nr]); end % steady state: sources and sinks if obj.time.steady if ~isempty(obj.stress.q) q = full(obj.repmat(obj.stress.q,[nz,nr])); bud(b) = bud(b) + q(b); end % transient: storage decrease and sources/sinks else bud = bud(:,:,2:end) + obj.qs; ndt = obj.time.ndt; nt = cumsum([0; ndt(:)]); b = ~b; for n = 1:obj.time.nper if ~isempty(obj.stress(n).q) q = full(obj.repmat(obj.stress(n).q,[nz,nr])); q(b) = 0; bud(:,:,nt(n)+1:nt(n+1)) = bud(:,:,nt(n)+1:nt(n+1)) + repmat(q,[1 1 ndt(n)]); end end end end function totbud = get.totbud(obj) b = obj.repmat(obj.par.constant,[obj.grid.nz,obj.grid.nr]); bud = obj.bud; for n = 1:size(bud,3) tmp = bud(:,:,n); totbud(n,:) = [sum(tmp(~b)),sum(tmp(b))]; end end end methods (Access = protected) function setpar(obj) % set obj.par obj.par = MAxSym.Parameters(obj.grid.nz,obj.grid.nr,obj.grid.confined,obj.time.steady); end function setstress(obj) % set obj.stress for n = 1:obj.time.nper obj.stress(n) = MAxSym.Stresses(obj.grid.nz,obj.grid.nr); end end function checkinput(obj) % check model input if isempty(obj.time) error('Time steps are not defined!') end if isempty(obj.grid) error('Grid is not defined!') end if ~obj.par.isdefined error('Some parameters are missing!') end for n = 1:length(obj.stress) b(n) = obj.stress(n).isdefined; end if ~any(b) error('No sources or sinks defined!') elseif obj.par.steady && (isempty(obj.par.constant) || ~any(obj.par.constant(:))) q = obj.stress.q; if isempty(q) || abs(sum(q(:)))>sum(q(q>0))*1.001 error('Steady state model is ill-posed!') end end if isempty(obj.solver) error('Solver is not defined!') end end function setflowconstants(obj) % set flow constants required for solver routine: % obj.qrc, obj.qzc, obj.qssc, obj.qsyc, obj.hskz % get grid properties nr = obj.par.nr; nz = obj.par.nz; steady = obj.par.steady; confined = obj.par.confined; rb = obj.repmat(obj.grid.rb,[nz,nr+1]); D = obj.repmat(obj.grid.D,[nz,nr]); hs = obj.repmat(obj.grid.hs,[nz,nr]); vol = obj.repmat(obj.grid.vol,[nz,nr]); ia = obj.repmat(obj.par.inactive,[nz,nr]); cst = obj.repmat(obj.par.constant,[nz,nr]); if isempty(ia) ia = false(nz,nr); end if isempty(cst) cst = false(nz,nr); end % set qrc kr = obj.repmat(obj.par.kr,[nz,nr]); cr = full(obj.repmat(obj.par.cr,[nz,nr-1])); if ~isempty(kr) dr = log(rb(:,2:end)./rb(:,1:end-1)); obj.qrc = 4*pi * D(:,1:end-1) ./ (dr(:,1:end-1)./kr(:,1:end-1) + dr(:,2:end)./kr(:,2:end)); if ~isempty(cr) b = cr > 0 & ~isnan(cr); dr = 2*pi * rb(:,2:end-1) .* D(:,1:end-1); obj.qrc(b) = dr(b) ./ cr(b); end elseif ~isempty(cr) obj.qrc = 2*pi * rb(:,2:end-1) .* D(:,1:end-1) ./ obj.cr; else error('Radial conductivity is not defined!') end b = ~(ia(:,1:end-1) | ia(:,2:end)); if any(isnan(obj.qrc(b)) | obj.qrc(b) < 0 | isinf(obj.qrc(b))) error('Radial conductivity is not defined properly!') end obj.qrc(~b) = 0; % set qzc if nz > 1 kz = obj.repmat(obj.par.kz,[nz,nr]); cz = full(obj.repmat(obj.par.cz,[nz-1,nr])); if ~isempty(kz) obj.qzc = 2*hs(1:end-1,:) ./ (D(1:end-1,:)./kz(1:end-1,:) + D(2:end,:)./kz(2:end,:)); if ~isempty(cz) b = cz > 0 & ~isnan(cz); tmp = hs(1:end-1,:); obj.qzc(b) = tmp(b) ./ cz(b); end elseif ~isempty(cz) obj.qzc = hs(1:end-1,:) ./ cz; else error('Vertical conductivity is not defined!') end b = ~(ia(1:end-1,:) | ia(2:end,:)); if any(isnan(obj.qzc(b)) | obj.qzc(b) < 0 | isinf(obj.qzc(b))) error('Vertical conductivity is not defined properly!') end obj.qzc(~b) = 0; else obj.qzc = []; end % set qssc if ~steady ss = obj.repmat(obj.par.ss,[nz,nr]); if ~isempty(ss) obj.qssc = vol .* ss; else error('Specific elastic storage is not defined!') end b = ~(ia | cst); if any(isnan(obj.qssc(b)) | obj.qssc(b) < 0 | isinf(obj.qssc(b))) error('Specific elastic storage is not defined properly!') end obj.qssc(~b) = 0; else obj.qssc = []; end % set qsyc if ~(steady || confined) sy = obj.repmat(obj.par.sy,[1,nr]); if ~isempty(sy) obj.qsyc = hs(1,:) .* sy; else error('Specific yield is not defined!') end b = ~(ia(1,:) | cst(1,:)); if any(isnan(obj.qsyc(b)) | obj.qsyc(b) < 0 | isinf(obj.qsyc(b))) error('Specific yield is not defined properly!') end obj.qsyc(~b) = 0; else obj.qsyc = []; end % set hskz if nz > 1 && ~confined if ~isempty(kz) obj.hskz = 2 * hs(1,:) .* kz(1,:); if ~isempty(cz) b = cz(1,:) > 0 & ~isnan(cz(1,:)); obj.hskz(b) = -1; end else obj.hskz = -ones(1,nr); end else obj.hskz = []; end end function solve(obj) % calls appropriate solver routine: % solveconfined if confined system % solveunconfined if unconfined system [nz,nr] = deal(obj.par.nz,obj.par.nr); if obj.par.confined [obj.s,obj.niter] = MAxSym.solveconfined(nz,nr,obj.time.nper,obj.time.ndt,obj.time.dt,... obj.qrc,obj.qzc,obj.qssc,... obj.repcellelements({obj.stress.q},[nz,nr]),obj.repcellelements({obj.stress.s0},[nz,nr]),... obj.repmat(obj.par.inactive,[nz,nr]),obj.repmat(obj.par.constant,[nz,nr]),... obj.solver.delta,obj.solver.mni,obj.solver.nparm,obj.solver.wseed,obj.solver.accl,obj.solver.mex); else [obj.s,obj.niter] = MAxSym.solveunconfined(nz,nr,obj.time.nper,obj.time.ndt,obj.time.dt,... obj.qrc,obj.qzc,obj.qssc,obj.qsyc,obj.grid.D(1),obj.hskz,... obj.repcellelements({obj.stress.q},[nz,nr]),obj.repcellelements({obj.stress.s0},[nz,nr]),... obj.repmat(obj.par.inactive,[nz,nr]),obj.repmat(obj.par.constant,[nz,nr]),... obj.solver.delta,obj.solver.mni,obj.solver.nparm,obj.solver.wseed,obj.solver.accl,obj.solver.mex); end end function [b,ti,si] = times4s0(obj,t) % selects times for which interpolated drawdown must be corrected for initial drawdown s0 % % syntax: [b,ti,si] = times4s0(obj,t) % t is vector with times at which drawdown is interpolated % b indicates which times t are inside time steps ending with an instantaneous head change % ti is vector with relevant simulation times % si is array with relevant drawdowns corrected for instantaneous head changes % size(si) = [m.grid.nz, m.grid.nr, length(ti)] % % times4s0 is called by methods interp1t and interp2 b = false(size(t)); [ti,si] = deal([]); it = cumsum([1;obj.time.ndt(:)]); for n = 2:length(obj.stress) s0 = obj.repmat(obj.stress(n).s0,[obj.grid.nz,obj.grid.nr]); if any(abs(s0(:))>0) k = [it(n)-1, it(n)]; j = t > obj.time.t(k(1)) & t < obj.time.t(k(2)); if any(j) b = b|j; ti(end+1:end+2,1) = obj.time.t(k); si = cat(3,si,obj.s(:,:,k(1)),obj.s(:,:,k(2))-s0); end end end end end end