def divergenceCell(c, F): ret = 0 for bi in range(c.boundaryCount()): b = pg.findBoundary(c.boundaryNodes(bi)) # print(b.norm(c).dot(F[b.id()])) ret += b.norm(c).dot(F[b.id()]) * b.size() return ret
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.Vector(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.matrix.SparseMapMatrix(dof, dof, 0) else: S = np.zeros((dof, dof)) rhsBoundaryScales = np.zeros(dof) # 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.core.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.core.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 is not 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): # not defined! 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
def _trace_back(self, sensor_idx, source_idx, epsilon=1e-5): """ Traces a ray backwards through the mesh from a particular sensor towards the seismic source. """ msh = self.mesh() self.poslist = [] self._jac[source_idx] = np.zeros((msh.cellCount())) pos_offset = pg.RVector3(0., epsilon, 0.) sensor_pos = self.data().sensorPosition(sensor_idx) source_pos = self.data().sensorPosition(source_idx) source_node = msh.findNearestNode(source_pos) current_cell = msh.findCell(sensor_pos - pos_offset) new_cell = current_cell ray_origin = sensor_pos - pos_offset was_on_edge = False while ray_origin.dist(source_pos) > epsilon: self.poslist.append(ray_origin) if new_cell is None: print("Ended up outside mesh!") print("Last valid cell: {}".format(current_cell)) break # other_boundary = pg.findBoundary( # current_cell.node((node_idx+2)%nnodes), # current_cell.node((node_idx+1)%nnodes)) # new_cell = self._get_new_cell(other_boundary, current_cell) # gradient = current_cell.node((node_idx+1)%nnodes).pos() - # current_cell.node(node_idx).pos() else: old_cell_id = current_cell.id() # going to slower cell # if new_cell.attribute() > current_cell.attribute(): # gradient = current_cell.grad(current_cell.center(), # self.timefields[source_idx]) # else: # gradient = new_cell.grad(current_cell.center(), # self.timefields[source_idx]) current_cell = new_cell if not was_on_edge: gradient = current_cell.grad(current_cell.center(), self.timefields[source_idx]) else: was_on_edge = False print("Current cell: {}".format(current_cell.id())) # gradient = current_cell.grad(current_cell.center(), # self.timefields[source_idx]) # gradient_norm = -gradient / gradient.length() gradient_norm = -gradient.norm() nnodes = current_cell.nodeCount() params = np.zeros((nnodes, 2)) gradient_line = pg.Line(ray_origin, ray_origin + gradient_norm) for i in range(nnodes): if current_cell.node(i).id() == source_node: print("cell closest to source") params[i, :] = [ray_origin.dist(source_pos), i] break edge = pg.Line( current_cell.node(i).pos(), current_cell.node((i + 1) % nnodes).pos()) # print("Grad: {}".format(gradient_line)) # print("Edge: {}".format(edge)) s_t = self._intersect_lines(gradient_line, edge) # print("s_t: {}".format(s_t)) params[i, :] = [s_t[0], i] t, node_idx, stay_on_edge = self._check_param(params) print("Stay on edge: {}".format(stay_on_edge)) boundary = pg.findBoundary( current_cell.node(node_idx), current_cell.node((node_idx + 1) % nnodes)) if stay_on_edge: # break next_node_id, next_cell_id = self._get_next_node( boundary, current_cell.id(), ray_origin, gradient_norm) t = ray_origin.dist(msh.node(next_node_id).pos()) print("Current: {}, next: {}, t: {}".format( current_cell.id(), next_cell_id, t)) print("") self._jac[source_idx][next_cell_id] += t temp = msh.node(next_node_id).pos() - ray_origin ray_origin = msh.node(next_node_id).pos() + \ 1e-5 * temp.norm() - pg.RVector3(0.0, 1e-6, 0.0) # new_cell = mesh.cell(next_cell_id) new_cell = msh.findCell(ray_origin) was_on_edge = True # print("next_cell_id: {}, findCell: {}".format( # next_cell_id, new_cell.id())) else: # print("params: {}, t: {}, i: {}".format(params, t, node_idx)) # Save distance travelled in the cell (t) and update origin self._jac[source_idx][current_cell.id()] = t ray_origin = gradient_line.lineAt(t) # print("ray origin: {}".format(ray_origin)) new_cell = self._get_new_cell(boundary, current_cell) if new_cell.id() == old_cell_id: # If we keep jumping back and forth between two cells. print("Jumping back and forth...") break return self._jac
def fastMarch( mesh, downwind, times, upTags, downTags ): def findSlowness( edge ): if edge.leftCell() is None: slowness = edge.rightCell().attribute() elif edge.rightCell() is None: slowness = edge.leftCell().attribute() else: slowness = min( edge.leftCell().attribute(), edge.rightCell().attribute() ) return slowness # def findSlowness( ... ) upCandidate = [] for node in downwind: neighNodes = pg.commonNodes( node.cellSet() ) upNodes = [] for n in neighNodes: if upTags[ n.id() ]: upNodes.append( n ) if len( upNodes ) == 1: # this is the dijkstra case edge = pg.findBoundary( upNodes[0], node ) tt = times[ upNodes[0].id() ] + findSlowness( edge ) * edge.shape().domainSize() heapq.heappush( upCandidate, (tt, node) ) else: cells = node.cellSet() for c in cells: for i in range( c.nodeCount() ): edge = pg.findBoundary( c.node( i ), c.node( (i + 1 )%3 ) ) a = edge.node( 0 ) b = edge.node( 1 ) ta = times[ a.id() ] tb = times[ b.id() ] if upTags[ a.id() ] and upTags[ b.id() ]: line = pg.Line( a.pos(), b.pos() ) t = min( 1., max( 0., line.nearest( node.pos() ) ) ) ea = pg.findBoundary( a, node ) eb = pg.findBoundary( b, node ) if t == 0: slowness = findSlowness( ea ) elif t == 1: slowness = findSlowness( eb ) else: slowness = c.attribute() ttimeA = ( ta + slowness * a.pos().distance( node.pos() ) ) ttimeQ = ( ta + t*(tb-ta) ) + slowness * line( t ).distance( node.pos() ) ttimeB = ( tb + slowness * b.pos().distance( node.pos() ) ) heapq.heappush( upCandidate, (min(ttimeA,ttimeQ,ttimeB), node ) ) #for c in upCandidate: #print c[1].id(), c[0] candidate = heapq.heappop( upCandidate ) #print candidate newUpNode = candidate[1] times[ newUpNode.id() ] = candidate[0] upTags[ newUpNode.id() ] = 1 #print newUpNode downwind.remove( newUpNode ) newDownNodes = pg.commonNodes( newUpNode.cellSet() ) for nn in newDownNodes: if not upTags[ nn.id() ] and not downTags[ nn.id() ]: downwind.add( nn ) downTags[ nn.id() ] = 1
def fastMarch(mesh, downwind, times, upT, downT): """Do one front marching.""" upCandidate = [] for node in downwind: neighNodes = pg.commonNodes(node.cellSet()) upNodes = [] for n in neighNodes: if upT[n.id()]: upNodes.append(n) if len(upNodes) == 1: # the Dijkstra case edge = pg.findBoundary(upNodes[0], node) if edge is None: continue raise StandardError("no edge found") tt = times[upNodes[0].id()] + \ findSlowness(edge) * edge.shape().domainSize() # use node id additionally in case of equal travel times heapq.heappush(upCandidate, (tt, node.id(), node)) else: cells = node.cellSet() for c in cells: for i in range(c.nodeCount()): edge = pg.findBoundary(c.node(i), c.node((i + 1) % c.nodeCount())) a = edge.node(0) b = edge.node(1) ta = times[a.id()] tb = times[b.id()] if upT[a.id()] and upT[b.id()]: line = pg.Line(a.pos(), b.pos()) t = min(1., max(0., line.nearest(node.pos()))) ea = pg.findBoundary(a, node) eb = pg.findBoundary(b, node) #if ea is None or eb is None: #print(a, b, node) if t == 0: slowness = findSlowness(ea) elif t == 1: slowness = findSlowness(eb) else: slowness = c.attribute() ttimeA = (ta + slowness * a.pos().distance(node.pos())) ttimeQ = (ta + t * (tb - ta)) + \ slowness * line(t).distance(node.pos()) ttimeB = (tb + slowness * b.pos().distance(node.pos())) tmin = min(ttimeA, ttimeQ, ttimeB) heapq.heappush(upCandidate, (tmin, node.id(), node)) candidate = heapq.heappop(upCandidate) newUpNode = candidate[2] # original times[newUpNode.id()] = candidate[0] upT[newUpNode.id()] = 1 downwind.remove(newUpNode) newDownNodes = pg.commonNodes(newUpNode.cellSet()) # newUpNodeId = candidate[1] # original # times[newUpNodeId] = candidate[0] # upT[newUpNodeId] = 1 # downwind.remove(newUpNodeId) # newDownNodes = pg.commonNodes(mesh.node(newUpNodeId).cellSet()) for nn in newDownNodes: if not upT[nn.id()] and not downT[nn.id()]: downwind.add(nn) downT[nn.id()] = 1
def calcSeismics(meshIn, vP): """Do seismic computations.""" meshSeis = meshIn.createH2() meshSeis = mt.appendTriangleBoundary(meshSeis, xbound=25, ybound=22.0, marker=1, quality=32.0, area=0.3, smooth=True, markerBoundary=1, isSubSurface=False, verbose=False) print(meshSeis) meshSeis = meshSeis.createH2() meshSeis = meshSeis.createH2() # meshSeis = meshSeis.createP2() meshSeis.smooth(1, 1, 1, 4) vP = pg.interpolate(meshIn, vP, meshSeis.cellCenters()) mesh = meshSeis vP = pg.solver.fillEmptyToCellArray(mesh, vP) print(mesh) # ax, cbar = pg.show(mesh, data=vP) # pg.show(mesh, axes=ax) geophPointsX = np.arange(-19, 19.1, 1) geophPoints = np.vstack((geophPointsX, np.zeros(len(geophPointsX)))).T sourcePos = geophPoints[4] c = mesh.findCell(sourcePos) h1 = pg.findBoundary(c.boundaryNodes(0)).size() h2 = pg.findBoundary(c.boundaryNodes(1)).size() h3 = pg.findBoundary(c.boundaryNodes(2)).size() print([h1, h2, h3]) h = pg.math.median([h1, h2, h3]) # h = pg.math.median(mesh.boundarySizes()) f0scale = 0.25 cfl = 0.5 dt = cfl * h / max(vP) print("Courant-Friedrich-Lewy number:", cfl) tmax = 40. / min(vP) times = np.arange(0.0, tmax, dt) solutionName = createCacheName('seis', mesh, times) + "cfl-" + str(cfl) try: # u = pg.load(solutionName + '.bmat') uI = pg.load(solutionName + 'I.bmat') except Exception as e: print(e) f0 = f0scale * 1. / dt print("h:", round(h, 2), "dt:", round(dt, 5), "1/dt:", round(1 / dt, 1), "f0", round(f0, 2), "Wavelength: ", round(max(vP) / f0, 2), " m") uSource = ricker(times, f0, t0=1. / f0) plt.figure() plt.plot(times, uSource, '-*') plt.show(block=0) plt.pause(0.01) u = solvePressureWave(mesh, vP, times, sourcePos=sourcePos, uSource=uSource, verbose=10) u.save(solutionName) uI = pg.Matrix() print("interpolate node to cell data ... ") pg.interpolate(mesh, u, mesh.cellCenters(), uI) print("... done") uI.save(solutionName + 'I') # nodes = [mesh.findNearestNode(p) for p in geophPoints] # fig = plt.figure() # axs = fig.add_subplot(1,1,1) # drawSeismogramm(axs, mesh, u, nodes, dt, i=None) # plt.show() dpi = 92 scale = 1 fig = plt.figure(facecolor='white', figsize=(scale * 800 / dpi, scale * 490 / dpi), dpi=dpi) ax = fig.add_subplot(1, 1, 1) gci = pg.viewer.mpl.drawModel(ax, mesh, data=uI[0], cMin=-1, cMax=1, cmap='bwr') pg.viewer.mpl.drawMeshBoundaries(ax, meshIn, hideMesh=1) ax.set_xlim((-20, 20)) ax.set_ylim((-15, 0)) ax.set_ylabel('Depth [m]') ax.set_xlabel('$x$ [m]') ticks = ax.yaxis.get_majorticklocs() tickLabels = [] for t in ticks: tickLabels.append(str(int(abs(t)))) ax.set_yticklabels(tickLabels) plt.tight_layout() # ax, cbar = pg.show(mesh, data=vP) # pg.showNow() # ax = fig.add_subplot(1,1,1) def animate(i): i = i * 5 if i > len(uI) - 1: return print("Frame:", i, "/", len(uI)) ui = uI[i] ui = ui / max(pg.abs(ui)) ui = pg.logDropTol(ui, 1e-2) cMax = max(pg.abs(ui)) pg.viewer.mpl.setMappableData(gci, ui, cMin=-cMax, cMax=cMax, logScale=False) # plt.pause(0.001) anim = animation.FuncAnimation(fig, animate, frames=int(len(uI) / 5), interval=0.001, repeat=0) # , blit=True) out = 'seis' + str(f0scale) + "cfl-" + str(cfl) anim.save(out + ".mp4", writer=None, fps=20, dpi=dpi, codec=None, bitrate=24 * 1024, extra_args=None, metadata=None, extra_anim=None, savefig_kwargs=None) try: print("create frames ... ") os.system('mkdir -p anim-' + out) os.system('ffmpeg -i ' + out + '.mp4 anim-' + out + '/movie%d.jpg') except: pass
def _trace_back(self, sensor_idx, source_idx, epsilon=1e-5): """ Traces a ray backwards through the mesh from a particular sensor towards the seismic source. """ msh = self.mesh() self.poslist = [] self._jac[source_idx] = np.zeros((msh.cellCount())) pos_offset = pg.RVector3(0., epsilon, 0.) sensor_pos = self.data().sensorPosition(sensor_idx) source_pos = self.data().sensorPosition(source_idx) source_node = msh.findNearestNode(source_pos) current_cell = msh.findCell(sensor_pos - pos_offset) new_cell = current_cell ray_origin = sensor_pos - pos_offset was_on_edge = False while ray_origin.dist(source_pos) > epsilon: self.poslist.append(ray_origin) if new_cell is None: print("Ended up outside mesh!") print("Last valid cell: {}".format(current_cell)) break # other_boundary = pg.findBoundary( # current_cell.node((node_idx+2)%nnodes), # current_cell.node((node_idx+1)%nnodes)) # new_cell = self._get_new_cell(other_boundary, current_cell) # gradient = current_cell.node((node_idx+1)%nnodes).pos() - # current_cell.node(node_idx).pos() else: old_cell_id = current_cell.id() # going to slower cell # if new_cell.attribute() > current_cell.attribute(): # gradient = current_cell.grad(current_cell.center(), # self.timefields[source_idx]) # else: # gradient = new_cell.grad(current_cell.center(), # self.timefields[source_idx]) current_cell = new_cell if not was_on_edge: gradient = current_cell.grad( current_cell.center(), self.timefields[source_idx]) else: was_on_edge = False print("Current cell: {}".format(current_cell.id())) # gradient = current_cell.grad(current_cell.center(), # self.timefields[source_idx]) # gradient_norm = -gradient / gradient.length() gradient_norm = -gradient.norm() nnodes = current_cell.nodeCount() params = np.zeros((nnodes, 2)) gradient_line = pg.Line(ray_origin, ray_origin + gradient_norm) for i in range(nnodes): if current_cell.node(i).id() == source_node: print("cell closest to source") params[i, :] = [ray_origin.dist(source_pos), i] break edge = pg.Line(current_cell.node(i).pos(), current_cell.node((i+1) % nnodes).pos()) # print("Grad: {}".format(gradient_line)) # print("Edge: {}".format(edge)) s_t = self._intersect_lines(gradient_line, edge) # print("s_t: {}".format(s_t)) params[i, :] = [s_t[0], i] t, node_idx, stay_on_edge = self._check_param(params) print("Stay on edge: {}".format(stay_on_edge)) boundary = pg.findBoundary( current_cell.node(node_idx), current_cell.node((node_idx+1) % nnodes)) if stay_on_edge: # break next_node_id, next_cell_id = self._get_next_node( boundary, current_cell.id(), ray_origin, gradient_norm) t = ray_origin.dist(msh.node(next_node_id).pos()) print("Current: {}, next: {}, t: {}".format( current_cell.id(), next_cell_id, t)) print("") self._jac[source_idx][next_cell_id] += t temp = msh.node(next_node_id).pos() - ray_origin ray_origin = msh.node(next_node_id).pos() + \ 1e-5 * temp.norm() - pg.RVector3(0.0, 1e-6, 0.0) # new_cell = mesh.cell(next_cell_id) new_cell = msh.findCell(ray_origin) was_on_edge = True # print("next_cell_id: {}, findCell: {}".format( # next_cell_id, new_cell.id())) else: # print("params: {}, t: {}, i: {}".format(params, t, node_idx)) # Save distance travelled in the cell (t) and update origin self._jac[source_idx][current_cell.id()] = t ray_origin = gradient_line.lineAt(t) # print("ray origin: {}".format(ray_origin)) new_cell = self._get_new_cell(boundary, current_cell) if new_cell.id() == old_cell_id: # If we keep jumping back and forth between two cells. print("Jumping back and forth...") break return self._jac
def fastMarch(mesh, downwind, times, upT, downT): """WRITEME.""" upCandidate = [] # print('.', end='') for node in downwind: neighNodes = pg.commonNodes(node.cellSet()) upNodes = [] for n in neighNodes: if upT[n.id()]: upNodes.append(n) if len(upNodes) == 1: # the Dijkstra case edge = pg.findBoundary(upNodes[0], node) tt = times[upNodes[0].id()] + \ findSlowness(edge) * edge.shape().domainSize() heapq.heappush(upCandidate, (tt, node)) else: cells = node.cellSet() for c in cells: for i in range(c.nodeCount()): edge = pg.findBoundary(c.node(i), c.node((i + 1) % 3)) a = edge.node(0) b = edge.node(1) ta = times[a.id()] tb = times[b.id()] if upT[a.id()] and upT[b.id()]: line = pg.Line(a.pos(), b.pos()) t = min(1., max(0., line.nearest(node.pos()))) ea = pg.findBoundary(a, node) eb = pg.findBoundary(b, node) if t == 0: slowness = findSlowness(ea) elif t == 1: slowness = findSlowness(eb) else: slowness = c.attribute() ttimeA = (ta + slowness * a.pos().distance(node.pos())) ttimeQ = (ta + t * (tb - ta)) + \ slowness * line(t).distance(node.pos()) ttimeB = (tb + slowness * b.pos().distance(node.pos())) heapq.heappush(upCandidate, (min(ttimeA, ttimeQ, ttimeB), node)) # for c in upCandidate: # print c[1].id(), c[0] candidate = heapq.heappop(upCandidate) # print candidate newUpNode = candidate[1] times[newUpNode.id()] = candidate[0] upT[newUpNode.id()] = 1 # print newUpNode downwind.remove(newUpNode) newDownNodes = pg.commonNodes(newUpNode.cellSet()) for nn in newDownNodes: if not upT[nn.id()] and not downT[nn.id()]: downwind.add(nn) downT[nn.id()] = 1
def calcSeismics(meshIn, vP): """Do seismic computations.""" meshSeis = meshIn.createH2() meshSeis = mt.appendTriangleBoundary( meshSeis, xbound=25, ybound=22.0, marker=1, quality=32.0, area=0.3, smooth=True, markerBoundary=1, isSubSurface=False, verbose=False) print(meshSeis) meshSeis = meshSeis.createH2() meshSeis = meshSeis.createH2() # meshSeis = meshSeis.createP2() meshSeis.smooth(1, 1, 1, 4) vP = pg.interpolate(meshIn, vP, meshSeis.cellCenters()) mesh = meshSeis vP = pg.solver.fillEmptyToCellArray(mesh, vP) print(mesh) # ax, cbar = pg.show(mesh, data=vP) # pg.show(mesh, axes=ax) geophPointsX = np.arange(-19, 19.1, 1) geophPoints = np.vstack((geophPointsX, np.zeros(len(geophPointsX)))).T sourcePos = geophPoints[4] c = mesh.findCell(sourcePos) h1 = pg.findBoundary(c.boundaryNodes(0)).size() h2 = pg.findBoundary(c.boundaryNodes(1)).size() h3 = pg.findBoundary(c.boundaryNodes(2)).size() print([h1, h2, h3]) h = pg.median([h1, h2, h3]) # h = pg.median(mesh.boundarySizes()) f0scale = 0.25 cfl = 0.5 dt = cfl * h / max(vP) print("Courant-Friedrich-Lewy number:", cfl) tmax = 40./min(vP) times = np.arange(0.0, tmax, dt) solutionName = createCacheName('seis', mesh, times) + "cfl-" + str(cfl) try: # u = pg.load(solutionName + '.bmat') uI = pg.load(solutionName + 'I.bmat') except Exception as e: print(e) f0 = f0scale * 1./dt print("h:", round(h, 2), "dt:", round(dt, 5), "1/dt:", round(1/dt, 1), "f0", round(f0, 2), "Wavelength: ", round(max(vP)/f0, 2), " m") uSource = ricker(times, f0, t0=1./f0) plt.figure() plt.plot(times, uSource, '-*') plt.show(block=0) plt.pause(0.01) u = solvePressureWave(mesh, vP, times, sourcePos=sourcePos, uSource=uSource, verbose=10) u.save(solutionName) uI = pg.RMatrix() print("interpolate node to cell data ... ") pg.interpolate(mesh, u, mesh.cellCenters(), uI) print("... done") uI.save(solutionName+'I') # nodes = [mesh.findNearestNode(p) for p in geophPoints] # fig = plt.figure() # axs = fig.add_subplot(1,1,1) # drawSeismogramm(axs, mesh, u, nodes, dt, i=None) # plt.show() dpi = 92 scale = 1 fig = plt.figure(facecolor='white', figsize=(scale*800/dpi, scale*490/dpi), dpi=dpi) ax = fig.add_subplot(1, 1, 1) gci = pg.mplviewer.drawModel(ax, mesh, data=uI[0], cMin=-1, cMax=1, cmap='bwr') pg.mplviewer.drawMeshBoundaries(ax, meshIn, hideMesh=1) ax.set_xlim((-20, 20)) ax.set_ylim((-15, 0)) ax.set_ylabel('Depth [m]') ax.set_xlabel('$x$ [m]') ticks = ax.yaxis.get_majorticklocs() tickLabels = [] for t in ticks: tickLabels.append(str(int(abs(t)))) ax.set_yticklabels(tickLabels) plt.tight_layout() # ax, cbar = pg.show(mesh, data=vP) # pg.showNow() # ax = fig.add_subplot(1,1,1) def animate(i): i = i*5 if i > len(uI)-1: return print("Frame:", i, "/", len(uI)) ui = uI[i] ui = ui / max(pg.abs(ui)) ui = pg.logDropTol(ui, 1e-2) cMax = max(pg.abs(ui)) pg.mplviewer.setMappableData(gci, ui, cMin=-cMax, cMax=cMax, logScale=False) # plt.pause(0.001) anim = animation.FuncAnimation(fig, animate, frames=int(len(uI)/5), interval=0.001, repeat=0) # , blit=True) out = 'seis' + str(f0scale) + "cfl-" + str(cfl) anim.save(out + ".mp4", writer=None, fps=20, dpi=dpi, codec=None, bitrate=24*1024, extra_args=None, metadata=None, extra_anim=None, savefig_kwargs=None) try: print("create frames ... ") os.system('mkdir -p anim-' + out) os.system('ffmpeg -i ' + out + '.mp4 anim-' + out + '/movie%d.jpg') except: pass
def diffusionConvectionKernel( mesh, a=None, b=0.0, uB=None, duB=None, vel=0, # u0=0, fn=None, scheme='CDS', sparse=False, time=0.0, userData=None): """ Generate system matrix for diffusion and convection in a velocity field. Particle concentration u inside a velocity field. Peclet Number - ratio between convection/diffusion = F/D F = velocity flow trough volume boundary, D = diffusion coefficient Parameters ---------- mesh : :gimliapi:`GIMLI::Mesh` Mesh represents spatial discretization of the calculation domain a : value | array | callable(cell, userData) Diffusion coefficient per cell b : value | array | callable(cell, userData) TODO What is b fn : iterable(cell) TODO What is fn vel : ndarray (N,dim) | RMatrix(N,dim) velocity field [[v_i,]_j,] with i=[1..3] for the mesh dimension and j = [0 .. N-1] per Cell or per Node so N is either mesh.cellCount() or mesh.nodeCount() scheme : str [CDS] Finite volume scheme * CDS -- Central Difference Scheme. maybe irregular for Peclet no. |F/D| > 2 Diffusion dominant. Error of order 2 * UDS -- Upwind Scheme. Convection dominant. Error of order 1 * HS -- Hybrid Scheme. Diffusion dominant for Peclet-number |(F/D)| < 2 Convection dominant else. * PS -- Power Law Scheme. Identical to HS for Peclet-number |(F/D)| > 10 and near to ES else Convection dominant. * ES -- Exponential scheme Only stationary one-dimensional but exact solution Returns ------- S : :gimliapi:`GIMLI::SparseMatrix` | numpy.ndarray(nCells, nCells) Kernel matrix, depends on vel, a, b, scheme, uB, duB .. if some of this has been changed you cannot cache these matrix rhsBoundaryScales : ndarray(nCells) RHS offset vector """ if a is None: a = pg.RVector(mesh.boundaryCount(), 1.0) AScheme = None if scheme == 'CDS': AScheme = lambda peclet_: 1.0 - 0.5 * abs(peclet_) elif scheme == 'UDS': AScheme = lambda peclet_: 1.0 elif scheme == 'HS': AScheme = lambda peclet_: max(0.0, 1.0 - 0.5 * abs(peclet_)) elif scheme == 'PS': AScheme = lambda peclet_: max(0.0, (1.0 - 0.1 * abs(peclet_))**5.0) elif scheme == 'ES': AScheme = lambda peclet_: (peclet_) / (np.exp(abs(peclet_)) - 1.0) \ if peclet_ != 0.0 else 1 else: raise BaseException("Scheme unknwon:" + scheme) useHalfBoundaries = False dof = mesh.cellCount() if not uB: uB = [] if not duB: duB = [] if useHalfBoundaries: dof = mesh.cellCount() + len(uB) S = None if sparse: S = pg.RSparseMapMatrix(dof, dof, stype=0) + identity(dof, scale=b) 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 [boundary, val] in uB: 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 [boundary, val] in duB: if not isinstance(boundary, pg.Boundary): raise BaseException("Please give boundary, value list") duBoundaryID.append(boundary.id()) duBoundaryVals[boundary.id()] = val # iterate over all cells for cell in mesh.cells(): cID = cell.id() 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() # print(F, boundary.size(), v, vel) # Diffusion part D = findDiffusion(mesh, a, boundary, cell, ncell) # print(F, D, F/D) # print((1.0 - 0.1 * abs(F/D))**5.0) 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(cID, ncell.id(), -aB) S.addVal(cID, cID, +aB) else: S[cID, ncell.id()] -= aB S[cID, cID] += 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(cID, cID, aB) else: S[cID, cID] += aB rhsBoundaryScales[cID] += aB * val if boundary.id() in duBoundaryID: # Neumann boundary condition val = pg.solver.generateBoundaryValue( boundary, duBoundaryVals[boundary.id()], time=time, userData=userData) # amount of flow through the boundary outflow = val * boundary.size() / cell.size() if sparse: S.addVal(cID, cID, outflow) else: S[cID, cID] += outflow if fn is not None: if sparse: # * cell.shape().domainSize()) S.addVal(cell.id(), cell.id(), -fn[cell.id()]) else: # * cell.shape().domainSize() S[cell.id(), cell.id()] -= fn[cell.id()] return S, rhsBoundaryScales
def diffusionConvectionKernel(mesh, a=None, b=0.0, f=None, uB=None, duB=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 diff. scheme .. maybe irregular for Peclet no. |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 uB: uB = [] if not duB: duB = [] if useHalfBoundaries: dof = mesh.cellCount() + len(uB) S = None if sparse: S = pg.RSparseMapMatrix(dof, dof, 0) + identity(dof) * b 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(uB): 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(duB): 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 is not None: if sparse: # * cell.shape().domainSize()) S.addVal(cell.id(), cell.id(), -fn[cell.id()]) else: # * cell.shape().domainSize() S[cell.id(), cell.id()] -= fn[cell.id()] if useHalfBoundaries: for i, [b, val] in enumerate(duB): # not defined!!! 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
def diffusionConvectionKernel(mesh, a=None, b=0.0, uB=None, duB=None, vel=0, # u0=0, fn=None, scheme='CDS', sparse=False, time=0.0, userData=None): """ Generate system matrix for diffusion and convection in a velocity field. Particle concentration u inside a velocity field. Peclet Number - ratio between convection/diffusion = F/D F = velocity flow trough volume boundary, D = diffusion coefficient Parameters ---------- mesh : :gimliapi:`GIMLI::Mesh` Mesh represents spatial discretization of the calculation domain a : value | array | callable(cell, userData) Diffusion coefficient per cell b : value | array | callable(cell, userData) TODO What is b fn : iterable(cell) TODO What is fn vel : ndarray (N,dim) | RMatrix(N,dim) velocity field [[v_i,]_j,] with i=[1..3] for the mesh dimension and j = [0 .. N-1] per Cell or per Node so N is either mesh.cellCount() or mesh.nodeCount() scheme : str [CDS] Finite volume scheme * CDS -- Central Difference Scheme. maybe irregular for Peclet no. |F/D| > 2 Diffusion dominant. Error of order 2 * UDS -- Upwind Scheme. Convection dominant. Error of order 1 * HS -- Hybrid Scheme. Diffusion dominant for Peclet-number |(F/D)| < 2 Convection dominant else. * PS -- Power Law Scheme. Identical to HS for Peclet-number |(F/D)| > 10 and near to ES else Convection dominant. * ES -- Exponential scheme Only stationary one-dimensional but exact solution Returns ------- S : :gimliapi:`GIMLI::SparseMatrix` | numpy.ndarray(nCells, nCells) Kernel matrix, depends on vel, a, b, scheme, uB, duB .. if some of this has been changed you cannot cache these matrix rhsBoundaryScales : ndarray(nCells) RHS offset vector """ if a is None: a = pg.RVector(mesh.boundaryCount(), 1.0) AScheme = None if scheme == 'CDS': AScheme = lambda peclet_: 1.0 - 0.5 * abs(peclet_) elif scheme == 'UDS': AScheme = lambda peclet_: 1.0 elif scheme == 'HS': AScheme = lambda peclet_: max(0.0, 1.0 - 0.5 * abs(peclet_)) elif scheme == 'PS': AScheme = lambda peclet_: max(0.0, (1.0 - 0.1 * abs(peclet_))**5.0) elif scheme == 'ES': AScheme = lambda peclet_: (peclet_) / (np.exp(abs(peclet_)) - 1.0) \ if peclet_ != 0.0 else 1 else: raise BaseException("Scheme unknwon:" + scheme) useHalfBoundaries = False dof = mesh.cellCount() if not uB: uB = [] if not duB: duB = [] if useHalfBoundaries: dof = mesh.cellCount() + len(uB) S = None if sparse: S = pg.RSparseMapMatrix(dof, dof, stype=0) + identity(dof, scale=b) 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 [boundary, val] in uB: 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 [boundary, val] in duB: if not isinstance(boundary, pg.Boundary): raise BaseException("Please give boundary, value list") duBoundaryID.append(boundary.id()) duBoundaryVals[boundary.id()] = val # iterate over all cells for cell in mesh.cells(): cID = cell.id() 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() # print(F, boundary.size(), v, vel) # Diffusion part D = findDiffusion(mesh, a, boundary, cell, ncell) # print(F, D, F/D) # print((1.0 - 0.1 * abs(F/D))**5.0) 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(cID, ncell.id(), -aB) S.addVal(cID, cID, +aB) else: S[cID, ncell.id()] -= aB S[cID, cID] += 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(cID, cID, aB) else: S[cID, cID] += aB rhsBoundaryScales[cID] += aB * val if boundary.id() in duBoundaryID: # Neumann boundary condition val = pg.solver.generateBoundaryValue( boundary, duBoundaryVals[boundary.id()], time=time, userData=userData) # amount of flow through the boundary outflow = val * boundary.size() / cell.size() if sparse: S.addVal(cID, cID, outflow) else: S[cID, cID] += outflow if fn is not None: if sparse: # * cell.shape().domainSize()) S.addVal(cell.id(), cell.id(), -fn[cell.id()]) else: # * cell.shape().domainSize() S[cell.id(), cell.id()] -= fn[cell.id()] return S, rhsBoundaryScales