def test(N): x = np.linspace(0, 1, N) pg.tic() mesh = pg.createGrid(x, x, x) print(mesh) pg.toc() A = pg.RSparseMatrix() A.fillStiffnessMatrix(mesh) pg.toc()
def sparseMatrix2Array(matrix, indices=True, getInCRS=True): """Extract indices and value from sparse matrix (SparseMap or CRS) Get python Arrays from SparseMatrix or SparseMapMatrix in either CRS convention (row index, column Start_End, values) or row index, column index, values. Parameters ---------- matrix: pg.SparseMapMatrix or pg.SparseMatrix Input matrix to be transformed to numpy arrays. indices: boolean (True) Decides weather the indices of the matrix will be returned or not. getInCSR: boolean (True) If returned, the indices can have the format of a compressed row storage (CSR), the default or uncompressed lists with column and row indices. Returns ------- vals: numpy.ndarray Entries of the matrix. indices: list, list Optional. Reurns additional array with the indices for reconstructing the matrix in the defined format. """ io_warn = 'Only working for pygimli.SparseMapMatrix or CSR shaped ' +\ 'pygimli.Sparse matrices. Import type is {}' assert isinstance(matrix, pg.SparseMapMatrix) or\ isinstance(matrix, pg.SparseMatrix), io_warn.format(type(matrix)) if not isinstance(matrix, pg.SparseMatrix): matrix = pg.RSparseMatrix(matrix) vals = np.array(matrix.vecVals()) if indices is True: rows = list(matrix.vecRowIdx()) cols = list(matrix.vecColPtr()) if getInCRS: return list(rows), list(cols), vals else: rr, cc = convertCRSIndex2Map(rows, cols) return rr, cc, vals else: return vals
def createStiffnessMatrix(mesh, a=None): """Create the Stiffness matrix. Calculates the scaled stiffness matrix for the given mesh scaled with the per cell values a. ..math:: ... Parameters ---------- mesh : :gimliapi:`GIMLI::Mesh` Arbitrary mesh to calculate the stiffness for. Type of base and shape functions depends on the cell types. a : array, either complex or real, callable Per cell values., e.g., physical parameter. If None given default is 1. Returns ------- A : :gimliapi:`GIMLI::RSparseMatrix` Stiffness matrix """ if a is None: a = pg.RVector(mesh.cellCount(), 1.0) A = None if isinstance(a[0], float) or isinstance(a[0], np.float64): A = pg.RSparseMatrix() A.fillStiffnessMatrix(mesh, a) return A else: A = pg.CSparseMatrix() # create matrix structure regarding the mesh A.buildSparsityPattern(mesh) # define a local element matrix A_l = pg.ElementMatrix() for c in mesh.cells(): A_l.ux2uy2uz2(c) A.add(A_l, scale=a[c.id()]) # if c.id() == 0: # print(c.id(), A_l) return A
def linsolve(A, b, verbose=False): r""" Direct solution after :math:`\textbf{x}` using cholmod: .. math:: \textbf{A}\textbf{x} = \textbf{b} If :math:`\textbf{A}` is symmetric, sparse and positive definite. Parameters ---------- A : :gimliapi:`GIMLI::RSparseMatrix` | :gimliapi:`GIMLI::RSparseMapMatrix`| numpy.array System matrix. Need to be symmetric, sparse and positive definite. b : iterable array Right hand side of the equation. verbose : bool [False] Be verbose. Returns ------- x : :gimliapi:`GIMLI::RVector` Solution vector """ x = pg.RVector(len(b), .0) if isinstance(A, pg.RSparseMapMatrix): S = pg.RSparseMatrix(A) solver = pg.LinSolver(S, verbose=verbose) solver.solve(b, x) elif isinstance(A, np.ndarray): return np.linalg.solve(A, b) else: solver = pg.LinSolver(A, verbose=verbose) solver.solve(b, x) return x
def createMassMatrix(mesh, b=None): """ Assemble mass element matrix. TODO remove b .. not necessary .. b should be scaled in final equation not here. Calculates the mass element matrix for the given mesh scaled with the per cell values b. ..math:: ... Parameters ---------- mesh : :gimliapi:`GIMLI::Mesh` Arbitrary mesh to calculate the mass element matrix. Type of base and shape functions depends on the cell types. b : array Per cell values. If None given default is 1. Returns ------- A : :gimliapi:`GIMLI::RSparseMatrix` Mass element matrix """ # need callable here if b is None: b = pg.RVector(mesh.cellCount(), 1.0) B = pg.RSparseMatrix() B.fillMassMatrix(mesh, b) return B
def solvePressureWave(mesh, velocities, times, sourcePos, uSource, verbose): r""" Solve pressure wave equation. Solve pressure wave for a given source function .. math:: \frac{\partial^2 u}{\partial t^2} & = \diverg(a\grad u) + f\\ finalize equation Parameters ---------- mesh : :gimliapi:`GIMLI::Mesh` Mesh to solve on velocities : array velocities for each cell of the mesh time : array Time base definition sourcePos : RVector3 Source position uSource : array u(t, sourcePos) source movement of length(times) Usually a Ricker wavelet of the desired seismic signal frequency. Returns ------- u : RMatrix Return Examples -------- See TODO write example """ A = pg.RSparseMatrix() M = pg.RSparseMatrix() # F = pg.RVector(mesh.nodeCount(), 0.0) rhs = pg.RVector(mesh.nodeCount(), 0.0) u = pg.RMatrix(len(times), mesh.nodeCount()) v = pg.RMatrix(len(times), mesh.nodeCount()) sourceID = mesh.findNearestNode(sourcePos) if len(uSource) != len(times): raise Exception("length of uSource does not fit length of times: " + str(uSource) + " != " + len(times)) A.fillStiffnessMatrix(mesh, velocities * velocities) M.fillMassMatrix(mesh) # M.fillMassMatrix(mesh, velocities) FV = 0 if FV: A, rhs = pygimli.solver.diffusionConvectionKernel(mesh, velocities * velocities, sparse=1) M = pygimli.solver.identity(len(rhs)) u = pg.RMatrix(len(times), mesh.cellCount()) v = pg.RMatrix(len(times), mesh.cellCount()) sourceID = mesh.findCell(sourcePos).id() dt = times[1] - times[0] theta = 0.51 #theta = 1. S1 = M + dt * dt * theta * theta * A S2 = M solver1 = pg.LinSolver(S1, verbose=False) solver2 = pg.LinSolver(S2, verbose=False) swatch = pg.Stopwatch(True) # ut = pg.RVector(mesh.nodeCount(), .0) # vt = pg.RVector(mesh.nodeCount(), .0) timeIter1 = np.zeros(len(times)) timeIter2 = np.zeros(len(times)) timeIter3 = np.zeros(len(times)) timeIter4 = np.zeros(len(times)) progress = pg.utils.ProgressBar(its=len(times), width=40, sign='+') for n in range(1, len(times)): u[n - 1, sourceID] = uSource[n - 1] # solve for u tic = time.time() # + * dt*dt * F rhs = dt * M * v[n - 1] + (M - dt * dt * theta * (1. - theta) * A) * u[n - 1] timeIter1[n - 1] = time.time() - tic tic = time.time() u[n] = solver1.solve(rhs) timeIter2[n - 1] = time.time() - tic # solve for v tic = time.time() rhs = M * v[n - 1] - dt * \ ((1. - theta) * A * u[n - 1] + theta * A * u[n]) # + dt * F timeIter3[n - 1] = time.time() - tic tic = time.time() v[n] = solver2.solve(rhs) timeIter4[n - 1] = time.time() - tic # same as above # rhs = M * v[n-1] - dt * A * u[n-1] + dt * F # v[n] = solver1.solve(rhs) t1 = swatch.duration(True) if verbose: progress(n) return u
def solveFiniteVolume(mesh, a=1.0, b=0.0, f=0.0, fn=0.0, vel=None, u0=0.0, times=None, uL=None, relax=1.0, ws=None, scheme='CDS', **kwargs): r"""Solve partial differential equation with Finite Volumes. This function is a syntactic sugar proxy for using the Finite Volume functionality of the library core to solve elliptic and parabolic partial differential of the following type: .. math:: \frac{\partial u}{\partial t} + \mathbf{v}\cdot\nabla u & = \nabla\cdot(a \nabla u) + b u + f(\mathbf{r},t)\\ u(\mathbf{r}, t) & = u_B \quad\mathbf{r}\in\Gamma_{\text{Dirichlet}}\\ \frac{\partial u(\mathbf{r}, t)}{\partial \mathbf{n}} & = u_{\partial \text{B}} \quad\mathbf{r}\in\Gamma_{\text{Neumann}}\\ u(\mathbf{r}, t=0) & = u_0 \quad\text{with} \quad\mathbf{r}\in\Omega The Domain :math:`\Omega` and the Boundary :math:`\Gamma` are defined through the given mesh with appropriate boundary marker. The solution :math:`u(\mathbf{r}, t)` is given for each cell in the mesh. TODO: * Refactor with solver class and Runga-Kutte solver * non steady boundary conditions Parameters ---------- mesh : :gimliapi:`GIMLI::Mesh` Mesh represents spatial discretization of the calculation domain a : value | array | callable(cell, userData) Stiffness weighting per cell values. b : value | array | callable(cell, userData) Scale for mass values b f : iterable(cell) Load vector fn : iterable(cell) TODO What is fn vel : ndarray (N,dim) | RMatrix(N,dim) Velocity field :math:`\mathbf{v}(\mathbf{r}, t=\text{const}) = \{[v_i]_j,\}` with :math:`i=[1\ldots 3]` for the mesh dimension and :math:`j = [0\ldots N-1]` with N either the amount of cells, nodes, or boundaries. Velocities per boundary are preferred and will be interpolated on demand. u0 : value | array | callable(cell, userData) Starting field times : iterable Time steps to calculate for. ws : Workspace This can be an empty class that will used as an Workspace to store and cache data. If ws is given: The system matrix is taken from ws or calculated once and stored in ws for further usage. The system matrix is cached in this Workspace as ws.S The LinearSolver with the factorized matrix is cached in this Workspace as ws.solver The rhs vector is only stored in this Workspace as ws.rhs scheme : str [CDS] Finite volume scheme: :py:mod:`pygimli.solver.diffusionConvectionKernel` **kwargs: * uB : Dirichlet boundary conditions * duB : Neumann boundary conditions Returns ------- u : ndarray(nTimes, nCells) solution field for all time steps """ verbose = kwargs.pop('verbose', False) # The Workspace is to hold temporary data or preserve matrix rebuild # swatch = pg.Stopwatch(True) sparse = True workspace = pg.solver.WorkSpace() if ws: workspace = ws a = pg.solver.parseArgToArray(a, [mesh.cellCount(), mesh.boundaryCount()]) f = pg.solver.parseArgToArray(f, mesh.cellCount()) fn = pg.solver.parseArgToArray(fn, mesh.cellCount()) boundsDirichlet = None boundsNeumann = None # BEGIN check for Courant-Friedrichs-Lewy if vel is not None: if isinstance(vel, float): print("Warning! .. velocity is float and no vector field") # we need velocities for boundaries if len(vel) is not mesh.boundaryCount(): if len(vel) == mesh.cellCount(): vel = pg.meshtools.cellDataToNodeData(mesh, vel) if len(vel) == mesh.nodeCount(): vel = pg.meshtools.nodeDataToBoundaryData(mesh, vel) else: print("mesh:", mesh) print("vel:", vel.shape) raise Exception("Cannot determine data format for velocities") vmax = 0 if mesh.dimension() == 3: vmax = np.max(np.sqrt(vel[:, 0]**2 + vel[:, 1]**2 + vel[:, 2]**2)) else: vmax = np.max(np.sqrt(vel[:, 0]**2 + vel[:, 1]**2)) if times is not None: dt = times[1] - times[0] dx = min(mesh.boundarySizes()) c = vmax * dt / dx if c > 1: print( "Courant-Friedrichs-Lewy Number:", c, "but sould be lower 1 to ensure movement inside a cell " "per timestep. (" "vmax =", vmax, "dt =", dt, "dx =", dx, "dt <", dx / vmax, " | N > ", int((times[-1] - times[0]) / (dx / vmax)) + 1, ")") # END check for Courant-Friedrichs-Lewy if not hasattr(workspace, 'S'): if 'uB' in kwargs: boundsDirichlet = pg.solver.parseArgToBoundaries( kwargs['uB'], mesh) if 'duB' in kwargs: boundsNeumann = pg.solver.parseArgToBoundaries(kwargs['duB'], mesh) workspace.S, workspace.rhsBCScales = diffusionConvectionKernel( mesh=mesh, a=a, b=b, uB=boundsDirichlet, duB=boundsNeumann, # u0=u0, fn=fn, vel=vel, scheme=scheme, sparse=sparse, userData=kwargs.pop('userData', None)) dof = len(workspace.rhsBCScales) workspace.ap = np.zeros(dof) # for nonlinears if uL is not None: for i in range(dof): val = 0.0 if sparse: val = workspace.S.getVal(i, i) / relax workspace.S.setVal(i, i, val) else: val = workspace.S[i, i] / relax workspace.S[i, i] = val workspace.ap[i] = val # print('FVM kernel 2:', swatch.duration(True)) # endif: not hasattr(workspace, 'S'): workspace.rhs = np.zeros(len(workspace.rhsBCScales)) workspace.rhs[0:mesh.cellCount()] = f # * mesh.cellSizes() workspace.rhs += workspace.rhsBCScales # for nonlinear: relax progress with scaled last result if uL is not None: workspace.rhs += (1. - relax) * workspace.ap * uL # print('FVM: Prep:', swatch.duration(True)) if not hasattr(times, '__len__'): if sparse and not hasattr(workspace, 'solver'): Sm = pg.RSparseMatrix(workspace.S) # hold Sm until we have reference counting, # loosing Sm here will kill LinSolver later workspace.Sm = Sm workspace.solver = pg.LinSolver(Sm, verbose=verbose) u = None if sparse: u = workspace.solver.solve(workspace.rhs) else: u = np.linalg.solve(workspace.S, workspace.rhs) # print('FVM solve:', swatch.duration(True)) return u[0:mesh.cellCount():1] else: theta = kwargs.pop('theta', 0.5 + 1e-6) if sparse: I = pg.solver.identity(len(workspace.rhs)) else: I = np.diag(np.ones(len(workspace.rhs))) if verbose: print("Solve timesteps with Crank-Nicolson.") return pg.solver.crankNicolson(times, theta, workspace.S, I, f=workspace.rhs, u0=pg.solver.parseArgToArray( u0, mesh.cellCount(), mesh), verbose=verbose)
grid), rhs) """ Solve for u """ u[n] = solver.linsolve(S, rhs) """ Solve the second equation for v """ rhs = M * v[n - 1] - A * u[n] * k * theta - k * tmpRhs """ Be aware of python's #No copy at all!#. So we need to take a copy of the mass element matrix ourself to keep in safe environment. """ S = pg.RSparseMatrix(M) #S = M if n == 1: solver.assembleDirichletBC( S, solver.parseArgToBoundaries([grid.findBoundaryByMarker(1), 1], grid), rhs) else: solver.assembleDirichletBC( S, solver.parseArgToBoundaries([grid.findBoundaryByMarker(1), 0], grid), rhs) solver.assembleDirichletBC( S,
def solveFiniteVolume(mesh, a=1.0, b=0.0, f=0.0, fn=0.0, vel=None, u0=0.0, times=None, uL=None, relax=1.0, ws=None, scheme='CDS', **kwargs): """Calculate for u. NOTE works only for steady boundary conditions!!! !!Refactor with solver class and Runga-Kutte solver!! Parameters ---------- mesh : :gimliapi:`GIMLI::Mesh` Mesh represents spatial discretization of the calculation domain a : value | array | callable(cell, userData) cell values b : value | array | callable(cell, userData) TODO What is b f : iterable(cell) TODO What is f fn : iterable(cell) TODO What is fn vel : ndarray (N,dim) | RMatrix(N,dim) velocity field [[v_j,]_i,] with i=[1..3] for the mesh dimension and j = [0 .. N-1] with N either Amount of Cells, Nodes or Boundaries. Velocity per boundary is preferred. u0 : value | array | callable(cell, userData) Starting field ws : Workspace This can be an empty class that will used as an Workspace to store and cache data. If ws is given: The system matrix is taken from ws or calculated once and stored in ws for further usage. The system matrix is cached in this Workspace as ws.S The LinearSolver with the factorized matrix is cached in this Workspace as ws.solver The rhs vector is only stored in this Workspace as ws.rhs scheme : str [CDS] Finite volume scheme: :py:mod:`pygimli.solver.diffusionConvectionKernel` **kwargs: * uB : Dirichlet boundary conditions * duB : Neumann boundary conditions Returns ------- u : ndarray(nTimes, nCells) solution field for all time steps """ verbose = kwargs.pop('verbose', False) # The Workspace is to hold temporary data or preserve matrix rebuild # swatch = pg.Stopwatch(True) sparse = True workspace = pg.solver.WorkSpace() if ws: workspace = ws a = pg.solver.parseArgToArray(a, [mesh.cellCount(), mesh.boundaryCount()]) f = pg.solver.parseArgToArray(f, mesh.cellCount()) fn = pg.solver.parseArgToArray(fn, mesh.cellCount()) boundsDirichlet = None boundsNeumann = None # BEGIN check for Courant-Friedrichs-Lewy if vel is not None: if isinstance(vel, float): print("Warning! .. velocity is float and no vector field") if len(vel) != mesh.cellCount() and \ len(vel) != mesh.nodeCount() and \ len(vel) != mesh.boundaryCount(): print("mesh:", mesh) print("vel:", vel.shape) raise BaseException("Velocity field has wrong dimension.") if len(vel) is not mesh.boundaryCount(): if len(vel) == mesh.cellCount(): vel = pg.meshtools.cellDataToNodeData(mesh, vel).T vel = pg.meshtools.nodeDataToBoundaryData(mesh, vel) vmax = 0 if mesh.dimension() == 3: vmax = np.max(np.sqrt(vel[:, 0]**2 + vel[:, 1]**2 + vel[:, 2]**2)) else: vmax = np.max(np.sqrt(vel[:, 0]**2 + vel[:, 1]**2)) dt = times[1] - times[0] dx = min(mesh.boundarySizes()) c = vmax * dt / dx if c > 1: print( "Courant-Friedrichs-Lewy Number:", c, "but sould be lower 1 to ensure movement inside a cell " "per timestep. (" "vmax =", vmax, "dt =", dt, "dx =", dx, "dt <", dx / vmax, " | N > ", int((times[-1] - times[0]) / (dx / vmax)) + 1, ")") # END check for Courant-Friedrichs-Lewy if not hasattr(workspace, 'S'): if 'uB' in kwargs: boundsDirichlet = pg.solver.parseArgToBoundaries( kwargs['uB'], mesh) if 'duB' in kwargs: boundsNeumann = pg.solver.parseArgToBoundaries(kwargs['duB'], mesh) workspace.S, workspace.rhsBCScales = diffusionConvectionKernel( mesh=mesh, a=a, b=b, uB=boundsDirichlet, duB=boundsNeumann, # u0=u0, fn=fn, vel=vel, scheme=scheme, sparse=sparse, userData=kwargs.pop('userData', None)) dof = len(workspace.rhsBCScales) workspace.ap = np.zeros(dof) # for nonlinears if uL is not None: for i in range(dof): val = 0.0 if sparse: val = workspace.S.getVal(i, i) / relax workspace.S.setVal(i, i, val) else: val = workspace.S[i, i] / relax workspace.S[i, i] = val workspace.ap[i] = val # print('FVM kernel 2:', swatch.duration(True)) # endif: not hasattr(workspace, 'S'): workspace.rhs = np.zeros(len(workspace.rhsBCScales)) workspace.rhs[0:mesh.cellCount()] = f # * mesh.cellSizes() workspace.rhs += workspace.rhsBCScales # for nonlinear: relax progress with scaled last result if uL is not None: workspace.rhs += (1. - relax) * workspace.ap * uL # print('FVM: Prep:', swatch.duration(True)) if not hasattr(times, '__len__'): if sparse and not hasattr(workspace, 'solver'): Sm = pg.RSparseMatrix(workspace.S) # hold Sm until we have reference counting, # loosing Sm here will kill LinSolver later workspace.Sm = Sm workspace.solver = pg.LinSolver(Sm, verbose=verbose) u = None if sparse: u = workspace.solver.solve(workspace.rhs) else: u = np.linalg.solve(workspace.S, workspace.rhs) # print('FVM solve:', swatch.duration(True)) return u[0:mesh.cellCount():1] else: theta = kwargs.pop('theta', 0.5 + 1e-6) if sparse: I = pg.solver.identity(len(workspace.rhs)) else: I = np.diag(np.ones(len(workspace.rhs))) if verbose: print("Solve timesteps with Crank-Nicolson.") return pg.solver.crankNicolson(times, theta, workspace.S, I, f=workspace.rhs, u0=pg.solver.parseArgToArray( u0, mesh.cellCount(), mesh), verbose=verbose)
def solveFiniteVolume(mesh, a=1.0, f=0.0, fn=0.0, vel=0.0, u0=None, times=None, uL=None, relax=1.0, ws=None, scheme='CDS', **kwargs): """ """ # The Workspace is to hold temporary data or preserve matrix rebuild swatch = pg.Stopwatch(True) sparse = True workspace = WorkSpace() if ws: workspace = ws a = solver.parseArgToArray(a, [mesh.cellCount(), mesh.boundaryCount()]) f = solver.parseArgToArray(f, mesh.cellCount()) fn = solver.parseArgToArray(fn, mesh.cellCount()) boundsDirichlet = None boundsNeumann = None if not hasattr(workspace, 'S'): if 'uBoundary' in kwargs: boundsDirichlet = pg.solver.parseArgToBoundaries( kwargs['uBoundary'], mesh) if 'duBoundary' in kwargs: boundsNeumann = pg.solver.parseArgToBoundaries( kwargs['duBoundary'], mesh) workspace.S, workspace.rhsBCScales = diffusionConvectionKernel( mesh=mesh, a=a, f=f, uBoundaries=boundsDirichlet, duBoundaries=boundsNeumann, u0=u0, fn=fn, vel=vel, scheme=scheme, sparse=sparse, userData=kwargs.pop('userData', None)) print('FVM kernel 1:', swatch.duration(True)) dof = len(workspace.rhsBCScales) # workspace.uDir = np.zeros(dof) # if u0 is not None: # workspace.uDir = np.array(u0) # # if len(boundsDirichlet): # for boundary, val in boundsDirichlet.items(): # workspace.uDir[boundary.leftCell().id()] = val workspace.ap = np.zeros(dof) # for nonlinears if uL is not None: for i in range(dof): val = 0.0 if sparse: val = workspace.S.getVal(i, i) / relax workspace.S.setVal(i, i, val) # workspace.S[i, i] /= relax # workspace.ap[i] = workspace.S[i, i] else: val = workspace.S[i, i] / relax workspace.S[i, i] = val workspace.ap[i] = val print('FVM kernel 2:', swatch.duration(True)) # endif: not hasattr(workspace, 'S'): workspace.rhs = np.zeros(len(workspace.rhsBCScales)) workspace.rhs[0:mesh.cellCount()] = f # * mesh.cellSizes() # if len(workspace.uDir): workspace.rhs += workspace.rhsBCScales # for nonlinear: relax progress with scaled last result if uL is not None: workspace.rhs += (1. - relax) * workspace.ap * uL # print('FVM: Prep:', swatch.duration(True)) if not hasattr(times, '__len__'): if sparse and not hasattr(workspace, 'solver'): Sm = pg.RSparseMatrix(workspace.S) # hold Sm until we have reference counting, # loosing Sm here will kill LinSolver later workspace.Sm = Sm workspace.solver = pg.LinSolver(Sm, True) u = None if sparse: u = workspace.solver.solve(workspace.rhs) else: u = np.linalg.solve(workspace.S, workspace.rhs) print('FVM solve:', swatch.duration(True)) return u[0:mesh.cellCount():1] else: theta = kwargs.pop('theta', 0.5) verbose = kwargs.pop('verbose', False) if sparse: I = solver.identity(len(workspace.rhs)) else: I = np.diag(np.ones(len(workspace.rhs))) print("solve cN") return solver.crankNicolson(times, theta, workspace.S, I, f=workspace.rhs, u0=u0, verbose=verbose)