def ldiv(a, b): """ Equivalent to matlab's a\b""" if a.shape[0] == a.shape[1] and a.shape[0] == b.shape[0]: lu, piv, x, info = dgesv(a, b, False, False) return x else: return lstsq(a, b, rcond=None)[0]
def run_dgesv(N,l): a = randn(N,N).astype('float64') b = randn(N,N).astype('float64') start = time.time(); for i in range(0,l): dgesv(a,b,1,1) end = time.time() timediff = (end -start) mflops = ( 2.0/3.0 *N*N*N + 2.0*N*N*N) *l / timediff mflops *= 1e-6 size = "%dx%d" % (N,N) print("%14s :\t%20f MFlops\t%20f sec" % (size,mflops,timediff))
def compute_inv_cov(npop, npk, kaiser, pk, nbar): """ Computes the covariance matrix of the auto and cross-power spectra for a given k and mu value, as well as its inverse. Parameters ---------- npop: int The number of different galaxy populations to consider. Used to compute all combinations necessary for the power spectra. npk: int The number of different auto and cross power spectra. Equivalent to npop*(npop+1)/2, but passed in to avoid recomputing for each k/mu value. kaiser: np.ndarray The kaiser factors for each galaxy population at a fixed mu and redshift. Has length npop. pk: float The power spectrum value at the given k, mu and redshift values. nbar: np.ndarray The number density in units of Mpc^3/h^3 for each of the npop samples at the current redshift. Returns ------- covariance: np.ndarray The covariance matrix between the various auto and cross-power spectra at a given k, mu and redshift. Includes shot noise and has size (npk, npk). cov_inv: np.ndarray The inverse of the covariance matrix between the various auto and cross-power spectra at a given k, mu and redshift. Includes shot noise and has size (npk, npk). """ covariance = np.empty((npk, npk)) # Loop over power spectra of different samples P_12 for ps1, pair1 in enumerate(combinations_with_replacement(range(npop), 2)): n1, n2 = pair1 # Loop over power spectra of different samples P_34 for ps2, pair2 in enumerate( combinations_with_replacement(range(npop), 2)): n3, n4 = pair2 # Cov(P_12,P_34) pk13, pk24 = kaiser[n1] * kaiser[n3] * pk, kaiser[n2] * kaiser[ n4] * pk pk14, pk23 = kaiser[n1] * kaiser[n4] * pk, kaiser[n2] * kaiser[ n3] * pk if n1 == n3: pk13 += 1.0 / nbar[n1] if n1 == n4: pk14 += 1.0 / nbar[n1] if n2 == n3: pk23 += 1.0 / nbar[n2] if n2 == n4: pk24 += 1.0 / nbar[n2] covariance[ps1, ps2] = pk13 * pk24 + pk14 * pk23 identity = np.eye(npk) cov_inv = dgesv(covariance, identity)[2] return covariance, cov_inv
def solver(vlmdata): """ Solve linear system for vortex strengths Args: :vlmdata: (object) data structure for VLM input and output """ logger.info("Solving linear system...") vlmdata.matrix_lu, vlmdata.array_pivots, vlmdata.panelwise['gamma'], _ \ = lapack.dgesv(vlmdata.matrix_downwash, vlmdata.array_rhs)
def _build_and_solve_system(y, d, smoothing, kernel, epsilon, powers): """Build and solve the RBF interpolation system of equations. Parameters ---------- y : (P, N) float ndarray Data point coordinates. d : (P, S) float ndarray Data values at `y`. smoothing : (P,) float ndarray Smoothing parameter for each data point. kernel : str Name of the RBF. epsilon : float Shape parameter. powers : (R, N) int ndarray The exponents for each monomial in the polynomial. Returns ------- coeffs : (P + R, S) float ndarray Coefficients for each RBF and monomial. shift : (N,) float ndarray Domain shift used to create the polynomial matrix. scale : (N,) float ndarray Domain scaling used to create the polynomial matrix. """ lhs, rhs, shift, scale = _build_system(y, d, smoothing, kernel, epsilon, powers) _, _, coeffs, info = dgesv(lhs, rhs, overwrite_a=True, overwrite_b=True) if info < 0: raise ValueError(f"The {-info}-th argument had an illegal value.") elif info > 0: msg = "Singular matrix." nmonos = powers.shape[0] if nmonos > 0: pmat = _polynomial_matrix((y - shift) / scale, powers) rank = np.linalg.matrix_rank(pmat) if rank < nmonos: msg = ("Singular matrix. The matrix of monomials evaluated at " "the data point coordinates does not have full column " f"rank ({rank}/{nmonos}).") raise LinAlgError(msg) return shift, scale, coeffs
def compute_cov_inv(data): if (data.cov is None): data.cov_det = None data.cov_inv = None else: # Compute the log determinant of the covariance matrix cov_copy, pivots, info = lapack.dgetrf(data.cov) abs_element = np.fabs(np.diagonal(cov_copy)) data.cov_det = np.sum(np.log(abs_element)) # Invert the covariance matrix identity = np.eye(len(data.x)) cov_lu, pivots, cov_inv, info = lapack.dgesv(data.cov, identity) data.cov_inv = cov_inv return data
def diagonalize_q_matrix(self): # type: () -> None """ calculate Q matrix and eigenvalues and eigenvectors if necessary c.f. Felsenstein - Inferring Phylogenies (chapter 13) The naming of the variables follows the naming in Felsenstein. """ # if self.necessary: self.freq_log = np.log(self.frequencies) self.create_q_c() # calculate root self.d_mat = np.sqrt(self.d_mat) self.b_mat = np.asfortranarray(self.b_mat) d_b_d = np.empty((self._size, self._size), dtype=np.float64, order="F") # get symmetric matrix # use SciPys BLAS wrappers c = sp_la.blas.dgemm(alpha=1.0, a=self.d_mat, b=self.b_mat) sp_la.blas.dgemm(alpha=1.0, a=c, b=self.d_mat, c=d_b_d, overwrite_c=1) # get eigenvalues and eigenvectors self.q_eigenvalues, u_mat = np_la.eigh(d_b_d) # calculate actual eigenvectors of Q-Matrix # use SciPys BLAS wrappers u_mat = np.asfortranarray(u_mat) sp_la.blas.dgemm(alpha=1.0, a=self.d_mat, b=u_mat, c=self.q_eigenvectors, overwrite_c=1) self.q_eigenvectors_inv = sp_la_lp.dgesv(self.q_eigenvectors, self._eye)[2] self.q_eigenvectors_inv = np.asfortranarray(self.q_eigenvectors_inv) self.necessary = False
ExtraCatch = Fish( cosmo, cosmo.kmax, 0.5, data, iz, recon[iz], derPalpha_BAO_only, True, pardict.as_bool("GoFast"), ) Catch[-2:, -2:] += ExtraCatch[-2:, -2:] # print(Catch) # Invert the Fisher matrix to get the parameter covariance matrix cov = dgesv(Catch, identity)[2] # Renormalise the covariance from fsigma8, alpha_perp, alpha_par to fsigma8, Da, H means = [cosmo.f[iz] * cosmo.sigma8[iz], cosmo.da[iz], cosmo.h[iz]] cov_renorm = CovRenorm(cov, means) # Print the parameter means and errors errs = 100.0 * np.sqrt(np.diag(cov_renorm)[-3:]) / means print( " {0:.2f} {1:.4f} {2:.3f} {3:.2f} {4:.1f} {5:.2f} {6:.1f} {7:.2f}" .format( cosmo.z[iz], cosmo.volume[iz] / 1e9, means[0], errs[0], means[1],
def HydrostaticShape(radius, rho, omega, gm, r_ref, rp=None, mp=None): """ Calculate the shape of hydrostatic relief in a rotating planet or moon, along with the total gravitation potential. For the case of a moon in synchronous rotation, optionally include the tidal potential. Usage ----- hlm, clm_hydro, mass = HydrostaticFlatteningLith(radius, density, omega, gm, r_ref, [rp, mp]) Returns ------- hlm : array of SHCoeffs class instances, size(n+1) Array of SHCoeffs class instances of the spherical harmonic coefficients of the hydrostatic relief at each interface. clm_hydro : SHCoeffs class instance containing the gravitational potential resulting from all hydrostatic interfaces. mass : float Total mass of the planet, assuming a spherical shape and the provided 1D density profile. Parameters ---------- radius : ndarray, float, size(n+1) Radius of each density interface, where index 0 corresponds to the center of the planet and n corresponds to the surface. density : ndarray, float, size(n+1) Density of layer i between radius[i] and radius[i+1]. The density at index 0 is from the center of the planet to radius[1], whereas the the density at index n should be zero. omega : float Angular rotation rate of the planet. gm : float GM of the planet. r_ref : float Refernce radius for output potential coefficients. rp : float, optional, default = None If specified, include the tidal potential acting on a synchronously rotating moon, where rp is the average distance between the planet and satellite. mp : float, optional, default = None The mass of the host planet, at a distance rp from the satellite. """ tides = False if rp is not None: if mp is None: raise ValueError('When including tides, both rp and mp must be ' + 'specified.') tides = True if mp is not None: if rp is None: raise ValueError('When including tides, both rp and mp must be ' + 'specified.') tides = True if len(radius) != len(rho): raise ('Length of radius and density must be the same.' + 'len(radius) = {:d}. len(density) = {:d}.'.format( len(radius), len(rho))) n = len(radius) - 1 # index of surface lmax = 4 g = pyshtools.constant.G.value hlm = [pyshtools.SHCoeffs.from_zeros(lmax) for i in range(n + 1)] clm_hydro = pyshtools.SHCoeffs.from_zeros(lmax) for i in range(n + 1): hlm[i].coeffs[0, 0, 0] = radius[i] # First determine the spherical harmonic coefficients of (Y20 Ylm) # and for tides, (Y22 Ylm). We are only concerned with the coefficient # that corresponds to lm, so for each lm, store only the lm component in # the array cp20 and cp22. sh20 = np.zeros((2, lmax + 1, lmax + 1)) sh22 = np.zeros((2, lmax + 1, lmax + 1)) sh = np.zeros((2, lmax + 1, lmax + 1)) cp20 = np.zeros((2, lmax + 1, lmax + 1)) cp22 = np.zeros((2, lmax + 1, lmax + 1)) sh20[0, 2, 0] = 1. # Y20 sh22[0, 2, 2] = 1. # Y22 for l in range(2, lmax + 1): for m in range(0, l + 1): sh[0, l, m] = 1. coeffs = pyshtools.expand.SHMultiply(sh20, sh) cp20[0, l, m] = coeffs[0, l, m] if m != 0: cp20[1, l, m] = cp20[0, l, m] if l == 2 and m == 0: p402020 = coeffs[0, 4, 0] if l == 2 and m == 1: p412021 = coeffs[0, 4, 1] if l == 2 and m == 2: p422022 = coeffs[0, 4, 2] coeffs = pyshtools.expand.SHMultiply(sh22, sh) cp22[0, l, m] = coeffs[0, l, m] sh[0, l, m] = 0. if m > 0: sh[1, l, m] = 1. coeffs = pyshtools.expand.SHMultiply(sh22, sh) cp22[1, l, m] = coeffs[1, l, m] sh[1, l, m] = 0. # Calculate delta_rho drho = np.zeros(n + 1) mass = np.zeros(n + 1) for i in range(1, n + 1): drho[i] = rho[i - 1] - rho[i] # Calculate matrix A and invert for relief. a = np.zeros((n + 1, n + 1)) atides = np.zeros((2, n + 1)) b4 = np.zeros((2, 3, n + 1)) # Calculate cumulate mass function for i in range(1, n + 1): if i == 1: mass[1] = 4. * np.pi * radius[1]**3 * rho[0] / 3. else: mass[i] = mass[i-1] + 4. * np.pi * \ (radius[i]**3 - radius[i-1]**3) * rho[i-1] / 3. mass_model = mass[n] for l in range(2, lmax + 1, 2): for m in range(0, lmax + 1): for i in range(1, n + 1): # zero index not computed for j in range(1, n + 1): if i == j: # cp20 for sin and cosine terms are equal a[i, j] = 4. * np.pi * g * drho[i] * radius[i] / \ (2. * l + 1.) - g * mass[i] / radius[i]**2 + \ (2./3.) * radius[i] * omega**2 * \ (1. - cp20[0, l, m] / np.sqrt(5.0)) elif j < i: a[i, j] = 4. * np.pi * g * drho[j] * \ radius[j]**(l+2) / (2. * l + 1.) / \ radius[i]**(l+1) else: a[i, j] = 4. * np.pi * g * drho[j] * \ radius[i]**l / (2. * l + 1.) / \ radius[j]**(l-1) if tides is True: atides[0, i] = g * mp * radius[i] / rp**3 * ( -np.sqrt(5.) / 5. * cp20[0, l, m] + np.sqrt(12. / 5.) * cp22[0, l, m] / 2.) atides[1, i] = g * mp * radius[i] / rp**3 * ( -np.sqrt(5.) / 5. * cp20[1, l, m] + np.sqrt(12. / 5.) * cp22[1, l, m] / 2.) # --- do cosine term --- b = np.zeros(n + 1) if l == 2 and m == 0: for i in range(1, n + 1): b[i] = (omega * radius[i])**2 / (3. * np.sqrt(5.)) if tides: b[i] += g * mp * radius[i]**2 / rp**3 * \ np.sqrt(5.) / 10. if l == 2 and m == 2 and tides: for i in range(1, n + 1): b[i] = - g * mp * radius[i]**2 / rp**3 * \ np.sqrt(12./5.) / 4. # Add contributions from degree 2 relief to degree 4. if l == 4 and m <= 2: b[1:n + 1] = b4[0, m, 1:n + 1] # solve the linear equation A h = b atemp = a.copy() if tides: for i in range(1, n + 1): atemp[i, i] += atides[0, i] btemp = b.copy() # note that the zero index is not used lu, piv, x, info = lapack.dgesv(atemp[1:, 1:], btemp[1:]) if info != 0: raise ("lapack.dgesv did not exit properly: {:d}", info) for i in range(1, n + 1): hlm[i].coeffs[0, l, m] = x[i - 1] # calculate b4 contribution if l == 2: for i in range(1, n + 1): if m == 0: b4[0, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[0, l, m] * p402020 elif m == 1: b4[0, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[0, l, m] * p412021 elif m == 2: b4[0, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[0, l, m] * p422022 # --- do sine term --- b = np.zeros(n + 1) if m != 0: # Add contributions from degree 2 relief to degree 4. if l == 4 and m <= 2: b[1:n + 1] = b4[1, m, 1:n + 1] # solve the linear equation A h = b atemp = a.copy() if tides: for i in range(1, n + 1): atemp[i, i] += atides[1, i] btemp = b.copy() # note that the zero index is not used lu, piv, x, info = lapack.dgesv(atemp[1:, 1:], btemp[1:]) if info != 0: raise ("lapack.dgesv did not exit properly: {:d}", info) for i in range(1, n + 1): hlm[i].coeffs[1, l, m] = x[i - 1] # calculate b4 contribution if l == 2: for i in range(1, n + 1): if m == 1: b4[1, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[1, l, m] * p412021 elif m == 2: b4[1, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[1, l, m] * p422022 # Calculate potential at r_ref resulting from all interfaces coeffs = np.zeros((2, lmax + 1, lmax + 1)) for i in range(1, n + 1): for l in range(2, lmax + 1): coeffs[:, l, :l+1] += hlm[i].coeffs[:, l, :l+1] * 4. * \ np.pi * drho[i] * radius[i]**2 * (radius[i] / r_ref)**l * \ g / gm / (2. * l + 1.) clm_hydro = pyshtools.SHGravCoeffs.from_array(coeffs, gm=gm, r0=r_ref, omega=omega) return hlm, clm_hydro, mass_model
def HydrostaticShape(radius, rho, omega, gm, r_ref, finiteamplitude=False, rp=None, mp=None, nmax=7, kmax=4): """ Calculate the shape of hydrostatic relief in a rotating planet or moon, along with the total gravitation potential. For the case of a moon in synchronous rotation, optionally include the tidal potential. Usage ----- hlm, clm_hydro, mass = HydrostaticFlatteningLith(radius, density, omega, gm, r_ref, [finiteamplitude, rp, mp, nmax]) Returns ------- hlm : array of SHCoeffs class instances, size(n+1) Array of SHCoeffs class instances of the spherical harmonic coefficients of the hydrostatic relief at each interface. clm_hydro : SHCoeffs class instance containing the gravitational potential resulting from all hydrostatic interfaces. mass : float Total mass of the planet, assuming a spherical shape and the provided 1D density profile. Parameters ---------- radius : ndarray, float, size(n+1) Radius of each density interface, where index 0 corresponds to the center of the planet and n corresponds to the surface. density : ndarray, float, size(n+1) Density of layer i between radius[i] and radius[i+1]. The density at index 0 is from the center of the planet to radius[1], whereas the the density at index n should be zero. omega : float Angular rotation rate of the planet. gm : float GM of the planet. r_ref : float Refernce radius for output potential coefficients. finiteamplitude : bool, optional, default = False If True, compute finite amplitude terms when calculating the gravitational potentials. rp : float, optional, default = None If specified, include the tidal potential acting on a synchronously rotating moon, where rp is the average distance between the planet and satellite. mp : float, optional, default = None The mass of the host planet, at a distance rp from the satellite. nmax : int, optional, default = 7 The order of the approximation when computing the gravitational potential. kmax : int, optionalm, defaut = 4 Number of iterations to perform when calculating the finite-amplitude correction. """ tides = False if rp is not None: if mp is None: raise ValueError('When including tides, both rp and mp must be ' + 'specified.') tides = True if mp is not None: if rp is None: raise ValueError('When including tides, both rp and mp must be ' + 'specified.') tides = True if len(radius) != len(rho): raise ('Length of radius and density must be the same.' + 'len(radius) = {:d}. len(density) = {:d}.'.format( len(radius), len(rho))) n = len(radius) - 1 # index of surface lmax = 4 lmaxgrid = 7 * lmax # increase grid size to avoid aliasing g = float(pyshtools.constant.grav_constant) hlm = [pyshtools.SHCoeffs.from_zeros(lmax) for i in range(n + 1)] clm_hydro = pyshtools.SHCoeffs.from_zeros(lmax) for i in range(n + 1): hlm[i].coeffs[0, 0, 0] = radius[i] if finiteamplitude: dcminus = np.zeros((n + 1, 2, lmax + 1, lmax + 1)) dcplus = np.zeros((n + 1, 2, lmax + 1, lmax + 1)) else: kmax = 1 # First determine the spherical harmonic coefficients of (Y20 Ylm) # and for tides, (Y22 Ylm). We are only concerned with the coefficient # that corresponds to lm, so for each lm, store only the lm component in # the array cp20 and cp22. sh20 = np.zeros((2, lmax + 1, lmax + 1)) sh22 = np.zeros((2, lmax + 1, lmax + 1)) sh = np.zeros((2, lmax + 1, lmax + 1)) cp20 = np.zeros((2, lmax + 1, lmax + 1)) cp22 = np.zeros((2, lmax + 1, lmax + 1)) sh20[0, 2, 0] = 1. # Y20 sh22[0, 2, 2] = 1. # Y22 for l in range(2, lmax + 1): for m in range(0, l + 1): sh[0, l, m] = 1. coeffs = pyshtools.expand.SHMultiply(sh20, sh) cp20[0, l, m] = coeffs[0, l, m] if m != 0: cp20[1, l, m] = cp20[0, l, m] if l == 2 and m == 0: p402020 = coeffs[0, 4, 0] if l == 2 and m == 1: p412021 = coeffs[0, 4, 1] if l == 2 and m == 2: p422022 = coeffs[0, 4, 2] coeffs = pyshtools.expand.SHMultiply(sh22, sh) cp22[0, l, m] = coeffs[0, l, m] sh[0, l, m] = 0. if m > 0: sh[1, l, m] = 1. coeffs = pyshtools.expand.SHMultiply(sh22, sh) cp22[1, l, m] = coeffs[1, l, m] sh[1, l, m] = 0. # Calculate delta_rho drho = np.zeros(n + 1) mass = np.zeros(n + 1) for i in range(1, n + 1): drho[i] = rho[i - 1] - rho[i] # Calculate matrix A and invert for relief. a = np.zeros((n + 1, n + 1)) atides = np.zeros((2, n + 1)) b4 = np.zeros((2, 3, n + 1)) mass_sheet = np.zeros((2, lmax + 1, lmax + 1)) for k in range(kmax): # calculate mass, taking into account the flattening of each interface for i in range(1, n + 1): if i == 1: mass[1] = 4. * np.pi * radius[1]**3 * rho[0] / 3. + \ 4. * np.pi * rho[0] * radius[1] * \ hlm[1].coeffs[0, 2, 0]**2 + 8. * np.pi * np.sqrt(5.) / \ 21. * rho[0] * hlm[1].coeffs[0, 2, 0]**3 else: mass[i] = mass[i-1] + 4. * np.pi * \ (radius[i]**3 - radius[i-1]**3) * rho[i-1] / 3. + \ 4. * np.pi * rho[i-1] * (radius[i] * hlm[i].coeffs[0, 2, 0]**2 - radius[i-1] * hlm[i-1].coeffs[0, 2, 0]**2) + \ 8. * np.pi * np.sqrt(5.) / 21. * rho[i-1] * \ (hlm[i].coeffs[0, 2, 0]**3 - hlm[i-1].coeffs[0, 2, 0]**3) mass_model = mass[n] # calculate finite amplitude corrections if finiteamplitude and k > 0: for i in range(1, n + 1): grid = hlm[i].expand(grid='DH1', lmax=lmaxgrid, lmax_calc=lmax) clm, r = pyshtools.gravmag.CilmPlusDH(grid.data, nmax, gm / g, drho[i], lmax=lmax) dcplus[i, :, :, :] = clm[:, :, :] clm, r = pyshtools.gravmag.CilmMinusDH(grid.data, nmax, gm / g, drho[i], lmax=lmax) dcminus[i, :, :, :] = clm[:, :, :] for l in range(1, lmax + 1): mass_sheet[:, l, :l+1] = 4. * np.pi * g * drho[i] * \ radius[i]**2 * hlm[i].coeffs[:, l, :l+1] / gm / \ (2. * l + 1.) dcplus[i, :, l, :l + 1] -= mass_sheet[:, l, :l + 1] dcminus[i, :, l, :l + 1] -= mass_sheet[:, l, :l + 1] for l in range(2, lmax + 1, 2): for m in range(0, lmax + 1): for i in range(1, n + 1): # zero index not computed for j in range(1, n + 1): if i == j: # cp20 for sin and cosine terms are equal a[i, j] = 4. * np.pi * g * drho[i] * radius[i] / \ (2. * l + 1.) - g * mass[i] / radius[i]**2 + \ (2./3.) * radius[i] * omega**2 * \ (1. - cp20[0, l, m] / np.sqrt(5.0)) elif j < i: a[i, j] = 4. * np.pi * g * drho[j] * \ radius[j]**(l+2) / (2. * l + 1.) / \ radius[i]**(l+1) else: a[i, j] = 4. * np.pi * g * drho[j] * \ radius[i]**l / (2. * l + 1.) / \ radius[j]**(l-1) if tides is True: atides[0, i] = g * mp * radius[i] / rp**3 * ( -np.sqrt(5.) / 5. * cp20[0, l, m] + np.sqrt(12. / 5.) * cp22[0, l, m] / 2.) atides[1, i] = g * mp * radius[i] / rp**3 * ( -np.sqrt(5.) / 5. * cp20[1, l, m] + np.sqrt(12. / 5.) * cp22[1, l, m] / 2.) b = np.zeros(n + 1) if l == 2 and m == 0: for i in range(1, n + 1): b[i] = (omega * radius[i])**2 / (3. * np.sqrt(5.)) if tides: b[i] += g * mp * radius[i]**2 / rp**3 * \ np.sqrt(5.) / 10. if l == 2 and m == 2 and tides: for i in range(1, n + 1): b[i] = - g * mp * radius[i]**2 / rp**3 * \ np.sqrt(12./5.) / 4. # --- do cosine term --- # Add contributions from degree 2 relief to degree 4. if l == 4 and m <= 2: b[1:n + 1] = b4[0, m, 1:n + 1] # calculate delta if k > 0: d = np.zeros(n + 1) for i in range(1, n + 1): for j in range(1, n + 1): if j <= i: d[i] -= dcplus[j, 0, l, m] * gm * \ radius[j]**l / radius[i]**(l+1) else: d[i] -= dcminus[j, 0, l, m] * gm * \ radius[i]**l / radius[j]**(l+1) # solve the linear equation A h = b atemp = a.copy() if tides: for i in range(1, n + 1): atemp[i, i] += atides[0, i] btemp = b.copy() if k > 0: btemp += d # note that the zero index is not used lu, piv, x, info = lapack.dgesv(atemp[1:, 1:], btemp[1:]) if info != 0: raise ("lapack.dgesv did not exit properly: {:d}", info) for i in range(1, n + 1): hlm[i].coeffs[0, l, m] = x[i - 1] # calculate b4 contribution if l == 2: for i in range(1, n + 1): if m == 0: b4[0, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[0, l, m] * p402020 elif m == 1: b4[0, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[0, l, m] * p412021 elif m == 2: b4[0, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[0, l, m] * p422022 # --- do sine term --- b = np.zeros(n + 1) if m != 0: # Add contributions from degree 2 relief to degree 4. if l == 4 and m <= 2: b[1:n + 1] = b4[1, m, 1:n + 1] # calculate delta if k > 0: d = np.zeros(n + 1) for i in range(1, n + 1): for j in range(1, n + 1): if j <= i: d[i] -= dcplus[j, 1, l, m] * gm * \ radius[j]**l / radius[i]**(l+1) else: d[i] -= dcminus[j, 1, l, m] * gm * \ radius[i]**l / radius[j]**(l+1) # solve the linear equation A h = b atemp = a.copy() if tides: for i in range(1, n + 1): atemp[i, i] += atides[1, i] btemp = b.copy() if k > 0: btemp += d # note that the zero index is not used lu, piv, x, info = lapack.dgesv(atemp[1:, 1:], btemp[1:]) if info != 0: raise ("lapack.dgesv did not exit properly: {:d}", info) for i in range(1, n + 1): hlm[i].coeffs[1, l, m] = x[i - 1] # calculate b4 contribution if l == 2: for i in range(1, n + 1): if m == 1: b4[1, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[1, l, m] * p412021 elif m == 2: b4[1, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[1, l, m] * p422022 # Calculate potential at r_ref resulting from all interfaces below and # including ilith coeffs = np.zeros((2, lmax + 1, lmax + 1)) for i in range(1, n + 1): if finiteamplitude is True: grid = hlm[i].expand(grid='DH1', lmax=lmaxgrid, lmax_calc=lmax) clm, r = pyshtools.gravmag.CilmPlusDH(grid.data, nmax, gm / g, drho[i], lmax=lmax) for l in range(2, lmax + 1): coeffs[:, l, :l + 1] += clm[:, l, :l + 1] * (radius[i] / r_ref)**l else: for l in range(2, lmax + 1): coeffs[:, l, :l+1] += hlm[i].coeffs[:, l, :l+1] * 4. * \ np.pi * drho[i] * radius[i]**2 * (radius[i] / r_ref)**l * \ g / gm / (2. * l + 1.) clm_hydro = pyshtools.SHCoeffs.from_array(coeffs, normalization='4pi', csphase=1) clm_hydro.r_ref = r_ref clm_hydro.gm = gm return hlm, clm_hydro, mass_model
def HydrostaticShapeLith(radius, rho, ilith, potential, topo, rho_surface, r_sigma, omega, lmax, rp=None, mp=None, nmax=7): """ Calculate the shape of hydrostatic relief in a rotating planet or moon with a non-hydrostatic lithosphere, along with the total gravitation potential of the hydrostatic interfaces. For the case of a moon in synchronous rotation, optionally include the tidal potential. Returns ------- hlm : array of SHCoeffs class instances, size(n+1) Array of SHCoeffs class instances of the spherical harmonic coefficients of the hydrostatic relief at each interface. clm_hydro : SHCoeffs class instance containing the gravitational potential resulting from all hydrostatic interfaces. mass : float Total mass of the planet, assuming a spherical shape and the provided 1D density profile. Parameters ---------- radius : ndarray, float, size(n+1) Radius of each density interface, where index 0 corresponds to the center of the planet and n corresponds to the surface. density : ndarray, float, size(n+1) Density of layer i between radius[i] and radius[i+1]. The density at index 0 is from the center of the planet to radius[1], whereas the the density at index n should be zero. ilith : int Index of the interface that corresponds to the base of the lithosphere. potential : SHGravCoeffs class instance Observed gravitational potential coefficients. topo : SHCoeffs class instance Observed shape of the planet. rho_surface : float Effective density of the surface relief. r_sigma : float Radius of the mass sheet source in the lithosphere. omega : float Angular rotation rate of the planet. lmax : int Maximum spherical harmonic degree to compute for the hydrostatic relief at each interface. rp : float, optional, default = None If specified, include the tidal potential acting on a synchronously rotating moon, where rp is the average distance between the planet and satellite. mp : float, optional, default = None The mass of the host planet, at a distance rp from the satellite. nmax : int, optional, default = 7 The order of the approximation when computing the gravitational potential of the surface. """ tides = False if rp is not None: if mp is None: raise ValueError('When including tides, both rp and mp must be ' + 'specified.') tides = True if mp is not None: if rp is None: raise ValueError('When including tides, both rp and mp must be ' + 'specified.') tides = True if len(radius) != len(rho): raise('Length of radius and density must be the same.' + 'len(radius) = {:d}. len(density) = {:d}.' .format(len(radius), len(rho))) n = len(radius) - 1 # index of surface lmaxgrid = 3*lmax # increase grid size to avoid aliasing g = pysh.constants.G.value gm = potential.gm r_ref = potential.r0 hlm = [pysh.SHCoeffs.from_zeros(lmax) for i in range(ilith+1)] clm_hydro = pysh.SHCoeffs.from_zeros(lmax) for i in range(ilith+1): hlm[i].coeffs[0, 0, 0] = radius[i] # First determine the spherical harmonic coefficients of (Y20 Ylm) # and for tides, (Y22 Ylm). We are only concerned with the coefficient # that corresponds to lm, so for each lm, store only the lm component in # the array cp20 and cp22. sh20 = np.zeros((2, lmax+1, lmax+1)) sh22 = np.zeros((2, lmax+1, lmax+1)) sh = np.zeros((2, lmax+1, lmax+1)) cp20 = np.zeros((2, lmax+1, lmax+1)) cp22 = np.zeros((2, lmax+1, lmax+1)) sh20[0, 2, 0] = 1. # Y20 sh22[0, 2, 2] = 1. # Y22 for l in range(1, lmax+1): for m in range(0, l+1): sh[0, l, m] = 1. coeffs = pysh.expand.SHMultiply(sh20, sh) cp20[0, l, m] = coeffs[0, l, m] if m != 0: cp20[1, l, m] = cp20[0, l, m] if l == 2 and m == 0: p402020 = coeffs[0, 4, 0] if l == 2 and m == 1: p412021 = coeffs[0, 4, 1] if l == 2 and m == 2: p422022 = coeffs[0, 4, 2] coeffs = pysh.expand.SHMultiply(sh22, sh) cp22[0, l, m] = coeffs[0, l, m] sh[0, l, m] = 0. if m > 0: sh[1, l, m] = 1. coeffs = pysh.expand.SHMultiply(sh22, sh) cp22[1, l, m] = coeffs[1, l, m] sh[1, l, m] = 0. # Calculate delta_rho drho = np.zeros(n+1) mass = np.zeros(n+1) for i in range(1, n): drho[i] = rho[i-1] - rho[i] drho[n] = rho_surface # Effective density of the surface layer a = np.zeros((ilith+2, ilith+2)) atides = np.zeros((2, ilith+1)) b4 = np.zeros((2, 3, ilith+1)) # Calculate cumulate mass function for i in range(1, n+1): if i == 1: mass[1] = 4. * np.pi * radius[1]**3 * rho[0] / 3. else: mass[i] = mass[i-1] + 4. * np.pi * \ (radius[i]**3 - radius[i-1]**3) * rho[i-1] / 3. mass_model = mass[n] # Calculate potential coefficients of the surface relief grid = topo.expand(grid='DH2', lmax=lmaxgrid, lmax_calc=lmax, extend=False) cplus, r_surface = pysh.gravmag.CilmPlusDH(grid.data, nmax, gm/g, rho_surface, lmax=lmax) cminus, r_surface = pysh.gravmag.CilmMinusDH(grid.data, nmax, gm/g, rho_surface, lmax=lmax) # Calculate matrix A and invert for relief. for l in range(1, lmax+1): for m in range(0, lmax+1): for i in range(1, ilith+1): # zero index not computed for j in range(1, ilith+1): if i == j: # cp20 for sin and cosine terms are equal a[i, j] = 4. * np.pi * g * drho[i] * radius[i] / \ (2. * l + 1.) - g * mass[i] / radius[i]**2 + \ (2./3.) * radius[i] * omega**2 * \ (1. - cp20[0, l, m] / np.sqrt(5.0)) elif j < i: a[i, j] = 4. * np.pi * g * drho[j] / (2. * l + 1.) \ * radius[j] * (radius[j] / radius[i])**(l+1) else: a[i, j] = 4. * np.pi * g * drho[j] / (2. * l + 1.) \ * radius[i] * (radius[i] / radius[j])**(l-1) a[i, ilith+1] = 4. * np.pi * g / (2. * l + 1.) \ * radius[i] * (radius[i] / r_sigma)**(l-1) if tides is True: atides[0, i] = g * mp * radius[i] / rp**3 * ( - np.sqrt(5.) / 5. * cp20[0, l, m] + np.sqrt(12./5.) * cp22[0, l, m] / 2.) atides[1, i] = g * mp * radius[i] / rp**3 * ( - np.sqrt(5.) / 5. * cp20[1, l, m] + np.sqrt(12./5.) * cp22[1, l, m] / 2.) for j in range(1, ilith+1): a[ilith+1, j] = 4. * np.pi * g * drho[j] / (2. * l + 1.) \ * radius[j] * (radius[j] / r_ref)**(l+1) a[ilith+1, ilith+1] = 4. * np.pi * g / (2. * l + 1.)\ * r_sigma * (r_sigma / r_ref)**(l+1) # --- do cosine term --- b = np.zeros(ilith+2) if l == 2 and m == 0: for i in range(1, ilith+1): b[i] = (omega * radius[i])**2 / (3. * np.sqrt(5.)) if tides: b[i] += g * mp * radius[i]**2 / rp**3 * \ np.sqrt(5.) / 10. if l == 2 and m == 2 and tides: for i in range(1, ilith+1): b[i] = - g * mp * radius[i]**2 / rp**3 * \ np.sqrt(12./5.) / 4. # Add contributions from degree 2 relief to degree 4. if l == 4 and m <= 2: b[1:ilith+1] = b4[0, m, 1:ilith+1] for i in range(1, ilith+1): b[i] -= gm * cminus[0, l, m] * (radius[i] / r_surface)**l \ / r_surface b[ilith+1] = gm * potential.coeffs[0, l, m] / r_ref - \ gm * cplus[0, l, m] * (r_surface / r_ref)**l / r_ref # solve the linear equation A h = b atemp = a.copy() if tides: for i in range(1, ilith+1): atemp[i, i] += atides[0, i] btemp = b.copy() # note that the zero index is not used lu, piv, x, info = lapack.dgesv(atemp[1:, 1:], btemp[1:]) if info != 0: raise("lapack.dgesv did not exit properly: {:d}", info) for i in range(1, ilith+1): hlm[i].coeffs[0, l, m] = x[i-1] # calculate b4 contribution if l == 2: for i in range(1, ilith+1): if m == 0: b4[0, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[0, l, m] * p402020 elif m == 1: b4[0, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[0, l, m] * p412021 elif m == 2: b4[0, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[0, l, m] * p422022 # --- do sine term --- b = np.zeros(ilith+2) if m != 0: # Add contributions from degree 2 relief to degree 4. if l == 4 and m <= 2: b[1:ilith+1] = b4[1, m, 1:ilith+1] for i in range(1, ilith+1): b[i] -= gm * cminus[1, l, m] * (radius[i] / r_surface)**l \ / r_surface b[ilith+1] = gm * potential.coeffs[1, l, m] / r_ref - \ gm * cplus[1, l, m] * (r_surface / r_ref)**l / r_ref # solve the linear equation A h = b atemp = a.copy() if tides: for i in range(1, ilith+1): atemp[i, i] += atides[1, i] btemp = b.copy() # note that the zero index is not used lu, piv, x, info = lapack.dgesv(atemp[1:, 1:], btemp[1:]) if info != 0: raise("lapack.dgesv did not exit properly: {:d}", info) for i in range(1, ilith+1): hlm[i].coeffs[1, l, m] = x[i-1] # calculate b4 contribution if l == 2: for i in range(1, ilith+1): if m == 1: b4[1, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[1, l, m] * p412021 elif m == 2: b4[1, m, i] = 2. / 3. / np.sqrt(5.) * \ omega**2 * radius[i] * \ hlm[i].coeffs[1, l, m] * p422022 # Calculate potential at r_ref resulting from all interfaces below and # including ilith coeffs = np.zeros((2, lmax+1, lmax+1)) for i in range(1, ilith+1): for l in range(1, lmax+1): coeffs[:, l, :l+1] += hlm[i].coeffs[:, l, :l+1] * 4. * \ np.pi * drho[i] * radius[i]**2 * (radius[i] / r_ref)**l * \ g / gm / (2. * l + 1.) clm_hydro = pysh.SHGravCoeffs.from_array(coeffs, gm=gm, r0=r_ref, omega=omega) return hlm, clm_hydro, mass_model