def createMeshPatches(ax, mesh, verbose=True, **kwargs): """Utility function to create 2d mesh patches within a given ax.""" if not mesh: print("drawMeshBoundaries(ax, mesh): invalid mesh") return if mesh.nodeCount() < 2: print("drawMeshBoundaries(ax, mesh): to few nodes") return swatch = pg.Stopwatch(True) if kwargs.pop('fitView', True): ax.set_xlim(mesh.xmin(), mesh.xmax()) ax.set_ylim(mesh.ymin(), mesh.ymax()) polys = [] for cell in mesh.cells(): if cell.shape().nodeCount() == 3: polys.append( list( zip([cell.node(0).x(), cell.node(1).x(), cell.node(2).x()], [cell.node(0).y(), cell.node(1).y(), cell.node(2).y()]))) elif cell.shape().nodeCount() == 4: polys.append( list( zip([ cell.node(0).x(), cell.node(1).x(), cell.node(2).x(), cell.node(3).x() ], [ cell.node(0).y(), cell.node(1).y(), cell.node(2).y(), cell.node(3).y() ]))) else: print(("unknown shape to patch: ", cell.shape(), cell.shape().nodeCount())) patches = mpl.collections.PolyCollection(polys, antialiaseds=False, picker=True) # ,lod=True # patches.set_edgecolor(None) patches.set_edgecolor('face') # patches.set_linewidth(1.001) ax.add_collection(patches) updateAxes_(ax) if verbose: print(("plotting time = ", swatch.duration(True))) return patches
def test_Performance(self): """ """ #pg.setDebug(True) sw = pg.Stopwatch(True) #print(timeit.repeat('r = grid.cellSizes() * np1', setup=setup, number=1000)) #print(timeit.repeat('r = c * np1', setup=setup, number=1000)) print( ("np(np)", timeit.repeat('np.array(np1)', setup=setup, number=5000))) print(("pg(pg)", timeit.repeat('pg.RVector(pg1)', setup=setup, number=5000))) print(("pg(np)", timeit.repeat('pg.RVector(np1)', setup=setup, number=5000))) print( ("np(pg)", timeit.repeat('np.array(pg1)', setup=setup, number=5000))) print(("np * np", timeit.repeat('np3 = np1 * np2', setup=setup, number=5000))) print(("pg * pg", timeit.repeat('pg3 = pg1 * pg2', setup=setup, number=5000))) print(("pg * np", timeit.repeat('pg1 * np1', setup=setup, number=5000))) print(("np * pg", timeit.repeat('np1 * pg1', setup=setup, number=5000))) print(("sum(np)", timeit.repeat('sum(np1)', setup=setup, number=300))) print(("sum(pg)", timeit.repeat('sum(pg1)', setup=setup, number=300))) print( ("pg.sum(pg)", timeit.repeat('pg.sum(pg1)', setup=setup, number=300))) print( ("pg.sum(st)", timeit.repeat('pg.sum(st)', setup=setup, number=300))) print(("s", sw.duration(True))) N = 10001 np1 = np.linspace(1.1, 1.2, N) np2 = np.linspace(2.1, 2.1, N) pg1 = pg.RVector(np1) pg2 = pg.RVector(np2) # print(sw.duration(True)) print((sum(np1 * np1))) print((sum(pg1 * pg1))) print((sum(np1 * pg1))) print((sum(pg1 * np1)))
def divergence(mesh, V, span=None): div = mesh.divergence(V) return div swatch = pg.Stopwatch(True) ret = np.zeros((mesh.cellCount(), 3)) for b in mesh.boundaries(): leftCell = b.leftCell() rightCell = b.rightCell() #flow = b.norm() * b.size() vec = mesh.boundarySizedNormals()[b.id()] * V[b.id()] #flow = mesh.boundaryFlow()[b.id()] if b.leftCell(): ret[leftCell.id(),:] += vec.array() #ret[leftCell.id(),:] += vec #ret[leftCell.id(), 0] += vec[0] #ret[leftCell.id(), 1] += vec[1] #ret[leftCell.id(), 2] += vec[2] #ret[leftCell.id()] += vec.array() #ret[leftCell.id()] += vec.array() if b.rightCell(): ret[rightCell.id(), :] -= vec.array() #ret[rightCell.id(), :] -= vec #ret[rightCell.id(), 0] -= vec[0] #ret[rightCell.id(), 1] -= vec[1] #ret[rightCell.id(), 2] -= vec[2] print(' C', swatch.duration(True)) ret[:,0] /= mesh.cellSizes() ret[:,1] /= mesh.cellSizes() ret[:,2] /= mesh.cellSizes() print(' D', swatch.duration(True)) if type(V[0]) == float: return ret return ret[:,0] + ret[:,1] +ret[:,2]
def appendTetrahedronBoundary(mesh, xbound=100, ybound=100, zbound=100, marker=1, quality=2, isSubSurface=False, verbose=False): """ Return new mesh surrounded by tetrahedron boundary box. Creates a tetrahedral box around a given mesh suitable for geo-simulation (surface boundary at top). Parameters ---------- mesh : mesh object Mesh to which the tetrahedron boundary should be appended. xbound : float, optional Horizontal prolongation distance in x-direction. ybound : float, optional Horizonal prolongation distance in y-direction. zbound : float, optional Vertical prolongation distance. marker : int, optional Marker of new cells. quality : float, optional Triangle quality. isSubSurface : boolean, optional Apply boundary conditions suitable for geo-simulaion and prolongate mesh to the surface if necessary. verbose : boolean, optional Be verbose. See Also -------- appendTriangleBoundary Notes ----- Boundaries of mesh need marker 1. """ # create boundary for mesh from boundary marker == 1 if isSubSurface: raise Exception('Implement me') meshBoundary = pg.Mesh() meshBoundary.createH2() bounds = [] for b in meshBoundary.boundaries(): if b.marker() == 1: bounds.append(b) meshBoundaryPoly = pg.Mesh() meshBoundaryPoly.createMeshByBoundaries( meshBoundary, meshBoundary.findBoundaryByMarker(1)) meshBoundaryPoly.exportAsTetgenPolyFile("paraBoundary.poly") # system( 'polyConvert -V paraBoundary' ) # create worldSurface.poly including boundary mesh for a nice surface mesh # it will be later the tetgen input with preserve boundary polyCreateWorld('worldSurface', x=xbound, y=ybound, depth=zbound, marker=1, verbose=verbose) os.system('polyMerge -N worldSurface paraBoundary worldSurface') polyAddVIP('worldSurface', mesh.cell(0).center(), isHoleMarker=True, verbose=verbose) worldBoundary = tetgen('worldSurface', quality=1.12, verbose=verbose) # worldBoundary.exportBoundaryVTU('worldSurface') worldPoly = pg.Mesh() worldPoly.createMeshByBoundaries(worldBoundary, worldBoundary.findBoundaryByMarker(-2, 0)) worldPoly.exportAsTetgenPolyFile("worldSurface.poly") os.system('polyMerge -N worldSurface paraBoundary boundaryWorld') # mesh should have to be a hole polyAddVIP('boundaryWorld', mesh.cell(0).center(), isHoleMarker=True, verbose=verbose) # system( 'polyConvert -o world-poly -V boundaryWorld' ) boundMesh = tetgen('boundaryWorld', quality=quality, preserveBoundary=True, verbose=verbose) # boundMesh.exportVTK( 'boundaryWorld' ) # merge mesh and worldBoundary for c in boundMesh.cells(): c.setMarker(marker) if verbose: print("merge grid and boundary") swatch = pg.Stopwatch(True) for c in meshBoundary.cells(): nodes = pg.stdVectorNodes() for n in c.nodes(): nodes.append(boundMesh.createNodeWithCheck(n.pos())) boundMesh.createCell(nodes, c.marker()) if verbose: print(" done.", swatch.duration(True)) try: os.remove('boundaryWorld.bms') os.remove('worldSurface.bms') os.remove('boundaryWorld.poly') os.remove('paraBoundary.poly') os.remove('worldSurface.poly') except BaseException as e: print(e) return boundMesh
def calcApparentResistivities(mesh, meshERT, poro, rhoBrine): ert = ERT(verbose=False) meshFOP = appendTriangleBoundary(meshERT, xbound=50, ybound=50, marker=1, quality=34.0, smooth=False, markerBoundary=1, isSubSurface=False, verbose=False) swatch = pg.Stopwatch(True) print("res 1:", swatch.duration(True)) resis = resistivityArchie(rBrine=rhoBrine, porosity=poro, S=1.0, mesh=mesh, meshI=meshFOP) print("res 2:", swatch.duration(True)) ertPointsX = [pg.RVector3(x, 0) for x in np.arange(-19, 19.1, 1)] ertScheme = ert.createData(ertPointsX, scheme="Dipole Dipole (CC-PP)") solutionName = createCacheName('appRes', mesh) + "-" + \ str(ertScheme.size()) + "-" + str(len(rhoBrine)) try: rhoa = np.load(solutionName + '.bmat.npy') ertData = pb.DataContainerERT(solutionName + '.dat') except Exception as e: print(e) print("Building .... ") rhoa = np.zeros((len(resis), ertScheme.size())) ertScheme.set('k', pb.geometricFactor(ertScheme)) ertData = ert.simulate(meshFOP, resis[0], ertScheme) errPerc = 1 errVolt = 1e-5 voltage = ertData('rhoa') / ertData('k') ertData.set('err', pg.abs(errVolt / voltage) + errPerc / 100.0) print('err min:', min(ertData('err')) * 100, 'max:', max(ertData('err')) * 100) ertData.save(solutionName + '.dat', 'a b m n rhoa err k') for i in range(0, len(resis)): tic = time.time() rhoa[i] = ert.fop.response(resis[i]) rand = pg.RVector(len(rhoa[i])) pg.randn(rand) rhoa[i] *= (1.0 + rand * ertData('err')) print(i, "/", len(resis), " : ", time.time() - tic, "s", "min:", min(resis[i]), "max:", max(resis[i]), "min:", min(rhoa[i]), "max:", max(rhoa[i])) np.save(solutionName + '.bmat', rhoa) return meshFOP, resis, ertData, rhoa
def calc(out, mesh, density, viscosity): print(mesh) velBoundary = [[1, [0.0, 'nan']], [2, [0.0, 'nan']], [3, ['nan', 0.0]], [4, ['nan', 0.0]]] preBoundary = [ [1, 0.0], [2, 0.0], [3, 0.0], [4, 0.0], ] densMatrix = pg.RMatrix() vels = [] swatch = pg.Stopwatch(True) class WS(): pass wsfv = WS() ax, _ = pg.show(mesh, density) v = 1 nSteps = 3000 dt = 0.1 * v dtSteps = 20 meshC = pg.createGrid(x=np.linspace(-10, 10, 21), y=np.linspace(0, 20, 21)) vel = None pre = None for i in range(nSteps): print(i, 'dens', min(density), max(density), "t:", dt * i) densMatrix.push_back(density) if v > 1: viscosity = 1.0 * density #v3 elif v < 1: viscosity = 1.0 / density #v3 else: viscosity = 1.0 vel, pre, pCNorm, divVNorm = solver.solveStokes( mesh, velBoundary=velBoundary, preBoundary=preBoundary, viscosity=viscosity, density=density, pre0=pre, vel0=vel, f=[density * 0, (density - 1.0) * -9.81], maxIter=1000, tol=1e-6, verbose=1, vRelax=0.1, pRelax=0.1, ws=wsfv) vels.append(vel) print("stokes:", swatch.duration(True), "div V: ", divVNorm[-1]) dens2 = solver.solveFiniteVolume( mesh, a=1. / 500, b=0.0, u0=density, vel=vel, times=np.linspace(0, dt, dtSteps), #uBoundary=[4, 0], scheme='PS', verbose=0) print("Convekt:", swatch.duration(True)) density = dens2[-1] ax.clear() pg.show(mesh, density, axes=ax) pg.show(mesh, vel, coarseMesh=meshC, axes=ax, color='white') mesh.save(out) meshC.save(out + 'C') densMatrix.save(out + 'density.bmat') np.save(out + 'velo.bmat', vels)
def divergence(mesh, F=None, normMap=None, order=1): """Divergence for callable function F((x,y,z)). MOVE THIS to a better place Divergence for callable function F((x,y,z)). Return sum div over boundary. Parameters ---------- Returns ------- """ if F is None: F = lambda r: r diverg = 0 directionCheck = False if mesh.cellCount() > 0: directionCheck = True bNorms = None if normMap is not None: bNorms = np.zeros((mesh.boundaryCount(), 2)) for pair in normMap: bounds = mesh.findBoundaryByMarker(pair[0]) for b in bounds: bN = [0.0, 0.0] if not isinstance(pair[1][0], str): bN[0] = pair[1][0] if not isinstance(pair[1][1], str): bN[1] = pair[1][1] bNorms[b.id()] = bN for b in mesh.boundaries(): if directionCheck: if b.leftCell() is None and b.rightCell() is None: # print(b.id(), b.leftCell(), b.rightCell()) sw = pg.Stopwatch(True) mesh.createNeighbourInfos() print("NeighbourInfos()", sw.duration(True)) # return gauss(grid, F) # don't calc for inner boundaries if b.leftCell() is not None and b.rightCell() is not None: continue tmpdiv = 0 shape = b.shape() if order == 1: if bNorms is not None: tmpdiv = shape.norm().dot(bNorms[b.id()]) * shape.domainSize() else: tmpdiv = shape.norm().dot(F( shape.center())) * shape.domainSize() else: weights = pg.IntegrationRules.instance().weights(shape, order) abscissa = pg.IntegrationRules.instance().abscissa(shape, order) for i, p in enumerate(abscissa): rPos = shape.xyz(p) tmpdiv += shape.norm().dot(F(rPos)) * \ weights[i] * shape.domainSize() if directionCheck and b.leftCell() is None: tmpdiv *= -1 # raise Exception("invalid mesh: left is None .. every # boundary need leftCell") diverg += tmpdiv return diverg
def crankNicolson(times, theta, S, I, f, u0=None, verbose=0): """ S = const over time f = const over time """ if len(times) < 2: raise BaseException("We need at least 2 times for Crank " "Nicolsen time discretization." + str(len(times))) sw = pg.Stopwatch(True) if u0 is None: u0 = np.zeros(len(f)) u = np.zeros((len(times), len(f))) u[0, :] = u0 dt = (times[1] - times[0]) rhs = np.zeros((len(times), len(f))) rhs[:] = f A = (I + dt * theta * S) solver = pg.LinSolver(A, verbose=verbose) timeAssemble = [] timeSolve = [] # print('0', min(u[0]), max(u[0]), min(f), max(f)) for n in range(1, len(times)): if verbose: pg.tic() # pg.tic() # bRef = (I + (dt * (theta - 1.)) * S) * u[n - 1] + \ # dt * ((1.0 - theta) * rhs[n - 1] + theta * rhs[n]) # pg.toc() # # pg.tic() # b = u[n - 1] + ((dt * (theta - 1.)) * S) * u[n - 1] + \ # dt * ((1.0 - theta) * rhs[n - 1] + theta * rhs[n]) # pg.toc() # # pg.tic() b = u[n - 1] + S.mult(dt * (theta - 1.) * u[n - 1]) + \ dt * ((1.0 - theta) * rhs[n - 1] + theta * rhs[n]) # pg.toc() # # print(np.linalg.norm(b-b1)) # np.testing.assert_allclose(bRef, b) if verbose: timeAssemble.append(pg.dur()) if verbose: pg.tic() u[n, :] = solver.solve(b) if verbose: timeSolve.append(pg.dur()) # A = (I + dt * theta * S) # u[n, : ] = linsolve(A, b) if verbose and (n % verbose == 0): # print(min(u[n]), max(u[n])) print("timesteps:", n, "/", len(times), 'runtime:', sw.duration(), "s", 'assemble:', np.mean(timeAssemble), 'solve:', np.mean(timeSolve)) # import matplotlib.pyplot as plt # plt.figure() # plt.plot(timeAssemble) # plt.figure() # plt.plot(timeSolve) # plt.show() return u
def drawShapes(ax, mesh, u): ''' ''' ax.set_aspect('equal') Nx = 21 Ny = 21 nLevels = 12 tix = np.linspace(-1.0, 1.0, Nx) tiy = np.linspace(-1.0, 1.0, Ny) X, Y = np.meshgrid(tix, tiy) uc = pg.RVector(len(X.flat)) c = mesh.cell(0) imax = pg.find(u == max(u))[0] for i in range(c.nodeCount()): print(c.rst(i)) print(imax) print(c.createShapeFunctions()[imax]) print("dx", c.createShapeFunctions()[imax].derive(0)) print("dy", c.createShapeFunctions()[imax].derive(1)) # draw nodes for i in range(c.nodeCount()): col = 'black' if i == imax: col = 'red' ax.plot(c.node(i).pos()[0], c.node(i).pos()[1], '.', markersize=12, linewidth=0, color=col) # draw boundary drawMeshBoundaries(ax, mesh) ptns = [] grads = [] swatch = pg.Stopwatch(True) for i, x in enumerate(X.flat): p = c.shape().xyz(pg.RVector3(X.flat[i], Y.flat[i])) X.flat[i] = p[0] Y.flat[i] = p[1] # ax.plot(p[0], p[1], '.', zorder=10, color='black', markersize = 1) if not c.shape().isInside(p): uc[i] = -99.0 continue uc[i] = c.pot(p, u) gr = c.grad(p, u) ptns.append(p) grads.append(gr) print(swatch.duration(True)) for i, p in enumerate(ptns): ax.quiver(p[0], p[1], grads[i][0], grads[i][1], zorder=10) Z = np.ma.masked_where(uc == -99., uc) Z = Z.reshape(Ny, Nx) ax.contourf(X, Y, Z, nLevels)
def _test_ConvectionAdvection(): """Test agains a refernce solution.""" N = 21 # 21 reference maxIter = 11 # 11 reference Nx = N Ny = N x = np.linspace(-1.0, 1.0, Nx + 1) y = np.linspace(-1.0, 1.0, Ny + 1) grid = pg.createGrid(x=x, y=y) a = pg.RVector(grid.cellCount(), 1.0) b7 = grid.findBoundaryByMarker(1)[0] for b in grid.findBoundaryByMarker(1): if b.center()[1] < b.center()[1]: b7 = b b7.setMarker(7) swatch = pg.Stopwatch(True) velBoundary = [[1, [0.0, 0.0]], [2, [0.0, 0.0]], [3, [1.0, 0.0]], [4, [0.0, 0.0]], [7, [0.0, 0.0]]] preBoundary = [[7, 0.0]] vel, pres, pCNorm, divVNorm = __solveStokes(grid, a, velBoundary, preBoundary, maxIter=maxIter, verbose=1) print("time", len(pCNorm), swatch.duration(True)) # referencesolution = 1.2889506342694153 referencesolutionDivV = 0.029187181920161752 print("divNorm: ", divVNorm[-1]) print("to reference: ", divVNorm[-1] - referencesolutionDivV) fig = plt.figure() ax1 = fig.add_subplot(1, 3, 1) ax2 = fig.add_subplot(1, 3, 2) ax3 = fig.add_subplot(1, 3, 3) show(grid, data=pg.meshtools.cellDataToNodeData(grid, pres), logScale=False, showLater=True, colorBar=True, ax=ax1, cbar='b2r') show(grid, data=pg.logTransDropTol( pg.meshtools.cellDataToNodeData(grid, vel[:, 0]), 1e-2), logScale=False, showLater=True, colorBar=True, ax=ax2) show(grid, data=pg.logTransDropTol( pg.meshtools.cellDataToNodeData(grid, vel[:, 1]), 1e-2), logScale=False, showLater=True, colorBar=True, ax=ax3) show(grid, data=vel, ax=ax1) show(grid, showLater=True, ax=ax1) plt.figure() plt.semilogy(pCNorm, label='norm') plt.legend() plt.ioff() plt.show()
def test2d(): mesh = pg.Mesh('mesh/world2d.bms') print(mesh) xMin = mesh.boundingBox().min()[0] yMax = mesh.boundingBox().max()[0] x = np.arange(xMin, yMax, 1.) mesh.createNeighbourInfos() rho = pg.RVector(len(mesh.cellAttributes()), 1.) * 2000.0 rho.setVal(0.0, pg.find(mesh.cellAttributes() == 1.0)) swatch = pg.Stopwatch(True) pnts = [] spnts = pg.stdVectorRVector3() for i in x: pnts.append(pg.RVector3(i, 0.0001)) spnts.append(pg.RVector3(i, 0.0001)) # gzC, GC = calcGCells(pnts, mesh, rho, 1) gzC = pg.calcGCells(spnts, mesh, rho, 1) print("calcGCells", swatch.duration(True)) # gzB, GB = calcGBounds(pnts, mesh, rho) # gzB = pg.calcGBounds(spnts, mesh, rho) # print("calcGBounds", swatch.duration(True)) gZ_Mesh = gzC ax1, ax2 = getaxes() # sphere analytical solution gAna = analyticalCircle2D(spnts, radius=2.0, pos=pg.RVector3(0.0, -5.0), dDensity=2000) gAna2 = analyticalCircle2D(spnts, radius=2.0, pos=pg.RVector3(5.0, -5.0), dDensity=2000) gAna = gAna + gAna2 ax1.plot(x, gAna, '-x', label='analytical') ax1.plot(x, gZ_Mesh, label='WonBevis1987-mesh') print(gAna / gZ_Mesh) # rho=GB[0]/mesh.cellSizes() drawModel(ax2, mesh, rho) for i in (0, 1): drawSelectedMeshBoundaries(ax2, mesh.findBoundaryByMarker(i), color=(1.0, 1.0, 1.0, 1.0), linewidth=0.3) # sphere polygone radius = 2. depth = 5. poly1 = pg.stdVectorRVector3() poly2 = pg.stdVectorRVector3() nSegment = 124 for i in range(nSegment): xp = np.sin((i + 1) * (2. * np.pi) / nSegment) yp = np.cos((i + 1) * (2. * np.pi) / nSegment) poly1.append(pg.RVector3(xp * radius, yp * radius - depth)) poly2.append(pg.RVector3(xp * radius + 5., yp * radius - depth)) gZ_Poly = calcPolydgdz(spnts, poly1, 2000) gZ_Poly += calcPolydgdz(spnts, poly2, 2000) ax1.plot(x, gZ_Poly, label='WonBevis1987-Poly') ax2.plot(pg.x(poly1), pg.y(poly1), color='red') ax2.plot(pg.x(poly2), pg.y(poly2), color='red') ax2.plot(pg.x(spnts), pg.y(spnts), marker='x', color='black') # test some special case for i, p in enumerate(poly1): poly1[i] = pg.RVector3(poly1[i] - pg.RVector3(5.0, -6.)) ax2.plot(pg.x(poly1), pg.y(poly1), color='green') gz = calcPolydgdz(spnts, poly1, 2000) ax1.plot(x, gz, label='Special Case', color='green') ax1.set_ylabel('dg/dz [mGal]') ax2.set_ylabel('Tiefe [m]') ax1.legend() ax2.set_xlim([x[0], x[-1]]) ax2.grid()
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)
def diffusionConvectionKernel(mesh, a=None, f=None, uBoundaries=None, duBoundaries=None, fn=None, vel=0, u0=0, scheme='CDS', sparse=False, time=0.0, userData=None): """ Peclet Number - ratio between convection/diffusion * Length Advection .. forced convection """ if a is None: a = pg.RVector(mesh.boundaryCount(), 1.0) AScheme = None if scheme == 'CDS': # CDS - central differences scheme .. maybe irregular for Peclet-number |F/D| > 2 # diffusion dominant # Error of order 2 AScheme = lambda peclet_: 1.0 - 0.5 * abs(peclet_) elif scheme == 'UDS': # UDS - upwind scheme # Convection dominant # Error of order 1 AScheme = lambda peclet_: 1.0 elif scheme == 'HS': # HS - hybrid scheme. # Diffusion dominant for Peclet-number |(F/D)| < 2 # Convection dominant else AScheme = lambda peclet_: max(0.0, 1.0 - 0.5 * abs(peclet_)) elif scheme == 'PS': # PS - power-law scheme. # Identical to HS for Peclet-number |(F/D)| > 10 and near to ES else AScheme = lambda peclet_: max(0.0, (1.0 - 0.1 * abs(peclet_))**5.0) elif scheme == 'ES': # ES - exponential scheme # Only stationary one-dimensional but exact solution AScheme = lambda peclet_: (peclet_) / (np.exp(abs(peclet_))-1.0) \ if peclet_ != 0.0 else 1 else: raise useHalfBoundaries = False dof = mesh.cellCount() if not uBoundaries: uBoundaries = [] if not duBoundaries: duBoundaries = [] if useHalfBoundaries: dof = mesh.cellCount() + len(uBoundaries) S = None if sparse: S = pg.RSparseMapMatrix(dof, dof, 0) else: S = np.zeros((dof, dof)) rhsBoundaryScales = np.zeros(dof) swatch = pg.Stopwatch(True) # we need this to fast identify uBoundary and value by boundary uBoundaryID = [] uBoundaryVals = [None] * mesh.boundaryCount() for i, [boundary, val] in enumerate(uBoundaries): if not isinstance(boundary, pg.Boundary): raise BaseException("Please give boundary, value list") uBoundaryID.append(boundary.id()) uBoundaryVals[boundary.id()] = val duBoundaryID = [] duBoundaryVals = [None] * mesh.boundaryCount() for i, [boundary, val] in enumerate(duBoundaries): if not isinstance(boundary, pg.Boundary): raise BaseException("Please give boundary, value list") duBoundaryID.append(boundary.id()) duBoundaryVals[boundary.id()] = val for cell in mesh.cells(): for bi in range(cell.boundaryCount()): boundary = pg.findBoundary(cell.boundaryNodes(bi)) ncell = boundary.leftCell() if ncell == cell: ncell = boundary.rightCell() v = findVelocity(mesh, vel, boundary, cell, ncell) # Convection part F = boundary.norm(cell).dot(v) * boundary.size() # Diffusion part D = findDiffusion(mesh, a, boundary, cell, ncell) aB = D * AScheme(F / D) + max(-F, 0.0) aB /= cell.size() #print(cell.center(), boundary.center(), boundary.norm(cell), aB) if ncell: # no boundary if sparse: S.addVal(cell.id(), ncell.id(), -aB) S.addVal(cell.id(), cell.id(), +aB) else: S[cell.id(), ncell.id()] -= aB S[cell.id(), cell.id()] += aB elif not useHalfBoundaries: if boundary.id() in uBoundaryID: val = pg.solver.generateBoundaryValue(boundary, uBoundaryVals[boundary.id()], time=time, userData=userData) if sparse: S.addVal(cell.id(), cell.id(), aB) else: S[cell.id(), cell.id()] += aB rhsBoundaryScales[cell.id()] += aB * val if boundary.id() in duBoundaryID: # Neumann boundary condition val = pg.solver.generateBoundaryValue(boundary, duBoundaryVals[boundary.id()], time=time, userData=userData) if sparse: # amount of flow through the boundary S.addVal(cell.id(), cell.id(), val * boundary.size()/cell.size()) else: S[cell.id(), cell.id()] += val* boundary.size()/cell.size() if fn != None: if sparse: S.addVal(cell.id(), cell.id(), -fn[cell.id()]) #* cell.shape().domainSize()) else: S[cell.id(), cell.id()] -= fn[cell.id()] #* cell.shape().domainSize() if useHalfBoundaries: for i, [b, val] in enumerate(uDirBounds): bIdx = mesh.cellCount() + i c = b.leftCell() if not c: c = b.rightCell() if c: n = b.norm(c) v = findVelocity(mesh, vel, b, c, nc=None) F = n.dot(v) * b.size() D = findDiffusion(mesh, a, b, c) aB = D * AScheme(F / D) + max(-F, 0.0) if useHalfBoundaries: if sparse: S.setVal(c.id(), c.id(), 1.) S.addVal(c.id(), bIdx, -aB) else: S[bIdx, bIdx] = 1. S[c.id(), bIdx] -= aB rhsBoundaryScales[bIdx] = aB return S, rhsBoundaryScales
.. math :: \vector{v}\cdot\nabla u = \frac{1}{P}\delta u """ import pygimli as pg import pygimli.solver as solver from pygimli.viewer import show, showMesh from pygimli.mplviewer import drawMesh, drawModel, drawField, drawStreams from pygimli.meshtools import createMesh from solverFVM import solveFiniteVolume, createFVPostProzessMesh, diffusionConvectionKernel import matplotlib.pyplot as plt import numpy as np swatch = pg.Stopwatch(True) x = np.linspace(-1.0, 1.0, 41) y = np.linspace( 0.0, 1.0, 21) dx = x[1] - x[0] dy = y[1] - y[0] print(dx,dy) grid = pg.createGrid(x=x, y=y) # force vector per cell f = pg.RVector(grid.cellCount(), 0.0) #f[grid.findCell([-0.85, 0.55]).id()]=10.0 # velocity per cell [x-direction, y-direction] vC = np.array(list(map(lambda p_: [ (2.*p_[1] *(1.0 -p_[0]**2)),
def solveFiniteElements(mesh, a=1.0, b=0.0, f=0.0, times=None, userData=None, verbose=False, stats=None, **kwargs): r"""Solve partial differential equation with Finite Elements. This function is a syntactic sugar proxy for using the Finite Element functionality of the library core to solve elliptic and parabolic partial differential of the following type: .. math:: \frac{\partial u}{\partial t} & = \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\quad\text{for}\quad t\neq 0 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 node in mesh. TODO: * unsteady ub and dub 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) Cell values u0 : value | array | callable(pos, userData) Node values uB : value | array | callable(pos, userData) Dirichlet values for u at the boundary uN : list([node, value]) Dirichlet values for u at given nodes duB : value | array | callable(pos, userData) Neumann values for du/dn at the boundary f : value | array(cells) | array(nodes) | callable(args, kwargs) force values times : array [None] Solve as time dependent problem for the given times. theta : float [1] - :math:`theta = 0` means explicit Euler, maybe stable for :math:`\Delta t \quad\text{near}\quad h` - :math:`theta = 0.5`, Crank-Nicolsen, maybe instable - :math:`theta = 1`, implicit Euler If unsure choose :math:`\theta = 0.5 + \epsilon`, which is probably stable. progress : bool Give some calculation progress. ret : Workspace for results so no new memory will be allocated. Returns ------- u : array Returns the solution u either 1,n array for stationary problems or for m,n array for m time steps Examples -------- >>> import pygimli as pg >>> from pygimli.meshtools import polytools as plc >>> from pygimli.mplviewer import drawField, drawMesh >>> import matplotlib.pyplot as plt >>> world = plc.createWorld(start=[-10, 0], end=[10, -10], ... marker=1, worldMarker=False) >>> c1 = plc.createCircle(pos=[0.0, -5.0], radius=3.0, area=.1, marker=2) >>> mesh = pg.meshtools.createMesh([world, c1], quality=34.3) >>> u = pg.solver.solveFiniteElements(mesh, a=[[1, 100], [2, 1]], ... uB=[[4, 1.0], [2, 0.0]]) >>> fig, ax = plt.subplots() >>> pc = drawField(ax, mesh, u) >>> drawMesh(ax, mesh) >>> plt.show() See Also -------- other solver TODO """ if 'uDirichlet' in kwargs or 'uBoundary' in kwargs: raise BaseException("use uB instead") if 'uBoundary' in kwargs: raise BaseException("use duB instead") debug = kwargs.pop('debug', False) mesh.createNeighbourInfos() if verbose: print("Mesh: ", str(mesh)) dof = mesh.nodeCount() swatch = pg.Stopwatch(True) swatch2 = pg.Stopwatch(True) # check for material parameter a = parseArgToArray(a, ndof=mesh.cellCount(), mesh=mesh, userData=userData) b = parseArgToArray(b, ndof=mesh.cellCount(), mesh=mesh, userData=userData) if debug: print("2: ", swatch2.duration(True)) # assemble the stiffness matrix A = createStiffnessMatrix(mesh, a) if debug: print("3: ", swatch2.duration(True)) M = createMassMatrix(mesh, b) if debug: print("4: ", swatch2.duration(True)) S = A + M if debug: print("5: ", swatch2.duration(True)) if times is None: rhs = assembleForceVector(mesh, f, userData=userData) if debug: print("6a: ", swatch2.duration(True)) if 'duB' in kwargs: assembleNeumannBC(S, parseArgToBoundaries(kwargs['duB'], mesh), time=0.0, userData=userData) if debug: print("6b: ", swatch2.duration(True)) if 'uB' in kwargs: assembleDirichletBC(S, parseArgToBoundaries(kwargs['uB'], mesh), rhs, time=0.0, userData=userData) if 'uN' in kwargs: assembleDirichletBC(S, [], nodePairs=kwargs['uN'], rhs=rhs, time=0.0, userData=userData) if debug: print("6c: ", swatch2.duration(True)) # create result array u = kwargs.pop('ret', None) singleForce = True if hasattr(rhs, 'ndim'): if rhs.ndim == 2: singleForce = False if u is None: u = np.zeros(rhs.shape) else: if isinstance(a[0], complex): if u is None: u = pg.CVector(rhs.size(), 0.0) rhs = pg.toComplex(rhs) else: if u is None: u = pg.RVector(rhs.size(), 0.0) assembleTime = swatch.duration(True) if stats: stats.assembleTime = assembleTime if verbose: print(("Asssemblation time: ", assembleTime)) # showSparseMatrix(S) solver = pg.LinSolver(False) solver.setMatrix(S, 0) if singleForce: u = solver.solve(rhs) else: for i, r in enumerate(rhs): solver.solve(r, u[i]) solverTime = swatch.duration(True) if verbose: if stats: stats.solverTime = solverTime print(("Solving time: ", solverTime)) return u else: if debug: print("start TL", swatch.duration()) M = createMassMatrix(mesh) F = assembleForceVector(mesh, f) if 'u0' in kwargs: u0 = parseArgToArray(kwargs['u0'], dof, mesh, userData) theta = kwargs.pop('theta', 1) if 'duB' not in kwargs: A = createStiffnessMatrix(mesh, a) if 'uB' in kwargs: assembleDirichletBC(A, parseArgToBoundaries(kwargs['uB'], mesh), rhs=F) if 'uN' in kwargs: assembleDirichletBC(A, [], nodePairs=kwargs['uN'], rhs=F) return crankNicolson(times, theta, A, M, F, u0=u0, verbose=verbose) rhs = np.zeros((len(times), dof)) # rhs kann zeitabhängig sein ..wird hier nicht berücksichtigt rhs[:] = F # this is slow: optimize if debug: print("rhs", swatch.duration()) U = np.zeros((len(times), dof)) U[0, :] = u0 # init state u = pg.RVector(dof, 0.0) if debug: print("u0", swatch.duration()) measure = 0. for n in range(1, len(times)): swatch.reset() dt = times[n] - times[n - 1] # previous timestep # print "i: ", i, dt, U[i - 1] if 'duB' in kwargs: # aufschreiben und checken ob neumann auf A oder auf S mit # skaliertem val*dt angewendet wird A = createStiffnessMatrix(mesh, a) assembleNeumannBC(A, parseArgToBoundaries(kwargs['duB'], mesh), time=times[n], userData=userData) swatch.reset() # (A + a*B)u is fastest, # followed by A*u + (B*u)*a and finally A*u + a*B*u and b = (M + (dt * (theta - 1.)) * A) * U[n - 1] + \ dt * ((1.0 - theta) * rhs[n - 1] + theta * rhs[n]) # print ('a',swatch.duration(True)) # b = M * U[n - 1] - (A * U[n - 1]) * (dt*(1.0 - theta)) + \ # dt * ((1.0 - theta) * rhs[n - 1] + theta * rhs[n]) # print ('b',swatch.duration(True)) # b = M * U[n - 1] - (dt*(1.0 - theta)) * A * U[n - 1] + \ # dt * ((1.0 - theta) * rhs[n - 1] + theta * rhs[n]) # print ('c',swatch.duration(True)) measure += swatch.duration() S = M + A * dt * theta if 'uB' in kwargs: assembleDirichletBC(S, parseArgToBoundaries(kwargs['uB'], mesh), rhs=b, time=times[n], userData=userData) if 'uN' in kwargs: assembleDirichletBC(S, [], nodePairs=kwargs['uN'], rhs=b, time=times[n], userData=userData) # u = S/b t_prep = swatch.duration(True) solver = pg.LinSolver(S, verbose) solver.solve(b, u) if 'plotTimeStep' in kwargs: kwargs['plotTimeStep'](u, times[n]) U[n, :] = np.asarray(u) if 'progress' in kwargs: if kwargs['progress']: print(("\t" + str(n) + "/" + str(len(times) - 1) + ": " + str(t_prep) + "/" + str(swatch.duration()))) if debug: print("Measure(" + str(len(times)) + "): ", measure, measure / len(times)) return U
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 crankNicolson(times, theta, S, I, f, u0=None, progress=None, debug=None): """ S = constant over time f = constant over time """ if len(times) < 2: raise BaseException("We need at least 2 times for Crank " "Nicolsen time discretization." + str(len(times))) sw = pg.Stopwatch(True) if u0 is None: u0 = np.zeros(len(f)) u = np.zeros((len(times), len(f))) u[0, :] = u0 dt = times[1] - times[0] rhs = np.zeros((len(times), len(f))) rhs[:] = f A = I + S * dt * theta solver = pg.LinSolver(A, verbose=False) timeAssemble = [] timeSolve = [] # print('0', min(u[0]), max(u[0]), min(f), max(f)) timeMeasure = False if progress: timeMeasure = True for n in range(1, len(times)): if timeMeasure: pg.tic() # pg.tic() #bRef = (I + (dt * (theta - 1.)) * S) * u[n - 1] + \ #dt * ((1.0 - theta) * rhs[n - 1] + theta * rhs[n]) # pg.toc() # # pg.tic() #b = I * u[n - 1] + ((dt * (theta - 1.)) * S) * u[n - 1] + \ #dt * ((1.0 - theta) * rhs[n - 1] + theta * rhs[n]) # pg.toc() # # pg.tic() b = I * u[n - 1] + S.mult(dt * (theta - 1.) * u[n - 1]) + \ dt * ((1.0 - theta) * rhs[n - 1] + theta * rhs[n]) # pg.toc() # # print(np.linalg.norm(b-b1)) #np.testing.assert_allclose(bRef, b) if timeMeasure: timeAssemble.append(pg.dur()) if timeMeasure: pg.tic() u[n, :] = solver.solve(b) if timeMeasure: timeSolve.append(pg.dur()) # A = (I + dt * theta * S) # u[n, : ] = linsolve(A, b) if progress: progress.update(n, ' t_prep: ' + str(round(timeAssemble[-1], 5)) + 's' + \ ' t_step: ' + str(round(timeSolve[-1], 5)) + 's') #if verbose and (n % verbose == 0): ## print(min(u[n]), max(u[n])) #print("timesteps:", n, "/", len(times), #'runtime:', sw.duration(), "s", #'assemble:', np.mean(timeAssemble), #'solve:', np.mean(timeSolve)) return u