def pdn(r1, r2, r3, r4, angle, mode, rid, delta=0.001): """ Calculate partial derivatives numerically by perturbation. Parameters ---------- r1 : float Vp2 / Vp1 r2 : float Vs1 / Vp1 r3 : float Vs2 / Vp1 r4 : float Ro2 / Ro1 angle : float incident angle in degrees mode : str 'PP' or 'PS' rid : int 1-4 corresponds to r1, r2, r3, r4 delta : float perturbation amount Returns ------- grad : float gradient or partial derivative w.r.t. r1 """ a1, a2, gra = 0, 0, 0 if mode is 'PP': a1, _ = rpp_cer1977(r1, r2, r3, r4, angle) if rid == 1: r1 += delta elif rid == 2: r2 += delta elif rid == 3: r3 += delta elif rid == 4: r4 += delta else: raise ValueError("Illegal ratio index") a2, _ = rpp_cer1977(r1, r2, r3, r4, angle) gra = (a2 - a1) / delta elif mode is 'PS': a1, _ = rps_cer1977(r1, r2, r3, r4, angle, amp_type='abs') if rid == 1: r1 += delta elif rid == 2: r2 += delta elif rid == 3: r3 += delta elif rid == 4: r4 += delta else: raise ValueError("Illegal ratio index") a2, _ = rps_cer1977(r1, r2, r3, r4, angle, amp_type='abs') gra = (a2 - a1) / delta else: raise ValueError("Unsupported wave mode") return gra
def test_pp_clean(self): # Two half spaces elastic model # vp1, vp2 = 3.0, 2.0 # vs1, vs2 = 1.5, 1.0 # ro1, ro2 = 2.3, 2.0 vp1, vp2 = 4.0, 2.0 vs1, vs2 = 2.0, 1.0 ro1, ro2 = 2.4, 2.0 # Change parameterization r1, r2, r3, r4 = elapar_hs2ratio(vp1, vs1, ro1, vp2, vs2, ro2) # Define angles angles = np.arange(1, 60, 6) # Calculate the reflection amplitude or b in Ax=b m = len(angles) rpp = np.zeros(m) rps = np.zeros(m) for i in range(m): angle = angles[i] amp, pha = rpp_cer1977(r1, r2, r3, r4, angle) rpp[i] = amp amp, pha = rps_cer1977(r1, r2, r3, r4, angle, amp_type='abs') rps[i] = amp # print("Target model:", r1, r2, r3, r4) r1_ini = 2.4 / 4.0 r2_ini = 2.2 / 4.0 r3_ini = 1.3 / 4.0 r4_ini = 1.6 / 2.4 x_ini = (r1_ini, r2_ini, r3_ini, r4_ini) # print("Initial model:", x_ini) for i in range(5): x_new = cer1itr(angles, rpp, x_ini, rps=rps) # print("Updated", x_new) x_ini = x_new self.assertLessEqual(r1 - x_new[0], 0.001) self.assertLessEqual(r2 - x_new[1], 0.001) self.assertLessEqual(r3 - x_new[2], 0.001) self.assertLessEqual(r4 - x_new[3], 0.001)
def test_2(self): # Two half spaces elastic model vp1, vp2 = 5.72, 2.87 vs1, vs2 = 2.93, 1.61 ro1, ro2 = 2.86, 2.14 # Change parameterization r1, r2, r3, r4 = elapar_hs2ratio(vp1, vs1, ro1, vp2, vs2, ro2) # Define angles # angles = np.arange(0, 90, 1) # for angle in angles: # amp, pha = rps_cer1977(r1, r2, r3, r4, angle, amp_type='abs') # amp, pha = rps_cer1977(r1, r2, r3, r4, angle, amp_type='real') # print("ang,amp,pha =", angle, amp, pha) angle = 10 amp, pha = rps_cer1977(r1, r2, r3, r4, angle, amp_type='real') amp_truth = -0.14823324 err = amp_truth - amp self.assertLessEqual(err, 0.001)
def test_idm_functionality(self): # Functionality-based IDM # r1 physical non negative, blocks: (0,1), >=1 r1s = [0.9, 1.1] angles = [-1, 0, 30, 100] # blocks: <0, =0, (0,90), >=90 # base choice r1, r2, r3, r4, angle, amp_type = 0.9, 0.5, 0.5, 0.9, 30, 'real' r1, angle = 0.9, -1 with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) with self.assertRaises(ValueError): a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1, angle = 0.9, 0 a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertLessEqual(-0.104972376 - a, 0.001) self.assertEqual(p, 180) a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertEqual(a, 0) self.assertEqual(p, 0) r1, angle = 0.9, 30 a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertLessEqual(-0.10705785 - a, 0.001) self.assertEqual(p, 180) a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertLessEqual(-0.04646549 - a, 0.001) self.assertEqual(p, 180) r1, angle = 0.9, 100 with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) with self.assertRaises(ValueError): a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1, angle = 1.1, -1 with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) with self.assertRaises(ValueError): a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1, angle = 1.1, 0 a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertLessEqual(-0.0050251256 - a, 0.001) self.assertEqual(p, 180) a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertEqual(a, 0) self.assertEqual(p, 0) r1, angle = 1.1, 30 a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertLessEqual(0.026191689 - a, 0.001) self.assertEqual(p, 0) a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertLessEqual(-0.04659759 - a, 0.001) self.assertEqual(p, 180) r1, angle = 1.1, 100 with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) with self.assertRaises(ValueError): a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) # Add tests to increase coverage r1, r2, r3, r4, angle, amp_type = -1, 0.5, 0.5, 0.9, 30, 'real' with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1, r2, r3, r4, angle, amp_type = 0.9, 0.8, 0.5, 0.9, 30, 'real' with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1, r2, r3, r4, angle, amp_type = 0.9, 0.5, 0.8, 0.9, 30, 'real' with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1, r2, r3, r4, angle, amp_type = 0.9, 0.5, 0.5, -0.9, 30, 'real' with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1, r2, r3, r4, angle, amp_type = 0.9, 0.5, 0.5, 0.9, 0, 'abs' a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1, r2, r3, r4, angle, amp_type = 0.9, 0.5, 0.5, 0.9, 0, 'else' with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1, r2, r3, r4, angle, amp_type = 0.9, 0.5, 0.5, 0.9, 30, 'abs' a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1, r2, r3, r4, angle, amp_type = 0.9, 0.5, 0.5, 0.9, 30, 'else' with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) with self.assertRaises(ValueError): a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) # post-critical angle r1, r2, r3, r4, angle, amp_type = 2, 0.5, 0.9, 1.2, 45, 'real' a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) # Add tests of geophysical interest # No contrast: r1=1, r2=r3, r4=1 r1, r2, r3, r4, angle, amp_type = 1, 0.5, 0.5, 1, 0, 'real' a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertEqual(a, 0) self.assertEqual(p, 0) a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertEqual(a, 0) self.assertEqual(p, 0) r1, r2, r3, r4, angle, amp_type = 1, 0.5, 0.5, 1, 30, 'real' a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertEqual(a, 0) self.assertEqual(p, 0) a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertEqual(a, 0) self.assertEqual(p, 0) # Only density contrast: r1=1, r2=r3, r4!=1 # NOT handle acoustic media # r1, r2, r3, r4, angle, amp_type = 1, 0, 0, 1.1, 30, 'real' # a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) # It is known that acoustic RC has NO AVA on density-only contrast. # Is there AVA with elastic density contrast? Seems there is. r1, r2, r3, r4, angle, amp_type = 1, 0.5, 0.5, 1.1, 0, 'real' a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) # print('test1', a, p) r1, r2, r3, r4, angle, amp_type = 1, 0.5, 0.5, 1.1, 30, 'real' a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) # print('test2', a, p) r1, r2, r3, r4, angle, amp_type = 1, 0.5, 0.5, 1.1, 50, 'real' a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type)
def test_idm_interface(self): # Interface-based IDM # r1 float, blocks: <0, =0, (0,1), =1, >1 r1s = [-1, 0, 0.5, 1, 2] r2s = [-1, 0, 0.5, 1, 2] r3s = [-1, 0, 0.5, 1, 2] r4s = [-1, 0, 0.5, 1, 2] angles = [-1, 0, 100] # blocks: <0, =0, >0 # angles = [-1, 0, 10] # blocks: <0, =0, >0 amp_types = ['real', 'abs', 'else'] # Total number of test cases is 5**4 * 4 * 3 = 7500 # To illustrate, we fix these parameters, iterate r1 and angle r2, r3, r4, amp_type = 0.5, 0.5, 1.0, 'real' r1 = -1 # illegal value for angle in angles: with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) with self.assertRaises(ValueError): a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1 = 0 # illegal value for angle in angles: with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) with self.assertRaises(ValueError): a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1 = 0.5 # illegal r3 = 0.5 > 0.707 * r1 for angle in angles: with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) with self.assertRaises(ValueError): a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1 = 1 angle = -1 # illegal value with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) with self.assertRaises(ValueError): a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) # No elastic contrast r1 = 1 angle = 0 a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertEqual(a, 0) self.assertEqual(p, 0) a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertEqual(a, 0) self.assertEqual(p, 0) r1 = 1 angle = 100 # illegal value with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) with self.assertRaises(ValueError): a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1 = 2 angle = -1 # illegal value with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) with self.assertRaises(ValueError): a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) r1 = 2 angle = 0 a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertLessEqual(0.33333333 - a, 0.001) self.assertEqual(p, 0) a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) self.assertEqual(a, 0) self.assertEqual(p, 0) r1 = 2 angle = 100 # illegal value with self.assertRaises(ValueError): a, p = rpp_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type) with self.assertRaises(ValueError): a, p = rps_cer1977(r1, r2, r3, r4, angle, amp_type=amp_type)
def cer1itr(angles, rpp, x_ini, rps=None, fm='numeric', scale=1, constraints={}): """ One iteration of linearized inversion. Parameters ---------- angles : array incident angles in degrees. rpp : array Rpp amplitude at the angles, also the b in Ax=b. x_ini : tuple Initial or starting model of this iteration. rps : array Rps amplitude at the angles, append b in Ax=b. fm : str the method to calculate Frechet derivatives, numeric or analytic scale : float Scale to the model update constraints : dict constraints e.g. {'r2': 0.5} Returns ------- x_new : tuple Updated model """ x_ini_copy = [x for x in x_ini] if 'r1' in constraints: x_ini_copy[0] = constraints['r1'] if 'r2' in constraints: x_ini_copy[1] = constraints['r2'] if 'r3' in constraints: x_ini_copy[2] = constraints['r3'] if 'r4' in constraints: x_ini_copy[3] = constraints['r4'] r1_ini, r2_ini, r3_ini, r4_ini = x_ini_copy m = len(angles) n = 4 rpp_ini = np.zeros(m) A1 = np.zeros((m, n)) for i in range(m): angle = angles[i] amp, pha = rpp_cer1977(r1_ini, r2_ini, r3_ini, r4_ini, angle) rpp_ini[i] = amp # Calculate the Jacobian matrix A in Ax=b for j in range(n): fre = gradient(r1_ini, r2_ini, r3_ini, r4_ini, angle, 'PP', j + 1, method=fm) A1[i, j] = fre # A *= -1 # needed when we take abs of negative rpp b1 = rpp - rpp_ini if rps is None: A = A1 b_dif = b1 else: rps_ini = np.zeros(m) A2 = np.zeros((m, n)) for i in range(m): angle = angles[i] amp, pha = rps_cer1977(r1_ini, r2_ini, r3_ini, r4_ini, angle, amp_type='abs') rps_ini[i] = amp # Calculate the Jacobian matrix A in Ax=b for j in range(n): fre = gradient(r1_ini, r2_ini, r3_ini, r4_ini, angle, 'PS', j + 1, method=fm) A2[i, j] = fre b2 = rps - rps_ini A = np.concatenate((A1, A2), axis=0) b_dif = np.concatenate((b1, b2), axis=0) lstsq = np.linalg.lstsq(A, b_dif, rcond=None) x_dif = lstsq[0] if 'r1' in constraints: x_dif[0] = 0 if 'r2' in constraints: x_dif[1] = 0 if 'r3' in constraints: x_dif[2] = 0 if 'r4' in constraints: x_dif[3] = 0 x_new = x_ini_copy + x_dif * scale return x_new
def modeling(model, inc_angles, equation, reflection): """ Unified API for GUI call. Parameters ---------- model : tuple Two half-space elastic model (vp1, vs1, ro1, vp2, vs2, ro2) inc_angles : str Incident angles in degrees, either comma separated values, or 1-60(2) means from 1 to 60 with step 2. equation : str modeling equation, 'linear', 'quadratic', 'zoeppritz' reflection : str reflection type, 'PP', 'PS' Returns ------- rc : array amplitude and phase of the reflection coefficients at the angles. The array shape is mx3 of columns: incident angle, amplitude, phase. """ # Change parameterization vp1, vs1, ro1, vp2, vs2, ro2 = model ro_rd, vp_rd, vs_rd, vs_vp_ratio = \ elapar_hs2delta(vp1, vs1, ro1, vp2, vs2, ro2) r1, r2, r3, r4 = elapar_hs2ratio(vp1, vs1, ro1, vp2, vs2, ro2) if '-' in inc_angles: a, b = inc_angles.split('-') c, d = b.split('(') e, f = d.split(')') a1 = float(a) a2 = float(c) ad = float(e) angles = np.arange(a1, a2, ad) else: angles = np.array([float(a) for a in inc_angles.split(',')]) ave_angles = inc2ave_angle(angles, vp_rd) m = len(angles) a, p = np.zeros(m), np.zeros(m) if reflection == 'PP': if equation == 'linear': a = aki1980(vs_vp_ratio, ro_rd, vp_rd, vs_rd, ave_angles) return np.vstack((angles, a, p)).T # mx3 array elif equation == 'quadratic': a = wang1999(vs_vp_ratio, ro_rd, vp_rd, vs_rd, ave_angles) return np.vstack((angles, a, p)).T # mx3 array elif equation == 'zoeppritz': for i in range(m): angle = angles[i] amp, pha = rpp_cer1977(r1, r2, r3, r4, angle) a[i], p[i] = amp, pha return np.vstack((angles, a, p)).T # mx3 array else: raise NotImplementedError elif reflection == 'PS': if equation == 'linear': raise NotImplementedError elif equation == 'quadratic': raise NotImplementedError elif equation == 'zoeppritz': for i in range(m): angle = angles[i] amp, pha = rps_cer1977(r1, r2, r3, r4, angle) a[i], p[i] = amp, pha return np.vstack((angles, a, p)).T # mx3 array else: raise NotImplementedError else: raise NotImplementedError
def test_pp_noise(self): # Two half spaces elastic model vp1, vp2 = 4.0, 2.0 vs1, vs2 = 2.0, 1.0 ro1, ro2 = 2.4, 2.0 # Change parameterization r1, r2, r3, r4 = elapar_hs2ratio(vp1, vs1, ro1, vp2, vs2, ro2) # Define angles angles = np.arange(1, 60, 1) # Calculate the reflection amplitude or b in Ax=b m = len(angles) rpp = np.zeros(m) rps = np.zeros(m) for i in range(m): angle = angles[i] amp, pha = rpp_cer1977(r1, r2, r3, r4, angle) rpp[i] = amp amp, pha = rps_cer1977(r1, r2, r3, r4, angle, amp_type='abs') rps[i] = amp # Add noise to data # ramp = np.max(rpp) - np.min(rpp) # mu, sigma = 0, 0.05 * ramp # mean and standard deviation # noise = np.random.normal(mu, sigma, m) # hard code noise to make test result consistent noise = np.array([ -3.40745756e-03, 4.41326891e-03, -1.84969329e-02, -8.57665267e-03, -4.64722728e-03, 1.81164323e-02, 2.35764041e-03, 8.66820650e-03, 2.61862025e-03, 5.60835320e-03, -1.32386200e-02, -1.13868325e-02, -3.85411636e-03, 8.30156732e-04, 6.08364262e-03, -1.13107829e-02, 7.51568819e-03, 8.32391400e-03, 7.18915187e-03, 2.48970883e-03, 1.42114394e-02, 2.45652884e-04, -4.69414374e-03, 4.60964000e-03, 1.43935631e-02, -5.88788401e-03, 3.13041871e-03, -6.68177919e-04, -6.20489672e-03, -1.68069368e-04, -1.78392131e-02, 8.38724551e-04, 1.30622636e-03, -9.83497743e-03, -1.17627106e-02, -1.62056738e-02, 4.62611536e-03, 1.48628494e-02, -1.24973356e-02, -1.01725440e-02, 7.38562227e-03, 9.21933387e-03, -6.69923701e-03, 6.42089408e-03, -4.77129595e-03, 2.33900064e-03, 3.29402557e-05, 9.54770479e-04, -1.49280387e-02, -6.65381602e-03, -1.58004300e-02, -7.08064272e-03, 5.65539007e-04, -2.76684435e-03, -5.60120257e-03, 8.84405490e-03, -3.24883460e-03, 5.64724034e-03, -9.45532624e-03, ]) rpp_noisy = rpp + noise # ramp = np.max(rps) - np.min(rps) # mu, sigma = 0, 0.05 * ramp # mean and standard deviation # noise = np.random.normal(mu, sigma, m) # hard code noise to make test result consistent noise = np.array([ -0.0309984, 0.00092359, -0.00770345, -0.03662312, 0.00336188, 0.00583431, -0.02101242, -0.0248055, -0.00333648, 0.02492424, -0.00099495, 0.00944948, -0.00325943, 0.01934984, -0.00704765, 0.01490579, 0.00779604, 0.02183828, -0.00405295, -0.01820525, -0.00446887, 0.01793082, 0.03251096, 0.0026122, 0.01377384, -0.01452418, 0.02901279, -0.00881719, 0.02308159, 0.01260138, -0.00522267, 0.00769085, 0.02171298, -0.01478435, 0.01349567, -0.00778548, -0.01922285, -0.01798599, -0.02126122, -0.00327526, 0.01550364, 0.00130878, 0.00680895, 0.02670106, -0.05456112, 0.02081972, 0.02333233, 0.03656901, 0.01069452, -0.01197574, 0.02639394, 0.01850353, 0.0232636, -0.00037154, -0.01148699, 0.03056004, 0.006255, 0.01079065, 0.02806546, ]) rps_noisy = rps + noise # print("Target model:", r1, r2, r3, r4) r1_ini = 2.4 / 4.0 r2_ini = 2.2 / 4.0 r3_ini = 1.3 / 4.0 r4_ini = 1.6 / 2.4 x_ini = (r1_ini, r2_ini, r3_ini, r4_ini) # print("Initial model:", x_ini) for i in range(10): # x_new = cer1itr(angles, rpp_noisy, x_ini, rps=None) # fails x_new = cer1itr(angles, rpp_noisy, x_ini, rps=rps_noisy) # print("Updated", i, x_new) x_ini = x_new self.assertLessEqual(0.54935233 - x_new[0], 0.001) self.assertLessEqual(0.48925867 - x_new[1], 0.001) self.assertLessEqual(0.25932287 - x_new[2], 0.001) self.assertLessEqual(0.76031868 - x_new[3], 0.001)