def MeSigmaIDeriv(self, u, v, adjoint=False): """ Derivative of :code:`MeSigmaI` with respect to the model """ if self.sigmaMap is None: return Utils.Zero() if len(self.sigma.shape) > 1: if self.sigma.shape[1] > self.mesh.dim: raise NotImplementedError( "Full anisotropy is not implemented for MeSigmaIDeriv.") dMeSigmaI_dI = -self.MeSigmaI**2 if self.storeInnerProduct: if adjoint: return self.MeSigmaDerivMat.T * (Utils.sdiag(u) * (dMeSigmaI_dI.T * v)) else: return dMeSigmaI_dI * Utils.sdiag(u) * (self.MeSigmaDerivMat * v) else: dMe_dsig = self.mesh.getEdgeInnerProductDeriv(self.sigma)(u) if adjoint: return self.sigmaDeriv.T * (dMe_dsig.T * (dMeSigmaI_dI.T * v)) else: return dMeSigmaI_dI * (dMe_dsig * (self.sigmaDeriv * v))
def edgeCurl(self): """The edgeCurl property.""" if self.nCy > 1: raise NotImplementedError('Edge curl not yet implemented for ' 'nCy > 1') if getattr(self, '_edgeCurl', None) is None: # 1D Difference matricies dr = sp.spdiags((np.ones((self.nCx + 1, 1)) * [-1, 1]).T, [-1, 0], self.nCx, self.nCx, format="csr") dz = sp.spdiags((np.ones((self.nCz + 1, 1)) * [-1, 1]).T, [0, 1], self.nCz, self.nCz + 1, format="csr") # 2D Difference matricies Dr = sp.kron(sp.identity(self.nNz), dr) Dz = -sp.kron(dz, sp.identity(self.nCx)) A = self.area E = self.edge # Edge curl operator self._edgeCurl = (Utils.sdiag(1 / A) * sp.vstack( (Dz, Dr)) * Utils.sdiag(E)) return self._edgeCurl
def solve_2D_J(rho1, rho2, h, A, B): ex, ez, V = solve_2D_E(rho1, rho2, h, A, B) sigma = 1. / rho2 * np.ones(mesh.nC) sigma[mesh.gridCC[:, 1] >= -h] = 1. / rho1 # since the model is 2D return Utils.sdiag(sigma) * ex, Utils.sdiag(sigma) * ez, V
def MccRhoiDeriv(self, u, v, adjoint=False): """ Derivative of :code:`MccRhoi` with respect to the model. """ if self.rhoMap is None: return Utils.Zero() if len(self.rho.shape) > 1: if self.rho.shape[1] > self.mesh.dim: raise NotImplementedError( "Full anisotropy is not implemented for MccRhoiDeriv.") if self.storeInnerProduct: if adjoint: return self.MccRhoiDerivMat.T * (Utils.sdiag(u) * v) else: return Utils.sdiag(u) * (self.MccRhoiDerivMat * v) else: vol = self.mesh.vol rho = self.rho if adjoint: return self.rhoDeriv.T * (Utils.sdiag(u * vol * (-1. / rho**2)) * v) else: return (Utils.sdiag(u * vol * (-1. / rho**2))) * (self.rhoDeriv * v)
def MeSigmaDeriv(self, u, v, adjoint=False): """ Derivative of MeSigma with respect to the model times a vector (u) """ if self.storeInnerProduct: if adjoint: return self.MeSigmaDerivMat.T * (Utils.sdiag(u)*v) else: return Utils.sdiag(u)*(self.MeSigmaDerivMat * v) else: if self.storeJ: dsigma_dlogsigma = Utils.sdiag(self.sigma)*self.actMap.P else: dsigma_dlogsigma = Utils.sdiag(self.sigma) if adjoint: return ( dsigma_dlogsigma.T * ( self.mesh.getEdgeInnerProductDeriv(self.sigma)(u).T * v ) ) else: return ( self.mesh.getEdgeInnerProductDeriv(self.sigma)(u) * (dsigma_dlogsigma * v) )
def getResidual(self, m, hn, h, dt, bc, return_g=True): """ Where h is the proposed value for the next time iterate (h_{n+1}) """ DIV = self.mesh.faceDiv GRAD = self.mesh.cellGrad BC = self.mesh.cellGradBC AV = self.mesh.aveF2CC.T Dz = self.Dz T = self.modelMap.theta(h, m) dT = self.modelMap.thetaDerivU(h, m) Tn = self.modelMap.theta(hn, m) K = self.modelMap.k(h, m) dK = self.modelMap.kDerivU(h, m) aveK = 1. / (AV * (1. / K)) RHS = DIV * Utils.sdiag(aveK) * (GRAD * h + BC * bc) + Dz * aveK if self.method == 'mixed': r = (T - Tn) / dt - RHS elif self.method == 'head': r = dT * (h - hn) / dt - RHS if not return_g: return r J = dT / dt - DIV * Utils.sdiag(aveK) * GRAD if self.doNewton: DDharmAve = Utils.sdiag(aveK**2) * AV * Utils.sdiag(K**(-2)) * dK J = J - DIV * Utils.sdiag(GRAD * h + BC * bc) * DDharmAve - Dz * DDharmAve return r, J
def test_updateMultipliers(self): nP = 10 m = np.random.rand(nP) W1 = Utils.sdiag(np.random.rand(nP)) W2 = Utils.sdiag(np.random.rand(nP)) phi1 = ObjectiveFunction.L2ObjectiveFunction(W=W1) phi2 = ObjectiveFunction.L2ObjectiveFunction(W=W2) phi = phi1 + phi2 self.assertTrue(phi(m) == phi1(m) + phi2(m)) phi.multipliers[0] = Utils.Zero() self.assertTrue(phi(m) == phi2(m)) phi.multipliers[0] = 1. phi.multipliers[1] = Utils.Zero() self.assertTrue(len(phi.objfcts) == 2) self.assertTrue(len(phi.multipliers) == 2) self.assertTrue(len(phi) == 2) self.assertTrue(phi(m) == phi1(m))
def MfRhoIDeriv(self, u, v, adjoint=False): """ Derivative of :code:`MfRhoI` with respect to the model. """ dMfRhoI_dI = -self.MfRhoI**2 if self.storeInnerProduct: if adjoint: return ( self.MfRhoDerivMat.T * ( Utils.sdiag(u) * (dMfRhoI_dI.T * v) ) ) else: return dMfRhoI_dI * (Utils.sdiag(u) * (self.MfRhoDerivMat*v)) else: if self.storeJ: drho_dlogrho = Utils.sdiag(self.rho)*self.actMap.P else: drho_dlogrho = Utils.sdiag(self.rho) dMf_drho = self.mesh.getFaceInnerProductDeriv(self.rho)(u) if adjoint: return drho_dlogrho.T * (dMf_drho.T * (dMfRhoI_dI.T*v)) else: return dMfRhoI_dI * (dMf_drho * (drho_dlogrho*v))
def evalDeriv(self, f, freq, P0, df_dm_v=None, v=None, adjoint=False): if adjoint: if self.component == "real": PTvr = (P0.T*v).astype(complex) dZr_dfT_v = Utils.sdiag((1./(f**2)))*PTvr return dZr_dfT_v elif self.component == "imag": PTvi = P0.T*v*-1j dZi_dfT_v = Utils.sdiag((1./(f**2)))*PTvi return dZi_dfT_v elif self.component == "both": PTvr = (P0.T*np.r_[v[0]]).astype(complex) PTvi = P0.T*np.r_[v[1]]*-1j dZr_dfT_v = Utils.sdiag((1./(f**2)))*PTvr dZi_dfT_v = Utils.sdiag((1./(f**2)))*PTvi return dZr_dfT_v + dZi_dfT_v else: raise NotImplementedError('must be real, imag or both') else: dZd_dm_v = P0 * (Utils.sdiag(1./(f**2)) * df_dm_v) if self.component == "real": return dZd_dm_v.real elif self.component == "imag": return dZd_dm_v.imag elif self.component == "both": return np.r_[dZd_dm_v.real, dZd_dm_v.imag] else: raise NotImplementedError('must be real, imag or both')
def evalDeriv(self, f, freq, P0, df_dm_v=None, v=None, adjoint=False): if adjoint: if self.component == "real": PTvr = (P0.T * v).astype(complex) dZr_dfT_v = Utils.sdiag((1. / (f**2))) * PTvr return dZr_dfT_v elif self.component == "imag": PTvi = P0.T * v * -1j dZi_dfT_v = Utils.sdiag((1. / (f**2))) * PTvi return dZi_dfT_v elif self.component == "both": PTvr = (P0.T * np.r_[v[0]]).astype(complex) PTvi = P0.T * np.r_[v[1]] * -1j dZr_dfT_v = Utils.sdiag((1. / (f**2))) * PTvr dZi_dfT_v = Utils.sdiag((1. / (f**2))) * PTvi return dZr_dfT_v + dZi_dfT_v else: raise NotImplementedError('must be real, imag or both') else: dZd_dm_v = P0 * (Utils.sdiag(1. / (f**2)) * df_dm_v) if self.component == "real": return dZd_dm_v.real elif self.component == "imag": return dZd_dm_v.imag elif self.component == "both": return np.r_[dZd_dm_v.real, dZd_dm_v.imag] else: raise NotImplementedError('must be real, imag or both')
def _fastInnerProduct(self, projType, prop=None, invProp=False, invMat=False): """ Fast version of getFaceInnerProduct. This does not handle the case of a full tensor prop. :param numpy.array prop: material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) :param str projType: 'E' or 'F' :param bool returnP: returns the projection matrices :param bool invProp: inverts the material property :param bool invMat: inverts the matrix :rtype: scipy.sparse.csr_matrix :return: M, the inner product matrix (nF, nF) """ assert projType in ['F', 'E'], ("projType must be 'F' for faces or 'E'" " for edges") if prop is None: prop = np.ones(self.nC) if invProp: prop = 1./prop if Utils.isScalar(prop): prop = prop*np.ones(self.nC) # number of elements we are averaging (equals dim for regular # meshes, but for cyl, where we use symmetry, it is 1 for edge # variables and 2 for face variables) if self._meshType == 'CYL': n_elements = np.sum(getattr(self, 'vn'+projType).nonzero()) else: n_elements = self.dim # Isotropic? or anisotropic? if prop.size == self.nC: Av = getattr(self, 'ave'+projType+'2CC') Vprop = self.vol * Utils.mkvc(prop) M = n_elements * Utils.sdiag(Av.T * Vprop) elif prop.size == self.nC*self.dim: Av = getattr(self, 'ave'+projType+'2CCV') # if cyl, then only certain components are relevant due to symmetry # for faces, x, z matters, for edges, y (which is theta) matters if self._meshType == 'CYL': if projType == 'E': prop = prop[:, 1] # this is the action of a projection mat elif projType == 'F': prop = prop[:, [0, 2]] V = sp.kron(sp.identity(n_elements), Utils.sdiag(self.vol)) M = Utils.sdiag(Av.T * V * Utils.mkvc(prop)) else: return None if invMat: return Utils.sdInv(M) else: return M
def MnSigmaDeriv(self, u): """ Derivative of MnSigma with respect to the model """ sigma = self.curModel.sigma sigmaderiv = self.curModel.sigmaDeriv vol = self.mesh.vol return Utils.sdiag(u)*self.mesh.aveN2CC.T*Utils.sdiag(vol) * self.curModel.sigmaDeriv
def _fastInnerProduct(self, projType, prop=None, invProp=False, invMat=False): """ Fast version of getFaceInnerProduct. This does not handle the case of a full tensor prop. :param numpy.array prop: material property (tensor properties are possible) at each cell center (nC, (1, 3, or 6)) :param str projType: 'E' or 'F' :param bool returnP: returns the projection matrices :param bool invProp: inverts the material property :param bool invMat: inverts the matrix :rtype: scipy.sparse.csr_matrix :return: M, the inner product matrix (nF, nF) """ assert projType in ["F", "E"], "projType must be 'F' for faces or 'E'" " for edges" if prop is None: prop = np.ones(self.nC) if invProp: prop = 1.0 / prop if Utils.isScalar(prop): prop = prop * np.ones(self.nC) # number of elements we are averaging (equals dim for regular # meshes, but for cyl, where we use symmetry, it is 1 for edge # variables and 2 for face variables) if self._meshType == "CYL": n_elements = np.sum(getattr(self, "vn" + projType).nonzero()) else: n_elements = self.dim # Isotropic? or anisotropic? if prop.size == self.nC: Av = getattr(self, "ave" + projType + "2CC") Vprop = self.vol * Utils.mkvc(prop) M = n_elements * Utils.sdiag(Av.T * Vprop) elif prop.size == self.nC * self.dim: Av = getattr(self, "ave" + projType + "2CCV") # if cyl, then only certain components are relevant due to symmetry # for faces, x, z matters, for edges, y (which is theta) matters if self._meshType == "CYL": if projType == "E": prop = prop[:, 1] # this is the action of a projection mat elif projType == "F": prop = prop[:, [0, 2]] V = sp.kron(sp.identity(n_elements), Utils.sdiag(self.vol)) M = Utils.sdiag(Av.T * V * Utils.mkvc(prop)) else: return None if invMat: return Utils.sdInv(M) else: return M
def diagsJacobian(self, m, hn, hn1, dt, bc): """Diagonals and rhs of the jacobian system The matrix that we are computing has the form:: .- -. .- -. .- -. | Adiag | | h1 | | b1 | | Asub Adiag | | h2 | | b2 | | Asub Adiag | | h3 | = | b3 | | ... ... | | .. | | .. | | Asub Adiag | | hn | | bn | '- -' '- -' '- -' """ if m is not None: self.model = m DIV = self.mesh.faceDiv GRAD = self.mesh.cellGrad BC = self.mesh.cellGradBC AV = self.mesh.aveF2CC.T Dz = self.Dz dT = self.water_retention.derivU(hn) dT1 = self.water_retention.derivU(hn1) dTm = self.water_retention.derivM(hn) dTm1 = self.water_retention.derivM(hn1) K1 = self.hydraulic_conductivity(hn1) dK1 = self.hydraulic_conductivity.derivU(hn1) dKm1 = self.hydraulic_conductivity.derivM(hn1) # Compute part of the derivative of: # # DIV*diag(GRAD*hn1+BC*bc)*(AV*(1.0/K))^-1 DdiagGh1 = DIV*Utils.sdiag(GRAD*hn1+BC*bc) diagAVk2_AVdiagK2 = ( Utils.sdiag((AV*(1./K1))**(-2)) * AV*Utils.sdiag(K1**(-2)) ) Asub = (-1.0/dt)*dT Adiag = ( (1.0/dt)*dT1 - DdiagGh1*diagAVk2_AVdiagK2*dK1 - DIV*Utils.sdiag(1./(AV*(1./K1)))*GRAD - Dz*diagAVk2_AVdiagK2*dK1 ) B = ( DdiagGh1*diagAVk2_AVdiagK2*dKm1 + Dz*diagAVk2_AVdiagK2*dKm1 + (1.0/dt)*(dTm - dTm1) ) return Asub, Adiag, B
def MnSigmaDeriv(self, u): """ Derivative of MnSigma with respect to the model """ sigma = self.curModel.sigma sigmaderiv = self.curModel.sigmaDeriv vol = self.mesh.vol return (Utils.sdiag(u) * self.mesh.aveN2CC.T * Utils.sdiag(vol) * self.curModel.sigmaDeriv)
def BiotSavartFun(self, rxlocs, component='z'): """ Compute systematrix G using Biot-Savart Law G = np.vstack((G1,G2,G3..,Gnpts) .. math:: """ if rxlocs.ndim == 1: npts = 1 else: npts = rxlocs.shape[0] e = np.ones((self.mesh.nC, 1)) o = np.zeros((self.mesh.nC, 1)) const = mu_0/4/np.pi if self.mesh._meshType == "CYL": G = np.zeros((npts, self.mesh.nC)) else: G = np.zeros((npts, self.mesh.nC*3)) # TODO: this works fine, but potential improvement by analyticially # evaluating integration. for i in range(npts): if npts == 1: r_rx = np.repeat( Utils.mkvc(rxlocs).reshape([1, -1]), self.mesh.nC, axis = 0 ) else: r_rx = np.repeat( rxlocs[i, :].reshape([1, -1]), self.mesh.nC, axis = 0 ) r_CC = self.mesh.gridCC r = r_rx-r_CC r_abs = np.sqrt((r**2).sum(axis=1)) rxind = r_abs == 0. # r_abs[rxind] = self.mesh.vol.min()**(1./3.)*0.5 r_abs[rxind] = 1e20 Sx = const*Utils.sdiag(self.mesh.vol*r[:, 0]/r_abs**3) Sy = const*Utils.sdiag(self.mesh.vol*r[:, 1]/r_abs**3) Sz = const*Utils.sdiag(self.mesh.vol*r[:, 2]/r_abs**3) if self.mesh._meshType == "CYL": if component == 'x': G_temp = np.hstack((e.T*Sz)) elif component == 'z': G_temp = np.hstack((-e.T*Sx)) else: if component == 'x': G_temp = np.hstack((o.T, e.T*Sz, -e.T*Sy)) elif component == 'y': G_temp = np.hstack((-e.T*Sz, o.T, e.T*Sx)) elif component == 'z': G_temp = np.hstack((e.T*Sy, -e.T*Sx, o.T)) G[i, :] = G_temp return G
def MnSigma(self): """ Node inner product matrix for \\(\\sigma\\). Used in the E-B formulation """ # TODO: only works isotropic sigma sigma = self.curModel.sigma vol = self.mesh.vol MnSigma = Utils.sdiag(self.mesh.aveN2CC.T*(Utils.sdiag(vol)*sigma)) return MnSigma
def getADeriv(self, ky, u, v, adjoint= False): D = self.Div G = self.Grad vol = self.mesh.vol MfRhoIDeriv = self.MfRhoIDeriv rho = self.curModel.rho if adjoint: return(MfRhoIDeriv( G * u ).T) * ( D.T * v) + ky**2*Utils.sdiag(u.flatten()*vol*(-1./rho**2))*v return D * ((MfRhoIDeriv( G * u )) * v) + ky**2*Utils.sdiag(u.flatten()*vol*(-1./rho**2))*v
def getError(self): # Test function def phi_fun(x): return np.cos(np.pi * x) def j_fun(x): return np.pi * np.sin(np.pi * x) def phi_deriv(x): return -j_fun(x) def q_fun(x): return (np.pi**2) * np.cos(np.pi * x) xc_ana = phi_fun(self.M.gridCC) q_ana = q_fun(self.M.gridCC) j_ana = j_fun(self.M.gridFx) # Get boundary locations vecN = self.M.vectorNx vecC = self.M.vectorCCx # Setup Mixed B.C (alpha, beta, gamma) alpha_xm, alpha_xp = 1., 1. beta_xm, beta_xp = 1., 1. alpha = np.r_[alpha_xm, alpha_xp] beta = np.r_[beta_xm, beta_xp] vecN = self.M.vectorNx vecC = self.M.vectorCCx phi_bc = phi_fun(vecN[[0, -1]]) phi_deriv_bc = phi_deriv(vecN[[0, -1]]) gamma = alpha * phi_bc + beta * phi_deriv_bc x_BC, y_BC = getxBCyBC_CC(self.M, alpha, beta, gamma) sigma = np.ones(self.M.nC) Mfrho = self.M.getFaceInnerProduct(1. / sigma) MfrhoI = self.M.getFaceInnerProduct(1. / sigma, invMat=True) V = Utils.sdiag(self.M.vol) Div = V * self.M.faceDiv P_BC, B = self.M.getBCProjWF_simple() q = q_fun(self.M.gridCC) M = B * self.M.aveCC2F G = Div.T - P_BC * Utils.sdiag(y_BC) * M # Mrhoj = D.T V phi + P_BC*Utils.sdiag(y_BC)*M phi - P_BC*x_BC rhs = V * q + Div * MfrhoI * P_BC * x_BC A = Div * MfrhoI * G if self.myTest == 'xc': # TODO: fix the null space Ainv = Solver(A) xc = Ainv * rhs err = np.linalg.norm((xc - xc_ana), np.inf) else: NotImplementedError return err
def MccRhoiDerivMat(self): """ Derivative of MccRho with respect to the model """ if getattr(self, '_MccRhoiDerivMat', None) is None: rho = self.rho vol = self.mesh.vol drho_dlogrho = Utils.sdiag(rho) * self.etaDeriv self._MccRhoiDerivMat = (Utils.sdiag(vol * (-1. / rho**2)) * drho_dlogrho) return self._MccRhoiDerivMat
def MnSigmaDerivMat(self): """ Derivative of MnSigma with respect to the model """ if getattr(self, '_MnSigmaDerivMat', None) is None: sigma = self.sigma vol = self.mesh.vol dsigma_dlogsigma = Utils.sdiag(sigma) * self.etaDeriv self._MnSigmaDerivMat = (self.mesh.aveN2CC.T * Utils.sdiag(vol) * dsigma_dlogsigma) return self._MnSigmaDerivMat
def MnSigma(self): """ Node inner product matrix for \\(\\sigma\\). Used in the E-B formulation """ # TODO: only works isotropic sigma sigma = self.curModel.sigma vol = self.mesh.vol MnSigma = Utils.sdiag(self.mesh.aveN2CC.T * (Utils.sdiag(vol) * sigma)) return MnSigma
def faceDivz(self): """ Construct divergence operator in the z component (face-stg to cell-centres). """ if getattr(self, "_faceDivz", None) is None: D3 = Utils.kron3(Utils.ddx(self.nCz), Utils.speye(self.nCy), Utils.speye(self.nCx)) S = self.r(self.area, "F", "Fz", "V") V = self.vol self._faceDivz = Utils.sdiag(1 / V) * D3 * Utils.sdiag(S) return self._faceDivz
def casing_currents(j, mesh, model_parameters): """ Compute the current (A) within the casing :param numpy.ndarray j: current density :param discretize.BaseMesh mesh: the discretize mesh which the casing is on :param casingSimulations.model.BaseCasingParametersMixin: a model with casing :return: :code:`(ix_casing, iz_casing)` """ casing_a = model_parameters.casing_a casing_b = model_parameters.casing_b casing_z = model_parameters.casing_z IxCasing = {} IzCasing = {} casing_faces_x = ((mesh.gridFx[:, 0] >= casing_a) & (mesh.gridFx[:, 0] <= casing_b) & (mesh.gridFx[:, 2] <= casing_z[1]) & (mesh.gridFx[:, 2] >= casing_z[0])) casing_faces_y = np.zeros(mesh.nFy, dtype=bool) casing_faces_z = ((mesh.gridFz[:, 0] >= casing_a) & (mesh.gridFz[:, 0] <= casing_b) & (mesh.gridFz[:, 2] <= casing_z[1]) & (mesh.gridFz[:, 2] >= casing_z[0])) jA = Utils.sdiag(mesh.area) * j jACasing = Utils.sdiag( np.hstack([casing_faces_x, casing_faces_y, casing_faces_z])) * jA jxCasing = jACasing[:mesh.nFx, :].reshape(mesh.vnFx[0], mesh.vnFx[1], mesh.vnFx[2], order='F') jzCasing = jACasing[mesh.nFx + mesh.nFy:, :].reshape(mesh.vnFz[0], mesh.vnFz[1], mesh.vnFz[2], order='F') ixCasing = jxCasing.sum(0).sum(0) izCasing = jzCasing.sum(0).sum(0) ix_inds = (mesh.vectorCCz > casing_z[0]) & (mesh.vectorCCz < casing_z[1]) z_ix = mesh.vectorCCz[ix_inds] iz_inds = (mesh.vectorNz > casing_z[0]) & (mesh.vectorNz < casing_z[1]) z_iz = mesh.vectorNz[iz_inds] return {"x": (z_ix, ixCasing[ix_inds]), "z": (z_iz, izCasing[iz_inds])}
def MfRhoIDeriv(self, u): """ Derivative of :code:`MfRhoI` with respect to the model. """ dMfRhoI_dI = -self.MfRhoI**2 dMf_drho = self.mesh.getFaceInnerProductDeriv(self.rho)(u) if self.storeJ: drho_dlogrho = Utils.sdiag(self.rho)*self.actMap.P else: drho_dlogrho = Utils.sdiag(self.rho) return dMfRhoI_dI * (dMf_drho * drho_dlogrho)
def MeSigmaDeriv(self, u): """ Derivative of MeSigma with respect to the model """ if self.storeJ: dsigma_dlogsigma = Utils.sdiag(self.sigma)*self.actMap.P else: dsigma_dlogsigma = Utils.sdiag(self.sigma) return ( self.mesh.getEdgeInnerProductDeriv(self.sigma)(u) * dsigma_dlogsigma )
def MfRhoDerivMat(self): """ Derivative of MfRho with respect to the model """ if getattr(self, '_MfRhoDerivMat', None) is None: if self.storeJ: drho_dlogrho = Utils.sdiag(self.rho) * self.actMap.P else: drho_dlogrho = Utils.sdiag(self.rho) self._MfRhoDerivMat = self.mesh.getFaceInnerProductDeriv( np.ones(self.mesh.nC))(np.ones(self.mesh.nF)) * drho_dlogrho return self._MfRhoDerivMat
def faceDivz(self): """ Construct divergence operator in the z component (face-stg to cell-centres). """ if getattr(self, '_faceDivz', None) is None: D3 = Utils.kron3(Utils.ddx(self.nCz), Utils.speye(self.nCy), Utils.speye(self.nCx)) S = self.r(self.area, 'F', 'Fz', 'V') V = self.vol self._faceDivz = Utils.sdiag(1/V)*D3*Utils.sdiag(S) return self._faceDivz
def MccRhoiDerivMat(self): """ Derivative of MccRho with respect to the model """ if getattr(self, '_MccRhoiDerivMat', None) is None: rho = self.rho vol = self.mesh.vol drho_dlogrho = Utils.sdiag(rho)*self.etaDeriv self._MccRhoiDerivMat = ( Utils.sdiag(vol*(-1./rho**2))*drho_dlogrho ) return self._MccRhoiDerivMat
def MnSigmaDerivMat(self): """ Derivative of MnSigma with respect to the model """ if getattr(self, '_MnSigmaDerivMat', None) is None: sigma = self.sigma vol = self.mesh.vol dsigma_dlogsigma = Utils.sdiag(sigma)*self.etaDeriv self._MnSigmaDerivMat = ( self.mesh.aveN2CC.T * Utils.sdiag(vol) * dsigma_dlogsigma ) return self._MnSigmaDerivMat
def MnSigma(self): """ Node inner product matrix for \\(\\sigma\\). Used in the E-B formulation """ # TODO: only works isotropic sigma if getattr(self, '_MnSigma', None) is None: sigma = self.sigma vol = self.mesh.vol self._MnSigma = Utils.sdiag(self.mesh.aveN2CC.T * (Utils.sdiag(vol) * sigma)) return self._MnSigma
def MeSigmaDerivMat(self): """ Derivative of MeSigma with respect to the model """ if getattr(self, '_MeSigmaDerivMat', None) is None: if self.storeJ: dsigma_dlogsigma = Utils.sdiag(self.sigma) * self.actMap.P else: dsigma_dlogsigma = Utils.sdiag(self.sigma) self._MeSigmaDerivMat = self.mesh.getEdgeInnerProductDeriv( np.ones(self.mesh.nC))(np.ones( self.mesh.nE)) * dsigma_dlogsigma return self._MeSigmaDerivMat
def MeSigmaDerivMat(self): """ Derivative of MeSigma with respect to the model """ if getattr(self, '_MeSigmaDerivMat', None) is None: if self.storeJ: dsigma_dlogsigma = Utils.sdiag(self.sigma)*self.actMap.P else: dsigma_dlogsigma = Utils.sdiag(self.sigma) self._MeSigmaDerivMat = self.mesh.getEdgeInnerProductDeriv( np.ones(self.mesh.nC) )(np.ones(self.mesh.nE)) * dsigma_dlogsigma return self._MeSigmaDerivMat
def getADeriv(self, ky, u, v, adjoint=False): D = self.Div G = self.Grad vol = self.mesh.vol MfRhoIDeriv = self.MfRhoIDeriv rho = self.curModel.rho if adjoint: return ((MfRhoIDeriv(G * u).T) * (D.T * v) + ky**2 * Utils.sdiag(u.flatten() * vol * (-1. / rho**2)) * v) return (D * ((MfRhoIDeriv(G * u)) * v) + ky**2 * Utils.sdiag(u.flatten() * vol * (-1. / rho**2)) * v)
def MfRhoDerivMat(self): """ Derivative of MfRho with respect to the model """ if getattr(self, '_MfRhoDerivMat', None) is None: if self.storeJ: drho_dlogrho = Utils.sdiag(self.rho)*self.actMap.P else: drho_dlogrho = Utils.sdiag(self.rho) self._MfRhoDerivMat = self.mesh.getFaceInnerProductDeriv( np.ones(self.mesh.nC) )(np.ones(self.mesh.nF)) * drho_dlogrho return self._MfRhoDerivMat
def getError(self): # Test function def phi_fun(x): return np.cos(np.pi*x) def j_fun(x): return np.pi*np.sin(np.pi*x) def phi_deriv(x): return -j_fun(x) def q_fun(x): return (np.pi**2)*np.cos(np.pi*x) xc_ana = phi_fun(self.M.gridCC) q_ana = q_fun(self.M.gridCC) j_ana = j_fun(self.M.gridFx) # Get boundary locations vecN = self.M.vectorNx vecC = self.M.vectorCCx # Setup Mixed B.C (alpha, beta, gamma) alpha_xm, alpha_xp = 1., 1. beta_xm, beta_xp = 1., 1. alpha = np.r_[alpha_xm, alpha_xp] beta = np.r_[beta_xm, beta_xp] vecN = self.M.vectorNx vecC = self.M.vectorCCx phi_bc = phi_fun(vecN[[0, -1]]) phi_deriv_bc = phi_deriv(vecN[[0, -1]]) gamma = alpha*phi_bc + beta*phi_deriv_bc x_BC, y_BC = getxBCyBC_CC(self.M, alpha, beta, gamma) sigma = np.ones(self.M.nC) Mfrho = self.M.getFaceInnerProduct(1./sigma) MfrhoI = self.M.getFaceInnerProduct(1./sigma, invMat=True) V = Utils.sdiag(self.M.vol) Div = V*self.M.faceDiv P_BC, B = self.M.getBCProjWF_simple() q = q_fun(self.M.gridCC) M = B*self.M.aveCC2F G = Div.T - P_BC*Utils.sdiag(y_BC)*M # Mrhoj = D.T V phi + P_BC*Utils.sdiag(y_BC)*M phi - P_BC*x_BC rhs = V*q + Div*MfrhoI*P_BC*x_BC A = Div*MfrhoI*G if self.myTest == 'xc': # TODO: fix the null space Ainv = Solver(A) xc = Ainv*rhs err = np.linalg.norm((xc-xc_ana), np.inf) else: NotImplementedError return err
def faceDivy(self): """ Construct divergence operator in the y component (face-stg to cell-centres). """ raise NotImplementedError("Wrapping the Utils.ddx is not yet " "implemented.") if getattr(self, "_faceDivy", None) is None: # TODO: this needs to wrap to join up faces which are # connected in the cylinder D2 = Utils.kron3(Utils.speye(self.nCz), Utils.ddx(self.nCy), Utils.speye(self.nCx)) S = self.r(self.area, "F", "Fy", "V") V = self.vol self._faceDivy = Utils.sdiag(1 / V) * D2 * Utils.sdiag(S) return self._faceDivy
def test_sum_fail(self): nP1 = 10 nP2 = 30 phi1 = ObjectiveFunction.L2ObjectiveFunction( W=Utils.sdiag(np.random.rand(nP1))) phi2 = ObjectiveFunction.L2ObjectiveFunction( W=Utils.sdiag(np.random.rand(nP2))) with self.assertRaises(Exception): phi = phi1 + phi2 with self.assertRaises(Exception): phi = phi1 + 100 * phi2
def BiotSavartFun(mesh, r_pts, component="z"): """ Compute systematrix G using Biot-Savart Law G = np.vstack((G1,G2,G3..,Gnpts) .. math:: """ if r_pts.ndim == 1: npts = 1 else: npts = r_pts.shape[0] e = np.ones((mesh.nC, 1)) o = np.zeros((mesh.nC, 1)) const = mu_0 / 4 / np.pi G = np.zeros((npts, mesh.nC * 3)) for i in range(npts): if npts == 1: r_rx = np.repeat(Utils.mkvc(r_pts).reshape([1, -1]), mesh.nC, axis=0) else: r_rx = np.repeat(r_pts[i, :].reshape([1, -1]), mesh.nC, axis=0) r_CC = mesh.gridCC r = r_rx - r_CC r_abs = np.sqrt((r**2).sum(axis=1)) rxind = r_abs == 0.0 # r_abs[rxind] = mesh.vol.min()**(1./3.)*0.5 r_abs[rxind] = 1e20 Sx = const * Utils.sdiag(mesh.vol * r[:, 0] / r_abs**3) Sy = const * Utils.sdiag(mesh.vol * r[:, 1] / r_abs**3) Sz = const * Utils.sdiag(mesh.vol * r[:, 2] / r_abs**3) # G_temp = sp.vstack((sp.hstack(( o.T, e.T*Sz, -e.T*Sy)), \ # sp.hstack((-e.T*Sz, o.T, e.T*Sx)), \ # sp.hstack((-e.T*Sy, e.T*Sx, o.T )))) if component == "x": G_temp = np.hstack((o.T, e.T * Sz, -e.T * Sy)) elif component == "y": G_temp = np.hstack((-e.T * Sz, o.T, e.T * Sx)) elif component == "z": G_temp = np.hstack((e.T * Sy, -e.T * Sx, o.T)) G[i, :] = G_temp return G
def CasingCurrents(cp, fields, mesh, survey): IxCasing = {} IzCasing = {} # casing_ind = sigma_m.copy() # casing_ind[[0, 1, 3]] = 0. # zero outside casing # casing_ind[2] = 1. # 1 inside casing # actMap_Zeros = Maps.InjectActiveCells(mesh, indActive, 0.) # indCasing = actMap_Zeros * casingMap * casing_ind # casing_faces = mesh.aveF2CC.T * indCasing # casing_faces[casing_faces < 0.25] = 0 casing_faces_x = ((mesh.gridFx[:, 0] >= cp.casing_a) & (mesh.gridFx[:, 0] <= cp.casing_b) & (mesh.gridFx[:, 2] <= cp.casing_z[1]) & (mesh.gridFx[:, 2] >= cp.casing_z[0])) casing_faces_z = ((mesh.gridFz[:, 0] >= cp.casing_a) & (mesh.gridFz[:, 0] <= cp.casing_b) & (mesh.gridFz[:, 2] <= cp.casing_z[1]) & (mesh.gridFz[:, 2] >= cp.casing_z[0])) for mur in cp.muModels: j = fields[mur][:, 'j'] jA = Utils.sdiag(mesh.area) * j jACasing = Utils.sdiag(np.hstack([casing_faces_x, casing_faces_z ])) * jA ixCasing = [] izCasing = [] for ind in range(len(survey.srcList)): jxCasing = jACasing[:mesh.nFx, ind].reshape(mesh.vnFx[0], mesh.vnFx[2], order='F') jzCasing = jACasing[mesh.nFx:, ind].reshape(mesh.vnFz[0], mesh.vnFz[2], order='F') ixCasing.append(jxCasing.sum(0)) izCasing.append(jzCasing.sum(0)) IxCasing[mur] = ixCasing IzCasing[mur] = izCasing return IxCasing, IzCasing
def CasingMagDipole2Deriv_z_z(z): obsloc = np.vstack([xobs, yobs, z]).T f = Casing._getCasingHertzMagDipoleDeriv_z(srcloc, obsloc, freq, sigma, a, b, mu) g = Utils.sdiag(Casing._getCasingHertzMagDipole2Deriv_z_z(srcloc, obsloc, freq, sigma, a, b, mu)) return f, g
def CasingMagDipoleDeriv_r(x): obsloc = np.vstack([x, yobs, zobs]).T f = Casing._getCasingHertzMagDipole(srcloc, obsloc, freq, sigma, a, b, mu) g = Utils.sdiag(Casing._getCasingHertzMagDipoleDeriv_r(srcloc, obsloc, freq, sigma, a, b, mu)) return f, g
def getInterpolationMatCartMesh(self, Mrect, locType="CC", locTypeTo=None): """ Takes a cartesian mesh and returns a projection to translate onto the cartesian grid. """ assert self.isSymmetric, ( "Currently we have not taken into account " "other projections for more complicated " "CylMeshes" ) if locTypeTo is None: locTypeTo = locType if locType == "F": # do this three times for each component X = self.getInterpolationMatCartMesh(Mrect, locType="Fx", locTypeTo=locTypeTo + "x") Y = self.getInterpolationMatCartMesh(Mrect, locType="Fy", locTypeTo=locTypeTo + "y") Z = self.getInterpolationMatCartMesh(Mrect, locType="Fz", locTypeTo=locTypeTo + "z") return sp.vstack((X, Y, Z)) if locType == "E": X = self.getInterpolationMatCartMesh(Mrect, locType="Ex", locTypeTo=locTypeTo + "x") Y = self.getInterpolationMatCartMesh(Mrect, locType="Ey", locTypeTo=locTypeTo + "y") Z = Utils.spzeros(getattr(Mrect, "n" + locTypeTo + "z"), self.nE) return sp.vstack((X, Y, Z)) grid = getattr(Mrect, "grid" + locTypeTo) # This is unit circle stuff, 0 to 2*pi, starting at x-axis, rotating # counter clockwise in an x-y slice theta = -np.arctan2(grid[:, 0] - self.cartesianOrigin[0], grid[:, 1] - self.cartesianOrigin[1]) + np.pi / 2 theta[theta < 0] += np.pi * 2.0 r = ((grid[:, 0] - self.cartesianOrigin[0]) ** 2 + (grid[:, 1] - self.cartesianOrigin[1]) ** 2) ** 0.5 if locType in ["CC", "N", "Fz", "Ez"]: G, proj = np.c_[r, theta, grid[:, 2]], np.ones(r.size) else: dotMe = { "Fx": Mrect.normals[: Mrect.nFx, :], "Fy": Mrect.normals[Mrect.nFx : (Mrect.nFx + Mrect.nFy), :], "Fz": Mrect.normals[-Mrect.nFz :, :], "Ex": Mrect.tangents[: Mrect.nEx, :], "Ey": Mrect.tangents[Mrect.nEx : (Mrect.nEx + Mrect.nEy), :], "Ez": Mrect.tangents[-Mrect.nEz :, :], }[locTypeTo] if "F" in locType: normals = np.c_[np.cos(theta), np.sin(theta), np.zeros(theta.size)] proj = (normals * dotMe).sum(axis=1) if "E" in locType: tangents = np.c_[-np.sin(theta), np.cos(theta), np.zeros(theta.size)] proj = (tangents * dotMe).sum(axis=1) G = np.c_[r, theta, grid[:, 2]] interpType = locType if interpType == "Fy": interpType = "Fx" elif interpType == "Ex": interpType = "Ey" Pc2r = self.getInterpolationMat(G, interpType) Proj = Utils.sdiag(proj) return Proj * Pc2r
def fields(self, m): """ Return magnetic potential (u) and flux (B) u: defined on the cell nodes [nC x 1] gField: defined on the cell faces [nF x 1] After we compute u, then we update B. .. math :: \mathbf{B}_s = (\MfMui)^{-1}\mathbf{M}^f_{\mu_0^{-1}}\mathbf{B}_0-\mathbf{B}_0 -(\MfMui)^{-1}\Div^T \mathbf{u} """ from scipy.constants import G as NewtG self.makeMassMatrices(m) A = self.getA(m) RHS = self.getRHS(m) if self.solver is None: m1 = sp.linalg.interface.aslinearoperator(Utils.sdiag(1/A.diagonal())) u, info = sp.linalg.bicgstab(A, RHS, tol=1e-6, maxiter=1000, M=m1) else: print("Solving with Paradiso") Ainv = self.solver(A) u = Ainv*RHS gField = 4.*np.pi*NewtG*1e+8*self._Div*u return {'G': gField, 'u': u}
def transformDerivU(self, u, m): self.setModel(m) alpha = self.alpha I = self.I n = self.n Ks = self.Ks m = 1.0 - 1.0 / n g = I * alpha * n * np.exp(Ks) * abs(alpha * u) ** (n - 1.0) * np.sign(alpha * u) * (1.0 / n - 1.0) * ( (abs(alpha * u) ** n + 1) ** (1.0 / n - 1) ) ** (I - 1) * ( (1 - 1.0 / ((abs(alpha * u) ** n + 1) ** (1.0 / n - 1)) ** (1.0 / (1.0 / n - 1))) ** (1 - 1.0 / n) - 1 ) ** 2 * ( abs(alpha * u) ** n + 1 ) ** ( 1.0 / n - 2 ) - ( 2 * alpha * n * np.exp(Ks) * abs(alpha * u) ** (n - 1) * np.sign(alpha * u) * (1.0 / n - 1) * ((abs(alpha * u) ** n + 1) ** (1.0 / n - 1)) ** I * ((1 - 1.0 / ((abs(alpha * u) ** n + 1) ** (1.0 / n - 1)) ** (1.0 / (1.0 / n - 1))) ** (1 - 1.0 / n) - 1) * (abs(alpha * u) ** n + 1) ** (1.0 / n - 2) ) / ( ((abs(alpha * u) ** n + 1) ** (1.0 / n - 1)) ** (1.0 / (1.0 / n - 1) + 1) * (1 - 1.0 / ((abs(alpha * u) ** n + 1) ** (1.0 / n - 1)) ** (1.0 / (1.0 / n - 1))) ** (1.0 / n) ) g[u >= 0] = 0 g = Utils.sdiag(g) return g
def Wd(self): """getWd(survey) The data weighting matrix. The default is based on the norm of the data plus a noise floor. :rtype: scipy.sparse.csr_matrix :return: Wd """ if getattr(self, '_Wd', None) is None: survey = self.survey if getattr(survey,'std', None) is None: print('SimPEG.DataMisfit.l2_DataMisfit assigning default std of 5%') survey.std = 0.05 if getattr(survey, 'eps', None) is None: print('SimPEG.DataMisfit.l2_DataMisfit assigning default eps of 1e-5 * ||dobs||') survey.eps = np.linalg.norm(Utils.mkvc(survey.dobs),2)*1e-5 self._Wd = Utils.sdiag(1/(abs(survey.dobs)*survey.std+survey.eps)) return self._Wd
def makeMassMatrices(self, m): mu = self.muMap*m self._MfMui = self.mesh.getFaceInnerProduct(1./mu)/self.mesh.dim # self._MfMui = self.mesh.getFaceInnerProduct(1./mu) # TODO: this will break if tensor mu self._MfMuI = Utils.sdiag(1./self._MfMui.diagonal()) self._MfMu0 = self.mesh.getFaceInnerProduct(1./mu_0)/self.mesh.dim
def Jvec(self, m, v, f=None): J_sigma = self.getJ_sigma(m) J_height = self.getJ_height(m) # This is deprecated at the moment # if self.parallel and self.parallel_jvec_jtvec: # # Extra division of sigma is because: # # J_sigma = dF/dlog(sigma) # # And here sigmaMap also includes ExpMap # v_sigma = Utils.sdiag(1./self.sigma) * self.sigmaMap.deriv(m, v) # V_sigma = v_sigma.reshape((self.n_sounding, self.n_layer)) # pool = Pool(self.n_cpu) # Jv = np.hstack( # pool.map( # dot, # [(J_sigma[i], V_sigma[i, :]) for i in range(self.n_sounding)] # ) # ) # if self.hMap is not None: # v_height = self.hMap.deriv(m, v) # V_height = v_height.reshape((self.n_sounding, self.n_layer)) # Jv += np.hstack( # pool.map( # dot, # [(J_height[i], V_height[i, :]) for i in range(self.n_sounding)] # ) # ) # pool.close() # pool.join() # else: Jv = J_sigma*(Utils.sdiag(1./self.sigma)*(self.sigmaDeriv * v)) if self.hMap is not None: Jv += J_height*(self.hDeriv * v) return Jv
def Jtvec(self, m, v, f=None): J_sigma = self.getJ_sigma(m) J_height = self.getJ_height(m) # This is deprecated at the moment # if self.parallel and self.parallel_jvec_jtvec: # pool = Pool(self.n_cpu) # Jtv = np.hstack( # pool.map( # dot, # [(J_sigma[i].T, v[self.data_index[i]]) for i in range(self.n_sounding)] # ) # ) # if self.hMap is not None: # Jtv_height = np.hstack( # pool.map( # dot, # [(J_sigma[i].T, v[self.data_index[i]]) for i in range(self.n_sounding)] # ) # ) # # This assumes certain order for model, m = (sigma, height) # Jtv = np.hstack((Jtv, Jtv_height)) # pool.close() # pool.join() # return Jtv # else: # Extra division of sigma is because: # J_sigma = dF/dlog(sigma) # And here sigmaMap also includes ExpMap Jtv = self.sigmaDeriv.T * (Utils.sdiag(1./self.sigma) * (J_sigma.T*v)) if self.hMap is not None: Jtv += self.hDeriv.T*(J_height.T*v) return Jtv
def MccSigma(self): """ Diagonal matrix for \\(\\sigma\\). """ if getattr(self, '_MccSigma', None) is None: self._MccSigma = Utils.sdiag(self.sigma) return self._MccSigma
def MccEpsilon(self): """ Diagonal matrix for \\(\\epsilon\\). """ if getattr(self, '_MccEpsilon', None) is None: self._MccEpsilon = Utils.sdiag(self.epsilon) return self._MccEpsilon
def fields(self, m): """ Return gravity potential (u) and field (g) u: defined on the cell nodes [nC x 1] gField: defined on the cell faces [nF x 1] """ from scipy.constants import G as NewtG self.makeMassMatrices(m) A = self.getA(m) RHS = self.getRHS(m) if self.solver is None: m1 = sp.linalg.interface.aslinearoperator( Utils.sdiag(1 / A.diagonal()) ) u, info = sp.linalg.bicgstab(A, RHS, tol=1e-6, maxiter=1000, M=m1) else: print("Solving with Paradiso") Ainv = self.solver(A) u = Ainv * RHS gField = 4. * np.pi * NewtG * 1e+8 * self._Div * u return {'G': gField, 'u': u}
def test_early_exits(self): nP = 10 m = np.random.rand(nP) v = np.random.rand(nP) W1 = Utils.sdiag(np.random.rand(nP)) phi1 = ObjectiveFunction.L2ObjectiveFunction(W=W1) phi2 = Error_if_Hit_ObjFct() objfct = phi1 + 0 * phi2 self.assertTrue(len(objfct) == 2) self.assertTrue(np.all(objfct.multipliers == np.r_[1, 0])) self.assertTrue(objfct(m) == phi1(m)) self.assertTrue(np.all(objfct.deriv(m) == phi1.deriv(m))) self.assertTrue(np.all(objfct.deriv2(m, v) == phi1.deriv2(m, v))) objfct.multipliers[1] = Utils.Zero() self.assertTrue(len(objfct) == 2) self.assertTrue(np.all(objfct.multipliers == np.r_[1, 0])) self.assertTrue(objfct(m) == phi1(m)) self.assertTrue(np.all(objfct.deriv(m) == phi1.deriv(m))) self.assertTrue(np.all(objfct.deriv2(m, v) == phi1.deriv2(m, v)))
def test_basic(self): expMap = Maps.ExpMap(Mesh.TensorMesh((3,))) assert expMap.nP == 3 for Example in [SimpleExample, ShortcutExample]: PM = Example(sigmaMap=expMap) assert PM.sigmaMap is not None assert PM.sigmaMap is expMap # There is currently no model, so sigma, which is mapped, fails self.assertRaises(AttributeError, getattr, PM, 'sigma') PM.model = np.r_[1., 2., 3.] assert np.all(PM.sigma == np.exp(np.r_[1., 2., 3.])) # PM = pickle.loads(pickle.dumps(PM)) # PM = Maps.ExpMap.deserialize(PM.serialize()) assert np.all( PM.sigmaDeriv.todense() == Utils.sdiag(np.exp(np.r_[1., 2., 3.])).todense() ) # If we set sigma, we should delete the mapping PM.sigma = np.r_[1., 2., 3.] assert np.all(PM.sigma == np.r_[1., 2., 3.]) # PM = pickle.loads(pickle.dumps(PM)) assert PM.sigmaMap is None assert PM.sigmaDeriv == 0 del PM.model # sigma is not changed assert np.all(PM.sigma == np.r_[1., 2., 3.])
def V(self): """ :code:`V` """ if getattr(self, "_V", None) is None: self._V = Utils.sdiag(self.mesh.vol) return self._V
def test_early_exits(self): nP = 10 m = np.random.rand(nP) v = np.random.rand(nP) W1 = Utils.sdiag(np.random.rand(nP)) phi1 = ObjectiveFunction.L2ObjectiveFunction(W=W1) phi2 = Error_if_Hit_ObjFct() objfct = phi1 + 0*phi2 self.assertTrue(len(objfct) == 2) self.assertTrue(np.all(objfct.multipliers == np.r_[1, 0])) self.assertTrue(objfct(m) == phi1(m)) self.assertTrue(np.all(objfct.deriv(m) == phi1.deriv(m))) self.assertTrue(np.all(objfct.deriv2(m, v) == phi1.deriv2(m, v))) objfct.multipliers[1] = Utils.Zero() self.assertTrue(len(objfct) == 2) self.assertTrue(np.all(objfct.multipliers == np.r_[1, 0])) self.assertTrue(objfct(m) == phi1(m)) self.assertTrue(np.all(objfct.deriv(m) == phi1.deriv(m))) self.assertTrue(np.all(objfct.deriv2(m, v) == phi1.deriv2(m, v)))