def fresnel_coefficients(self, eps_1, eps_2, mu_i, ks, kl): """calculate the fresnel coefficients at the angle mu_i or 0° depending on ks*kl. The transition is abrupt.""" if ks * kl > np.sqrt(eps_2 / eps_1): Rv, Rh, _ = fresnel_coefficients(eps_1, eps_2, 1) else: Rv, Rh, _ = fresnel_coefficients(eps_1, eps_2, mu_i) return Rv, Rh
def transmission_integrand_for_energy_conservation_test(self, frequency, eps_1, eps_2, mu_t, mu_i, dphi): """function relevant to compute energy conservation. See p87 in Tsang_tomeIII. """ n_2 = np.sqrt(eps_2) n_1 = np.sqrt(eps_1) mu_i = np.atleast_1d(mu_i)[np.newaxis, np.newaxis, :] mu_t = np.atleast_1d(mu_t)[np.newaxis, :, np.newaxis] dphi = np.atleast_1d(dphi)[:, np.newaxis, np.newaxis] sin_i = np.sqrt(1 - mu_i**2) sin_t = np.sqrt(1 - mu_t**2) cos_phi = np.cos(dphi) sin_phi = np.sin(dphi) ki = vector3.from_xyz(sin_i, 0, -mu_i) kt = vector3.from_xyz(sin_t * cos_phi, sin_t * sin_phi, -mu_t) # compute the local transmission Fresnel coefficient ktd = n_1.real * ki - n_2.real * kt # Eq 2.1.87 n = ktd / (np.sign(ktd.z) * ktd.norm) # Eq 2.1.128 mu_local = -vector3.dot(n, ki) Rv, Rh, _ = fresnel_coefficients(eps_1, eps_2, mu_local) # define polarizations ht = vector3.from_xyz(-sin_phi, cos_phi, 0) vt = vector3.from_xyz(-mu_t * cos_phi, -mu_t * sin_phi, -sin_t) #print(vector3.cross(vt, ht), kt) hi = vector3.from_xyz(0, 1, 0) vi = vector3.from_xyz(-mu_i, 0, -sin_i) #print(vector3.cross(vi, hi), ki) ht_ki = vector3.dot(ht, ki) vt_ki = vector3.dot(vt, ki) hi_kt = vector3.dot(hi, kt) vi_kt = vector3.dot(vi, kt) Tv = (hi_kt**2) * (1-abs2(Rh)) + (vi_kt**2) * (1-abs2(Rv)) Th = (vi_kt**2) * (1-abs2(Rh)) + (hi_kt**2) * (1-abs2(Rv)) coef = 1/(2 * np.pi * self.mean_square_slope) * eps_2 * ktd.norm2 * vector3.dot(n, kt) * vector3.dot(n, ki) \ / (mu_i * vector3.cross(ki, kt).norm2 * ktd.z**4 ) * \ np.exp(-(ktd.x**2 + ktd.y**2) / (2 * ktd.z**2 * self.mean_square_slope)) # Eq. 2.1.130 NB: k1^2 -> eps_2 #return Tv, Th # return coef*Tv, coef*Th
def _prepare_computation(self, frequency, eps_1, eps_2, mu1): # convert to Tsang's notation. See TsangI, pages 207, Eq. 5.2.14 eps_0 = eps_1 eps_1 = self.permittivity eps_t = eps_2 mu_0 = mu1 R01_v, R01_h, mu_1 = fresnel_coefficients(eps_0, eps_1, mu_0) R1t_v, R1t_h, mu_t = fresnel_coefficients(eps_1, eps_t, np.maximum(mu_1, 1e-4)) k_1 = 2 * np.pi / C_SPEED * frequency * np.sqrt(eps_1) phase = k_1 * mu_1 * self.layer.thickness assert np.all(phase.imag >= 0) incoherent = phase.real > 3 * np.pi / 4 # we consider coherency up to 3 pi / 2 like in MEMLS phase[incoherent].real = 0 exp_kd = np.exp(1j * phase) exp_2kd = np.exp(2j * phase) return R01_v, R01_h, R1t_v, R1t_h, exp_kd, exp_2kd, mu_t
def reflection_integrand_for_energy_conservation_test(self, frequency, eps_1, eps_2, mu_s, mu_i, dphi): """function relevant to compute energy conservation. See p87 in Tsang_tomeIII. """ mu_i = np.atleast_1d(mu_i)[np.newaxis, np.newaxis, :] mu_s = np.atleast_1d(mu_s)[np.newaxis, :, np.newaxis] dphi = np.atleast_1d(dphi)[:, np.newaxis, np.newaxis] sin_i = np.sqrt(1 - mu_i**2) sin_s = np.sqrt(1 - mu_s**2) cos_phi = np.cos(dphi) sin_phi = np.sin(dphi) ki = vector3.from_xyz(sin_i, 0, -mu_i) ks = vector3.from_xyz(sin_s * cos_phi, sin_s * sin_phi, mu_s) # compute the local reflection Fresnel coefficient kd = ki - ks # in principe: *sqrt(eps_1), but in the following it appears everywhere as a ratio n = kd / (np.sign(kd.z) * kd.norm) # EQ 2.1.223 #equivalent to np.vector3(kd_x / kd_z, kd_y / kd_z, 1) mu_local = -vector3.dot(n, ki) Rv, Rh, _ = fresnel_coefficients(eps_1, eps_2, mu_local) # define polarizations hs = vector3.from_xyz(-sin_phi, cos_phi, 0) vs = vector3.from_xyz(mu_s * cos_phi, mu_s * sin_phi, -sin_s) #print(vector3.cross(vs, hs), ks) hi = vector3.from_xyz(0, 1, 0) vi = vector3.from_xyz(-mu_i, 0, -sin_i) #print(vector3.cross(vi, hi), ki) hs_ki = vector3.dot(hs, ki) vs_ki = vector3.dot(vs, ki) hi_ks = vector3.dot(hi, ks) vi_ks = vector3.dot(vi, ks) coef = 1/(2 * np.pi * self.mean_square_slope) * kd.norm2**2 / (4 * mu_i * vector3.cross(ki, ks).norm2 * kd.z**4) * \ np.exp( - (kd.x**2 + kd.y**2)/ (2 * kd.z**2 * self.mean_square_slope)) # Eq. 2.1.124 return coef * (hi_ks**2 * abs2(Rh) + vi_ks**2 * abs2(Rv)), \ coef * (vi_ks**2 * abs2(Rh) + hi_ks**2 * abs2(Rv))
def diffuse_reflection_matrix(self, frequency, eps_1, eps_2, mu_s, mu_i, dphi, npol): """compute the reflection coefficients for an array of incident, scattered and azimuth angles in medium 1. Medium 2 is where the beam is transmitted. :param eps_1: permittivity of the medium where the incident beam is propagating. :param eps_2: permittivity of the other medium :param mu1: array of cosine of incident angles :param npol: number of polarization :return: the reflection matrix """ mu_s = np.atleast_1d(mu_s) mu_i = np.atleast_1d(mu_i) if not np.allclose(mu_s, mu_i) or not np.allclose(dphi, np.pi): raise NotImplementedError( "Only the backscattering coefficient is implemented at this stage. This is a very preliminary implementation" ) if len(np.atleast_1d(dphi)) != 1: raise NotImplementedError( "Only the backscattering coefficient is implemented at this stage. " ) R_normal, _, _ = fresnel_coefficients(eps_1, eps_2, np.ones(1)) tantheta_i2 = 1 / mu_i**2 - 1 smrt_norm = 1 / (4 * np.pi) gamma = smrt_norm / (2 * self.mean_square_slope) * np.abs( R_normal)**2 / mu_i**5 * np.exp(-tantheta_i2 / (2 * self.mean_square_slope)) if self.shadow_correction: gamma *= 1 / (1 + shadow_function(self.mean_square_slope, 1 / np.sqrt(tantheta_i2))) reflection_coefficients = smrt_matrix.zeros((npol, len(mu_i))) reflection_coefficients[0] = gamma reflection_coefficients[1] = gamma return reflection_coefficients
def diffuse_reflection_matrix(self, frequency, eps_1, eps_2, mu_s, mu_i, dphi, npol, debug=False): """compute the reflection coefficients for an array of incident, scattered and azimuth angles in medium 1. Medium 2 is where the beam is transmitted. :param eps_1: permittivity of the medium where the incident beam is propagating. :param eps_2: permittivity of the other medium :param mu1: array of cosine of incident angles :param npol: number of polarization :return: the reflection matrix """ mu_s = np.atleast_1d(mu_s) mu_i = np.atleast_1d(mu_i) if not np.allclose(mu_s, mu_i) or not np.allclose(dphi, np.pi): raise NotImplementedError( "Only the backscattering coefficient is implemented at this stage. This is a very preliminary implementation" ) if len(np.atleast_1d(dphi)) != 1: raise NotImplementedError( "Only the backscattering coefficient is implemented at this stage. " ) mu = mu_i[None, :] k = vector3.from_angles( 2 * np.pi * frequency / C_SPEED * np.sqrt(eps_1).real, mu, 0) eps_r = eps_2 / eps_1 # check validity warning = None ks = abs(k.norm * self.roughness_rms) if ks > 3: warning = "Warning, roughness_rms is too high for the given wavelength. Limit is ks < 3. Here ks=%g" % ks kskl = abs(ks * k.norm * self.corr_length) if kskl > np.sqrt(eps_r): warning = "Warning, roughness_rms or correlation_length are too high for the given wavelength. Limit is ks * kl < sqrt(eps_r). Here ks*kl=%g and sqrt(eps_r)=%g" % ( kskl, np.sqrt(eps_r)) if warning: if self.warning_handling == "print": print(warning) elif self.warning_handling == "nan": return smrt_matrix.full((npol, len(mu_i)), np.nan) # chekc validity done Rv, Rh, _ = fresnel_coefficients(eps_1, eps_2, mu_i) fvv = 2 * Rv / mu # Eq 44 in Fung et al. 1992 fhh = -2 * Rh / mu # Eq 45 in Fung et al. 1992 # prepare the series N = self.series_truncation n = np.arange(1, N + 1, dtype=np.float64)[:, None] rms2 = self.roughness_rms**2 # Kirchoff term Iscalar_n = (2 * k.z)**n * np.exp(-rms2 * k.z**2) Ivv_n = Iscalar_n * fvv # Eq 82 in Fung et al. 1992 Ihh_n = Iscalar_n * fhh # Complementary term mu2 = mu**2 sin2 = 1 - mu2 tan2 = sin2 / mu2 Ivv_n += k.z**n * ( sin2 / mu * (1 + Rv)**2 * (1 - 1 / eps_r) * (1 + tan2 / eps_r) ) # part of Eq 91. We don't use all the simplification because we want validity for n>1, especially not np.exp(-rms2*k.z**2)=1 Ihh_n += -k.z**n * (sin2 / mu * (1 + Rh)**2 * (eps_r - 1) / mu2) # part of Eq 95. # compute the series rms2_over_fractorial = np.cumprod(rms2 / n)[:, None] # Eq 82 in Fung et al. 1992 coef = k.norm2 / 2 * np.exp(-2 * rms2 * k.z**2) coef_n = rms2_over_fractorial * self.W_n(n, -2 * k.x) sigma_vv = coef * np.sum(coef_n * abs2(Ivv_n), axis=0) sigma_hh = coef * np.sum(coef_n * abs2(Ihh_n), axis=0) #if debug: # self.sigma_vv_1 = ( 8*k.norm2**2*rms2*abs2(Rv*mu2 + (1-mu2)*(1+Rv)**2 / 2 * (1 - 1 / eps_r)) * self.W_n(1, -2 * k.x) ).flat # self.sigma_hh_1 = ( 8*k.norm2**2*rms2*abs2(Rh*mu2) * self.W_n(1, -2 * k.x) ).flat reflection_coefficients = smrt_matrix.zeros((npol, len(mu_i))) reflection_coefficients[0] = sigma_vv / (4 * np.pi * mu_i) reflection_coefficients[1] = sigma_hh / (4 * np.pi * mu_i) return reflection_coefficients
def diffuse_reflection_matrix(self, frequency, eps_1, eps_2, mu_s, mu_i, dphi, npol): """compute the reflection coefficients for an array of incident, scattered and azimuth angles in medium 1. Medium 2 is where the beam is transmitted. :param eps_1: permittivity of the medium where the incident beam is propagating. :param eps_2: permittivity of the other medium :param mu1: array of cosine of incident angles :param npol: number of polarization :return: the reflection matrix """ mu_i = np.atleast_1d(mu_i)[np.newaxis, np.newaxis, :] mu_s = np.atleast_1d(mu_s)[np.newaxis, :, np.newaxis] dphi = np.atleast_1d(dphi)[:, np.newaxis, np.newaxis] sin_i = np.sqrt(1 - mu_i**2) sin_s = np.sqrt(1 - mu_s**2) cos_phi = np.cos(dphi) sin_phi = np.sin(dphi) ki = vector3.from_xyz(sin_i, 0, -mu_i) ks = vector3.from_xyz(sin_s * cos_phi, sin_s * sin_phi, mu_s) # compute the local reflection Fresnel coefficient kd = ki - ks # in principe: *sqrt(eps_1), but in the following it appears everywhere as a ratio n = kd / (np.sign(kd.z) * kd.norm) # EQ 2.1.223 #equivalent to np.vector3(kd_x / kd_z, kd_y / kd_z, 1) mu_local = -vector3.dot(n, ki) Rv, Rh, _ = fresnel_coefficients(eps_1, eps_2, mu_local) # define polarizations hs = vector3.from_xyz(-sin_phi, cos_phi, 0) vs = vector3.from_xyz(mu_s * cos_phi, mu_s * sin_phi, -sin_s) hi = vector3.from_xyz(0, 1, 0) vi = vector3.from_xyz(-mu_i, 0, -sin_i) # compute the dot products hs_ki = vector3.dot(hs, ki) vs_ki = vector3.dot(vs, ki) hi_ks = vector3.dot(hi, ks) vi_ks = vector3.dot(vi, ks) fvv = abs2( hs_ki * hi_ks * Rh + vs_ki* vi_ks * Rv) # Eqs 2.1.122 in Tsang_tomeIII fhh = abs2( vs_ki * vi_ks * Rh + hs_ki* hi_ks * Rv) fhv = abs2( vs_ki * hi_ks * Rh - hs_ki* vi_ks * Rv) fvh = abs2( hs_ki * vi_ks * Rh + vs_ki* hi_ks * Rv) reflection_coefficients = smrt_matrix.zeros((npol, npol, dphi.shape[0], mu_s.shape[1], mu_i.shape[2])) reflection_coefficients[0, 0] = fvv reflection_coefficients[0, 1] = fvh reflection_coefficients[1, 0] = fhv reflection_coefficients[1, 1] = fhh smrt_norm = 1 / (4 * np.pi) # divide by 4*pi because this is the norm for SMRT coef = smrt_norm / (2 * self.mean_square_slope) / mu_i * kd.norm2**2 / (vector3.cross(ki, ks).norm2**2 * kd.z**4) * \ np.exp( - (kd.x**2 + kd.y**2)/ (2 * kd.z**2 * self.mean_square_slope)) # Eq. 2.1.124 if self.shadow_correction: backward = dphi == np.pi higher_thetas = mu_s <= mu_i zero_i = backward & higher_thetas zero_s = backward & ~higher_thetas s = 1 / (1 + zero_i * shadow_function(self.mean_square_slope, mu_i/sin_i) + zero_s * shadow_function(self.mean_square_slope, mu_s/sin_s)) coef *= s return reflection_coefficients * coef
def diffuse_transmission_matrix(self, frequency, eps_1, eps_2, mu_t, mu_i, dphi, npol): """compute the transmission coefficients for an array of incident, scattered and azimuth angles in medium 1. Medium 2 is where the beam is transmitted. :param eps_1: permittivity of the medium where the incident beam is propagating. :param eps_2: permittivity of the other medium :param mu_i: array of cosine of incident angles :param mu_t: array of cosine of transmitted wave angles :param npol: number of polarization :return: the transmission matrix """ n_2 = np.sqrt(eps_2) n_1 = np.sqrt(eps_1) eta1_eta = n_1 / n_2 # eta1 is the impedance in medium 2 and eta in medium 1. Impedance is sqrt(permeability/permittivity) mu_i = np.atleast_1d(mu_i)[np.newaxis, np.newaxis, :] mu_t = np.atleast_1d(mu_t)[np.newaxis, :, np.newaxis] dphi = np.atleast_1d(dphi)[:, np.newaxis, np.newaxis] sin_i = np.sqrt(1 - mu_i**2) sin_t = np.sqrt(1 - mu_t**2) cos_phi = np.cos(dphi) sin_phi = np.sin(dphi) ki = vector3.from_xyz(sin_i, 0, -mu_i) kt = vector3.from_xyz(sin_t * cos_phi, sin_t * sin_phi, -mu_t) # compute the local transmission Fresnel coefficient ktd = n_1.real * ki - n_2.real * kt # Eq 2.1.87 n = ktd / (np.sign(ktd.z) * ktd.norm) # Eq 2.1.128 mu_local = -vector3.dot(n, ki) Rv, Rh, _ = fresnel_coefficients(eps_1, eps_2, mu_local) n_kt = vector3.dot(n, kt) n_kt[mu_local < 0] = 0 # define polarizations ht = vector3.from_xyz(-sin_phi, cos_phi, 0) vt = vector3.from_xyz(-mu_t * cos_phi, -mu_t * sin_phi, -sin_t) hi = vector3.from_xyz(0, 1, 0) vi = vector3.from_xyz(-mu_i, 0, -sin_i) # compute the cosines ht_ki = vector3.dot(ht, ki) vt_ki = vector3.dot(vt, ki) hi_kt = vector3.dot(hi, kt) vi_kt = vector3.dot(vi, kt) Wvv = abs2( ht_ki * hi_kt * (1 + Rh) + vt_ki * vi_kt * (1 + Rv) * eta1_eta) # Eqs 2.1.130 in Tsang_tomeIII Whh = abs2( vt_ki * vi_kt * (1 + Rh) + ht_ki * hi_kt * (1 + Rv) * eta1_eta) Whv = abs2(-vt_ki * hi_kt * (1 + Rh) + ht_ki * vi_kt * (1 + Rv) * eta1_eta) Wvh = abs2( ht_ki * vi_kt * (1 + Rh) - vt_ki * hi_kt * (1 + Rv) * eta1_eta) transmission_coefficients = smrt_matrix.zeros((npol, npol, dphi.shape[0], mu_t.shape[1], mu_i.shape[2])) transmission_coefficients[0, 0] = Wvv transmission_coefficients[0, 1] = Wvh transmission_coefficients[1, 0] = Whv transmission_coefficients[1, 1] = Whh smrt_norm = 1 / (4 * np.pi) # SMRT requires scattering coefficient / 4 * pi coef = smrt_norm * 2 * eps_2 / (eta1_eta * self.mean_square_slope) / mu_i * \ ktd.norm2 * n_kt**2 / (vector3.cross(ki, kt).norm2**2 * ktd.z**4) * \ np.exp(-(ktd.x**2 + ktd.y**2) / (2 * ktd.z**2 * self.mean_square_slope)) # Eq. 2.1.130 NB: k1^2 -> eps_2 if self.shadow_correction: s = 1 / (1 + shadow_function(self.mean_square_slope, mu_i/sin_i) + shadow_function(self.mean_square_slope, mu_t/sin_t)) coef *= s return transmission_coefficients * coef.real
def fresnel_coefficients(self, eps_1, eps_2, mu_i, ks, kl): """calculate the fresnel coefficients at the angle mu_i whatever is ks and kl according to the original formulation of Fung 1992""" Rv, Rh, _ = fresnel_coefficients(eps_1, eps_2, mu_i) return Rv, Rh
def diffuse_reflection_matrix(self, frequency, eps_1, eps_2, mu_s, mu_i, dphi, npol): """compute the reflection coefficients for an array of incident, scattered and azimuth angles in medium 1. Medium 2 is where the beam is transmitted. :param eps_1: permittivity of the medium where the incident beam is propagating. :param eps_2: permittivity of the other medium :param mu1: array of cosine of incident angles :param npol: number of polarization :return: the reflection matrix """ mu_i = np.atleast_1d(self.clip_mu(mu_i))[np.newaxis, np.newaxis, :] mu_s = np.atleast_1d(self.clip_mu(mu_s))[np.newaxis, :, np.newaxis] dphi = np.atleast_1d(dphi)[:, np.newaxis, np.newaxis] sin_i = np.sqrt(1 - mu_i**2) sin_s = np.sqrt(1 - mu_s**2) cos_phi = np.cos(dphi) sin_phi = np.sin(dphi) ki = vector3.from_xyz(sin_i, 0, -mu_i) ks = vector3.from_xyz(sin_s * cos_phi, sin_s * sin_phi, mu_s) # compute the local reflection Fresnel coefficient kd = ki - ks # in principe: *sqrt(eps_1), but in the following it appears everywhere as a ratio n = kd / ( np.sign(kd.z) * kd.norm ) # EQ 2.1.123 #equivalent to np.vector3(kd_x / kd_z, kd_y / kd_z, 1) mu_local = -vector3.dot(n, ki) assert (np.all(mu_local >= 0)) assert (np.all(mu_local <= 1.0001)) # compare with some tolerance Rv, Rh, _ = fresnel_coefficients(eps_1, eps_2, self.clip_mu(mu_local)) # define polarizations hs = vector3.from_xyz(-sin_phi, cos_phi, 0) vs = vector3.from_xyz(mu_s * cos_phi, mu_s * sin_phi, -sin_s) hi = vector3.from_xyz(0, 1, 0) vi = vector3.from_xyz(-mu_i, 0, -sin_i) # compute the dot products cross_ki_ks_norm = vector3.cross(ki, ks).norm colinear = cross_ki_ks_norm < 1e-4 # avoid warning due to divide error, the colinear case is solved independantly: cross_ki_ks_norm[colinear] = 1 hs_ki = vector3.dot(hs, ki) / cross_ki_ks_norm hs_ki[colinear] = -1 vs_ki = vector3.dot(vs, ki) / cross_ki_ks_norm vs_ki[colinear] = 0 hi_ks = vector3.dot(hi, ks) / cross_ki_ks_norm hi_ks[colinear] = 1 vi_ks = vector3.dot(vi, ks) / cross_ki_ks_norm vi_ks[colinear] = 0 fvv = abs2(hs_ki * hi_ks * Rh + vs_ki * vi_ks * Rv) # Eqs 2.1.122 in Tsang_tomeIII fhh = abs2(vs_ki * vi_ks * Rh + hs_ki * hi_ks * Rv) fhv = abs2(vs_ki * hi_ks * Rh - hs_ki * vi_ks * Rv) fvh = abs2(hs_ki * vi_ks * Rh - vs_ki * hi_ks * Rv) reflection_coefficients = smrt_matrix.zeros( (npol, npol, dphi.shape[0], mu_s.shape[1], mu_i.shape[2])) reflection_coefficients[0, 0] = fvv reflection_coefficients[0, 1] = fvh reflection_coefficients[1, 0] = fhv reflection_coefficients[1, 1] = fhh smrt_norm = 1 / (4 * np.pi ) # divide by 4*pi because this is the norm for SMRT coef = smrt_norm / (2 * self.mean_square_slope) / mu_i * kd.norm2**2 / kd.z**4 * \ np.exp(-(kd.x**2 + kd.y**2) / (2 * kd.z**2 * self.mean_square_slope)) # Eq. 2.1.124 if self.shadow_correction: backward = dphi == np.pi higher_thetas = mu_s <= mu_i zero_i = backward & higher_thetas zero_s = backward & ~higher_thetas # this hack to avoid division-by-zero is safe, because the shadow_function is only important for large angles sin_i[sin_i < 1e-3] = 1e-3 sin_s[sin_s < 1e-3] = 1e-3 s = 1 / (1 + (~zero_i) * shadow_function(self.mean_square_slope, mu_i / sin_i) + (~zero_s) * shadow_function(self.mean_square_slope, mu_s / sin_s)) coef *= s return reflection_coefficients * coef