library(RcppOctave) .CallOctave("version")
library(RcppOctave) o.TPFA <- OctaveFunction(" function [P,V] = TPFA(Grid, K, q) % Compute transmissibilities by harmonic averaging. Nx=Grid.Nx; Ny=Grid.Ny; Nz=Grid.Nz; N=Nx*Ny*Nz; hx=Grid.hx; hy=Grid.hy; hz=Grid.hz; L = K.^(-1); tx = 2*hy*hz/hx; TX = zeros(Nx+1,Ny,Nz); ty = 2*hx*hz/hy; TY = zeros(Nx,Ny+1,Nz); tz = 2*hx*hy/hz; TZ = zeros(Nx,Ny,Nz+1); TX(2:Nx,:,:) = tx./(L(1,1:Nx-1,:,:)+L(1,2:Nx ,:,:)); TY(:,2:Ny,:) = ty./(L (2,:,1: Ny-1,:)+L(2,:,2:Ny,:)); TZ (:,:,2: Nz) = tz./(L (3,:,:,1: Nz-1)+L(3,:,:,2:Nz)); % Assemble TPFA discretization matrix. x1 = reshape(TX(1:Nx,:,:),N,1); x2 = reshape(TX(2:Nx+1,:,:),N,1); y1 = reshape(TY(:,1:Ny,:),N,1); y2 = reshape(TY(:,2:Ny+1,:),N,1); z1 = reshape(TZ(:,:,1:Nz),N,1); z2 = reshape(TZ(:,:,2:Nz+1),N,1); DiagVecs = [-z2,-y2,-x2,x1+x2+y1+y2+z1+z2,-x1,-y1,-z1]; DiagIndx = [-Nx*Ny,-Nx,-1,0,1,Nx,Nx*Ny]; A = spdiags(DiagVecs,DiagIndx,N,N); A(1,1) = A(1,1)+sum(Grid.K(:,1,1,1)); % Solve linear system and extract interface fluxes. u = A\\q; P = reshape(u,Nx,Ny,Nz); V.x = zeros(Nx+1,Ny,Nz); V.y = zeros(Nx,Ny+1,Nz); V.z = zeros(Nx,Ny,Nz+1); V.x(2:Nx ,:,:) = (P(1:Nx-1,:,:)-P(2:Nx,:,:)).*TX(2:Nx,:,:); V.y (:,2:Ny,:) = (P(:,1:Ny-1,:)-P(:,2:Ny,:)).*TY(:,2:Ny,:); V.z (:,:,2: Nz) = (P (:,:,1: Nz-1)-P(:,:,2:Nz)).*TZ(:,:,2:Nz); ") # TPFA <- o.TPFA(Grid, K, q) o.GenA <- OctaveFunction(" function A = GenA(Grid,V,q) Nx=Grid.Nx; Ny=Grid.Ny; Nz=Grid.Nz; N=Nx*Ny*Nz; N=Nx*Ny*Nz; % number of unknowns fp=min(q,0); % production XN=min(V.x,0); x1=reshape(XN(1:Nx,:,:),N,1); % separate flux into YN=min(V.y,0); y1=reshape(YN(:,1:Ny,:),N,1); % − flow in positive coordinate ZN=min(V.z,0); z1=reshape(ZN(:,:,1:Nz),N,1); % direction (XP,YP,ZP) XP=max(V.x,0); x2=reshape(XP(2:Nx+1,:,:),N,1); % − flow in negative coordinate YP=max(V.y,0); y2=reshape(YP(:,2:Ny+1,:),N,1); % direction (XN,YN,ZN) ZP=max(V.z,0); z2=reshape(ZP(:,:,2:Nz+1),N,1); % DiagVecs=[z2,y2,x2,fp+x1-x2+y1-y2+z1-z2,-x1,-y1,-z1]; % diagonal vectors DiagIndx=[-Nx*Ny,-Nx,-1,0,1,Nx,Nx*Ny]; % diagonal index A=spdiags(DiagVecs,DiagIndx,N,N); % matrix with upwind FV stencil ") # GenA <- o.GenA(Grid, V, q) o.RelPerm <- OctaveFunction(" function [Mw,Mo,dMw,dMo] = RelPerm(s,Fluid) S = (s-Fluid.swc)/(1-Fluid.swc-Fluid.sor); % Rescale saturations Mw = S.^2/Fluid.vw; % Water mobility Mo =(1-S).^2/Fluid.vo; % Oil mobility if (nargout==4) dMw = 2*S/Fluid.vw/(1-Fluid.swc-Fluid.sor); dMo = -2*(1-S)/Fluid.vo/(1-Fluid.swc-Fluid.sor); end ") # RelPerm <- o.RelPerm(s, Fluid) o.Pres <- OctaveFunction(" function [P,V] = Pres(Grid,S,Fluid,q) % Compute K∗lambda(S) [Mw,Mo] = RelPerm(S,Fluid); Mt = Mw+Mo; KM = reshape([Mt,Mt,Mt]',3,Grid.Nx,Grid.Ny,Grid.Nz).*Grid.K; % Compute pressure and extract fluxes [P,V] = TPFA(Grid,KM,q); ") # Pres <- o.Pres(Grid, S, Fluid, q) o.Upstream <- OctaveFunction(" function S = Upstream(Grid,S,Fluid,V,q,T) Nx=Grid.Nx; Ny=Grid.Ny; Nz=Grid.Nz; % number of grid points N=Nx*Ny*Nz; % number of unknowns pv = Grid.V(:).*Grid.por(:); % pore volume=cell volume∗porosity fi =max(q,0); % inflow from wells XP=max(V.x,0); XN=min(V.x,0); % influx and outflux, x−faces YP=max(V.y,0); YN=min(V.y,0); % influx and outflux, y−faces ZP=max(V.z,0); ZN=min(V.z,0); % influx and outflux, z−faces Vi = XP(1:Nx,:,:)+YP(:,1:Ny,:)+ZP(:,:,1:Nz)-... % total flux into XN(2:Nx+1,:,:)-YN(:,2:Ny+1,:)-ZN(:,:,2:Nz+1); % each gridblock pm = min(pv./(Vi(:)+fi)); % estimate of influx cfl = ((1-Fluid.swc-Fluid.sor)/3)*pm; % CFL restriction Nts = ceil(T/cfl); % number of local time steps dtx = (T/Nts)./pv; % local time steps A = GenA(Grid,V,q); % system matrix A=spdiags(dtx,0,N,N)*A; % A ∗ dt/|Omega i| fi =max(q,0).*dtx; % injection for t=1:Nts [mw,mo]=RelPerm(S,Fluid); % compute mobilities fw = mw./(mw+mo); % compute fractional flow S = S+(A*fw+fi); % update saturation end ") # Upstream <- o.Upstream(Grid, S, Fluid, V, q, TT)
o.runExample <- OctaveFunction(" function [Grid, N, S, Fluid, Q] = runExample() Grid.Nx=64; Dx=1; Grid.hx = Dx/Grid.Nx; % Dimension in x−direction Grid.Ny=64; Dy=1; Grid.hy = Dy/Grid.Ny; % Dimension in y−direction Grid.Nz=1; Dz=1; Grid.hz = Dz/Grid.Nz; % Dimension in z−direction N = Grid.Nx*Grid.Ny; % Total number of grid blocks Grid.V = Grid.hx*Grid.hy*Grid.hz; % Cell volumes Grid.K = ones(3,Grid.Nx,Grid.Ny,Grid.Nz); % Unit permeability Grid.por = ones(Grid.Nx,Grid.Ny,Grid.Nz); % Unit porosity Q = zeros(N,1); Q([1 N])=[1 -1]; % Production/injection Fluid.vw = 1.0; Fluid.vo=1.0; % Viscosities Fluid.swc = 0.0; Fluid.sor=0.0; % Irreducible saturations S = zeros(N,1); % Initial saturation nt = 25; dt = 0.7/nt; % Time steps for t=1:nt [P,V]=Pres(Grid,S,Fluid,Q); % pressure solver S=Upstream(Grid,S,Fluid,V,Q,dt); % saturation solver % plot filled contours at the midpoints of the grid cells contourf(linspace(Grid.hx/2,Dx-Grid.hx/2,Grid.Nx),... linspace(Grid.hy/2,Dy-Grid.hy/2,Grid.Ny),... reshape(S,Grid.Nx,Grid.Ny),11,'k'); axis square; caxis([0 1]); % equal axes and color drawnow; % force update of plot end ") runExample <- o.runExample()
runExample <- o.runExample() N <- runExample$N N dim(runExample$Q) names(runExample$Grid) # Grid Grid <- runExample$Grid Grid["Nx"] Grid["hx"] Grid["V"] dim(Grid[["por"]]) # porosity: array 64x64 dim(Grid[["K"]]) # permeability: array 3x64x64 # Fluid cat("Fluid \n") Fluid <- runExample$Fluid Fluid # Rates Q <- runExample$Q dim(Q) # rate: column vector of 4096x1 # Saturations S <- runExample$S dim(S)
o.PV <- OctaveFunction(" function [P, V] = getPV(N, Grid, S, Fluid, Q) nt = 25; dt = 0.7/nt; % Time steps for t = 1:nt [P,V] = Pres(Grid, S, Fluid, Q); % pressure solver S = Upstream(Grid, S, Fluid, V, Q, dt); % saturation solver end ")
PV <- o.PV(N, Grid, S, Fluid, Q)
o.PV <- OctaveFunction(" function [P, V] = getPV(N, Grid, S, Fluid, Q) % Pres(Grid,S,Fluid,q) [P,V] = Pres(Grid, S, Fluid, Q); % pressure solver ") PV <- o.PV(N, Grid, S, Fluid, Q)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.