def restore(self): """Read data from json infos""" if os.path.exists(self._name + '.json'): # Fricking mpl kills locale setting to system default .. this went # horrible wrong for german 'decimal_point': ',' pg.checkAndFixLocaleDecimal_point(verbose=False) try: with open(self._name + '.json') as file: self.info = json.load(file) # if len(self.info['type']) != 1: # pg.error('only single return caches supported for now.') #pg._y(pg.pf(self.info)) if self.info['type'] == 'DataContainerERT': self._value = pg.DataContainerERT(self.info['file'], removeInvalid=False) # print(self._value) elif self.info['type'] == 'RVector': self._value = pg.Vector() self._value.load(self.info['file'], format=pg.core.Binary) elif self.info['type'] == 'Mesh': pg.tic() self._value = pg.Mesh() self._value.loadBinaryV2(self.info['file'] + '.bms') pg.debug("Restoring cache took:", pg.dur(), "s") elif self.info['type'] == 'ndarray': self._value = np.load(self.info['file'] + '.npy', allow_pickle=True) elif self.info['type'] == 'Cm05Matrix': self._value = pg.matrix.Cm05Matrix(self.info['file']) elif self.info['type'] == 'GeostatisticConstraintsMatrix': self._value = pg.matrix.GeostatisticConstraintsMatrix( self.info['file']) else: self._value = np.load(self.info['file'] + '.npy', allow_pickle=True) if self.value is not None: self.info['restored'] = self.info['restored'] + 1 self.updateCacheInfo() pg.info('Cache {3} restored ({1}s x {0}): {2}'.\ format(self.info['restored'], round(self.info['dur'], 1), self._name, self.info['codeinfo'])) else: # default try numpy pg.warn('Could not restore cache of type {0}.'.format(self.info['type'])) pg.debug("Restoring cache took:", pg.dur(), "s") except Exception as e: import traceback traceback.print_exc(file=sys.stdout) print(self.info) pg.error('Cache restoring failed.')
def hash(self, funct, *args, **kwargs): """"Create a hash value""" pg.tic() functInfo = self.functInfo(funct) funcHash = strHash(functInfo) versionHash = strHash(pg.versionStr()) codeHash = strHash(inspect.getsource(funct)) argHash = 0 for a in args: if isinstance(a, str): argHash = argHash ^ strHash(a) elif isinstance(a, list): for item in a: if isinstance(item, str): argHash = argHash ^ strHash(item) else: argHash = argHash ^ hash(item) else: argHash = argHash ^ hash(a) for k, v in kwargs.items(): if isinstance(v, str): argHash = argHash ^ strHash(v) else: argHash = argHash ^ hash(v) pg.debug("Hashing took:", pg.dur(), "s") return funcHash ^ versionHash ^ codeHash ^ argHash
def __init__(self, A, verbose=False): """Constructor saving matrix and vector. Parameters ---------- A : ndarray numpy type (full) matrix """ super().__init__(verbose) # only in Python 3 self._mul = None if isinstance(A, str): self.load(A) else: from scipy.linalg import eigh # , get_blas_funcs if A.shape[0] != A.shape[1]: # rows/cols for pgcore matrix raise Exception("Matrix must by square (and symmetric)!") if verbose: t = pg.tic(key='init cm05') self.ew, self.EV = eigh(A) if verbose: pg.info( '(C) Time for eigenvalue decomposition: {:.1f}s'.format( pg.dur(key='init cm05')))
def hash(self, funct, *args, **kwargs): """"Create a hash value""" pg.tic() functInfo = self.functInfo(funct) funcHash = strHash(functInfo) versionHash = strHash(pg.versionStr()) codeHash = strHash(inspect.getsource(funct)) argHash = 0 for a in args: argHash = argHash ^ valHash(a) for k, v in kwargs.items(): argHash = argHash ^ valHash(k) ^ valHash(v) pg.debug("Hashing took:", pg.dur(), "s") return funcHash ^ versionHash ^ codeHash ^ argHash
def simulate(self, mesh, scheme, res, **kwargs): """Simulate an ERT measurement. Perform the forward task for a given mesh, a resistivity distribution (per cell), a measurement scheme and will return data (apparent resistivity) or potential fields. This function can also operate on complex resistivity models, thereby computing complex apparent resistivities. The forward operator itself only calculate potential values for the given scheme file. To calculate apparent resistivities, geometric factors (k) are needed. If there are no values k in the DataContainerERT scheme, then we will try to calculate them, either analytic or by using a p2-refined version of the given mesh. TODO ---- * 2D + Complex + SR Args ---- mesh : :gimliapi:`GIMLI::Mesh` 2D or 3D Mesh to calculate for. res : float, array(mesh.cellCount()) | array(N, mesh.cellCount()) | list Resistivity distribution for the given mesh cells can be: . float for homogeneous resistivity . single array of length mesh.cellCount() . matrix of N resistivity distributions of length mesh.cellCount() . resistivity map as [[regionMarker0, res0], [regionMarker0, res1], ...] scheme : :gimliapi:`GIMLI::DataContainerERT` Data measurement scheme. Keyword Args ------------ verbose: bool[False] Be verbose. Will override class settings. calcOnly: bool [False] Use fop.calculate instead of fop.response. Useful if you want to force the calculation of impedances for homogeneous models. No noise handling. Solution is put as token 'u' in the returned DataContainerERT. noiseLevel: float [0.0] add normally distributed noise based on scheme('err') or on noiseLevel if scheme did not contain 'err' noiseAbs: float [0.0] Absolute voltage error in V returnArray: bool [False] Returns an array of apparent resistivities instead of a DataContainerERT returnFields: bool [False] Returns a matrix of all potential values (per mesh nodes) for each injection electrodes. Returns ------- DataContainerERT | array(N, data.size()) | array(N, data.size()) | array(N, data.size()): Data container with resulting apparent resistivity data and errors (if noiseLevel or noiseAbs is set). Optional returns a Matrix of rhoa values (for returnArray==True forces noiseLevel=0). In case of a complex valued resistivity model, phase values will be returned in the DataContainerERT (see example below), or as an additional returned array. Examples -------- # TODO: Remove pybert dependencies # >>> import pybert as pb # >>> import pygimli as pg # >>> import pygimli.meshtools as mt # >>> world = mt.createWorld(start=[-50, 0], end=[50, -50], # ... layers=[-1, -5], worldMarker=True) # >>> scheme = pb.createData( # ... elecs=pg.utils.grange(start=-10, end=10, n=21), # ... schemeName='dd') # >>> for pos in scheme.sensorPositions(): # ... _= world.createNode(pos) # ... _= world.createNode(pos + [0.0, -0.1]) # >>> mesh = mt.createMesh(world, quality=34) # >>> rhomap = [ # ... [1, 100. + 0j], # ... [2, 50. + 0j], # ... [3, 10.+ 0j], # ... ] # >>> ert = pb.ERTManager() # >>> data = ert.simulate(mesh, res=rhomap, scheme=scheme, verbose=True) # >>> rhoa = data.get('rhoa').array() # >>> phia = data.get('phia').array() """ verbose = kwargs.pop('verbose', self.verbose) calcOnly = kwargs.pop('calcOnly', False) returnFields = kwargs.pop("returnFields", False) returnArray = kwargs.pop('returnArray', False) noiseLevel = kwargs.pop('noiseLevel', 0.0) noiseAbs = kwargs.pop('noiseAbs', 1e-4) seed = kwargs.pop('seed', None) #segfaults with self.fop (test & fix) fop = self.createForwardOperator(useBert=self.useBert, sr=self.sr) fop.data = scheme fop.setMesh(mesh, ignoreRegionManager=True) fop.verbose = verbose rhoa = None phia = None isArrayData = False # parse the given res into mesh-cell-sized array if isinstance(res, int) or isinstance(res, float): res = np.ones(mesh.cellCount()) * float(res) elif isinstance(res, complex): res = np.ones(mesh.cellCount()) * res elif hasattr(res[0], '__iter__'): # ndim == 2 if len(res[0]) == 2: # res seems to be a res map # check if there are markers in the mesh that are not defined in # the rhomap. better signal here before it results in some error meshMarkers = list(set(mesh.cellMarkers())) mapMarkers = [m[0] for m in res] if any([mark not in mapMarkers for mark in meshMarkers]): left = [m for m in meshMarkers if m not in mapMarkers] pg.critical( "Mesh contains markers without assigned resistivities {}. Please fix given rhomap." .format(left)) res = pg.solver.parseArgToArray(res, mesh.cellCount(), mesh) else: # probably nData x nCells array # better check for array data here isArrayData = True if isinstance(res[0], np.complex) or isinstance(res, pg.CVector): pg.info("Complex resistivity values found.") fop.setComplex(True) else: fop.setComplex(False) if not scheme.allNonZero('k') and not calcOnly: if verbose: pg.info('Calculate geometric factors.') scheme.set('k', fop.calcGeometricFactor(scheme)) ret = pg.DataContainerERT(scheme) ## just be sure that we don't work with artifacts ret['u'] *= 0.0 ret['i'] *= 0.0 ret['r'] *= 0.0 if isArrayData: rhoa = np.zeros((len(res), scheme.size())) for i, r in enumerate(res): rhoa[i] = fop.response(r) if verbose: print(i, "/", len(res), " : ", pg.dur(), "s", "min r:", min(r), "max r:", max(r), "min r_a:", min(rhoa[i]), "max r_a:", max(rhoa[i])) else: # res is single resistivity array if len(res) == mesh.cellCount(): if calcOnly: fop.mapERTModel(res, 0) dMap = pg.core.DataMap() fop.calculate(dMap) if fop.complex(): pg.critical('Implement me') else: ret["u"] = dMap.data(scheme) ret["i"] = np.ones(ret.size()) if returnFields: return pg.Matrix(fop.solution()) return ret else: if fop.complex(): res = pg.utils.squeezeComplex(res) resp = fop.response(res) if fop.complex(): rhoa, phia = pg.utils.toPolar(resp) else: rhoa = resp else: print(mesh) print("res: ", res) raise BaseException( "Simulate called with wrong resistivity array.") if not isArrayData: ret['rhoa'] = rhoa if phia is not None: ret.set('phia', phia) else: ret.set('rhoa', rhoa[0]) if phia is not None: ret.set('phia', phia[0]) if returnFields: return pg.Matrix(fop.solution()) if noiseLevel > 0: # if errors in data noiseLevel=1 just triggers if not ret.allNonZero('err'): # 1A and #100µV ret.set( 'err', self.estimateError(ret, relativeError=noiseLevel, absoluteUError=noiseAbs, absoluteCurrent=1)) print("Data error estimate (min:max) ", min(ret('err')), ":", max(ret('err'))) rhoa *= 1. + pg.randn(ret.size(), seed=seed) * ret('err') ret.set('rhoa', rhoa) ipError = None if phia is not None: if scheme.allNonZero('iperr'): ipError = scheme('iperr') else: # np.abs(self.data("phia") +TOLERANCE) * 1e-4absoluteError if noiseLevel > 0.5: noiseLevel /= 100. if 'phiErr' in kwargs: ipError = np.ones( ret.size()) * kwargs.pop('phiErr') / 1000 else: ipError = abs(ret["phia"]) * noiseLevel if verbose: print("Data IP abs error estimate (min:max) ", min(ipError), ":", max(ipError)) phia += np.randn(ret.size(), seed=seed) * ipError ret['iperr'] = ipError ret['phia'] = phia # check what needs to be setup and returned if returnArray: if phia is not None: return rhoa, phia else: return rhoa return ret
def response(self, model): """Solve forward task. Create apparent resistivity values for a given resistivity distribution for self.mesh. """ ### NOTE TODO can't be MT until mixed boundary condition depends on ### self.resistivity pg.tic() if not self.data.allNonZero('k'): pg.error('Need valid geometric factors: "k".') pg.warn('Fallback "k" values to -sign("rhoa")') self.data.set('k', -pg.math.sign(self.data('rhoa'))) mesh = self.mesh() nDof = mesh.nodeCount() elecs = self.data.sensorPositions() nEle = len(elecs) nData = self.data.size() self.resistivity = res = self.createMappedModel(model, -1.0) if self.verbose: print("Calculate response for model:", min(res), max(res)) rMin = elecs[0].dist(elecs[1]) / 2.0 rMax = elecs[0].dist(elecs[-1]) * 2.0 k, w = self.getIntegrationWeights(rMin, rMax) self.k = k self.w = w # pg.show(mesh, res, label='res') # pg.wait() rhs = self.createRHS(mesh, elecs) # store all potential fields u = np.zeros((nEle, nDof)) self.subPotentials = [pg.Matrix(nEle, nDof) for i in range(len(k))] for i, ki in enumerate(k): ws = dict() uE = pg.solve(mesh, a=1. / res, b=-(ki * ki) / res, f=rhs, bc={'Robin': ['*', self.mixedBC]}, userData={ 'sourcePos': elecs, 'k': ki }, verbose=False, stats=0, debug=False) self.subPotentials[i] = uE u += w[i] * uE # collect potential matrix, # i.e., potential for all electrodes and all injections pM = np.zeros((nEle, nEle)) for i in range(nEle): pM[i] = pg.interpolate(mesh, u[i, :], destPos=elecs) # collect resistivity values for all 4 pole measurements r = np.zeros(nData) for i in range(nData): iA = int(self.data('a')[i]) iB = int(self.data('b')[i]) iM = int(self.data('m')[i]) iN = int(self.data('n')[i]) uAB = pM[iA] - pM[iB] r[i] = uAB[iM] - uAB[iN] self.lastResponse = r * self.data('k') if self.verbose: print("Resp min/max: {0} {1} {2}s".format(min(self.lastResponse), max(self.lastResponse), pg.dur())) return self.lastResponse
mgr = TravelTimeManager() cols = ["orangered", "blue", "black"] recs = [1, 3, 8, 13] for i, n in enumerate(sec_nodes): # Perform traveltime calculations and log time with pg.tic() & pg.toc() pg.tic() res = mgr.simulate(vel=vel, scheme=data, mesh=mesh, secNodes=n) # We need to copy res['t'] here because res['t'] is a reference to # an array in res, and res will be removed in the next iteration. # Unfortunately, we don't have any reverence counting for core objects yet. t_all.append(res['t'].array()) durations.append(pg.dur()) pg.toc("Raytracing with %d secondary nodes:" % n) for r, p in enumerate(recs): if r == 0: lab = "Raypath with %d sec nodes" % n else: lab = None recNode = mgr.fop.mesh().findNearestNode([sensors[p], 0.0]) sourceNode = mgr.fop.mesh().findNearestNode([0.0, 0.0]) path = mgr.fop.dijkstra.shortestPath(sourceNode, recNode) points = mgr.fop.mesh().positions(withSecNodes=True)[path] ax[0, j].plot(pg.x(points), pg.y(points), cols[i], label=lab)
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
durations = [] paths = [] for n in sec_nodes: if n > 0: pg.tic() mesh2 = mesh.createMeshWithSecondaryNodes(n) pg.toc("Mesh generation with %d secondary nodes:" % n) else: mesh2 = mesh # Perform traveltime calculations and log time with pg.tic() & pg.toc() pg.tic() fop = pg.TravelTimeDijkstraModelling(mesh2, data) t_all.append(fop.response(1 / vel)) durations.append(pg.dur()) pg.toc("Raytracing with %d secondary nodes:" % n) # This is to show single raypaths. dij = pg.Dijkstra(fop.createGraph(1 / vel)) dij.setStartNode(mesh2.findNearestNode([0, 0])) paths_per_receiver = [] for receiver in sensors: ni = dij.shortestPathTo(mesh2.findNearestNode([receiver, 0])) pos = mesh2.positions(withSecNodes=True)[ni] paths_per_receiver.append([pg.x(pos), pg.y(pos)]) paths.append(paths_per_receiver) t_ana = ana(px[1:])
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