@@ -27,9 +27,10 @@ class CollocatedCokriging(Krige):
2727
2828 Both variants assume the cross-covariance follows:
2929
30- C_YZ(h) = ρ_YZ(0) · √(C_Z(h) · C_Y(h))
30+ .. math::
31+ C_{YZ}(h) = \\ rho_{YZ}(0) \\ cdot \\ sqrt{C_Z(h) \\ cdot C_Y(h)}
3132
32- where ρ_YZ (0) is the cross-correlation at zero lag. This assumption
33+ where :math:` \\ rho_{YZ} (0)` is the cross-correlation at zero lag. This assumption
3334 requires that primary and secondary variables have similar spatial
3435 correlation structures. Violations of MM1 can lead to suboptimal
3536 estimates and unreliable variance.
@@ -38,11 +39,11 @@ class CollocatedCokriging(Krige):
3839
3940 - **Simple Collocated** ("simple"):
4041 Uses only collocated secondary at estimation point. Simpler but
41- may show variance inflation (σ²_SCCK > σ²_SK) .
42+ may show variance inflation :math:` \\ sigma^2_{ \\ text{SCCK}} > \\ sigma^2_{ \\ text{SK}}` .
4243
4344 - **Intrinsic Collocated** ("intrinsic"):
4445 Uses collocated secondary plus secondary at all primary locations.
45- Provides accurate variance: σ²_ICCK = (1-ρ₀²)·σ²_SK ≤ σ²_SK .
46+ Provides accurate variance: :math:` \\ sigma^2_{ \\ text{ICCK}} = (1-\\ rho_0^2) \\ cdot \\ sigma^2_{ \\ text{SK}} \\ leq \\ sigma^2_{ \\ text{SK}}` .
4647
4748 Parameters
4849 ----------
@@ -235,27 +236,13 @@ def __call__(self, pos=None, secondary_data=None, **kwargs):
235236 sk_field , sk_var = super ().__call__ (pos = pos , ** kwargs_with_var )
236237 secondary_data = np .asarray (secondary_data , dtype = np .double )
237238
239+ # apply algorithm-specific post-processing
238240 if self .algorithm == "simple" :
239241 cokriging_field , cokriging_var = self ._apply_simple_collocated (
240242 sk_field , sk_var , secondary_data , user_return_var )
241243 elif self .algorithm == "intrinsic" :
242- # apply collocated secondary contribution
243- collocated_contribution = self ._lambda_Y0 * (
244- secondary_data - self .secondary_mean )
245- cokriging_field = sk_field + collocated_contribution
246-
247- # compute intrinsic variance
248- if user_return_var :
249- C_Z0 , C_Y0 , C_YZ0 = self ._compute_covariances ()
250- if C_Y0 * C_Z0 < 1e-15 :
251- rho_squared = 0.0
252- else :
253- rho_squared = (C_YZ0 ** 2 ) / (C_Y0 * C_Z0 )
254- icck_var = (1.0 - rho_squared ) * sk_var
255- icck_var = np .maximum (0.0 , icck_var )
256- cokriging_var = icck_var
257- else :
258- cokriging_var = None
244+ cokriging_field , cokriging_var = self ._apply_intrinsic_collocated (
245+ sk_field , sk_var , secondary_data , user_return_var )
259246 else :
260247 raise ValueError (f"Unknown algorithm: { self .algorithm } " )
261248
@@ -292,6 +279,34 @@ def _apply_simple_collocated(self, sk_field, sk_var, secondary_data, return_var)
292279 scck_variance = None
293280 return scck_field , scck_variance
294281
282+ def _apply_intrinsic_collocated (self , sk_field , sk_var , secondary_data , return_var ):
283+ """
284+ Apply intrinsic collocated cokriging.
285+
286+ Adds the collocated secondary contribution at estimation locations
287+ and computes ICCK variance.
288+
289+ Note: The secondary-at-primary contribution is already added during
290+ the kriging solve in _summate().
291+ """
292+ # apply collocated secondary contribution
293+ collocated_contribution = self ._lambda_Y0 * (
294+ secondary_data - self .secondary_mean )
295+ icck_field = sk_field + collocated_contribution
296+
297+ # compute intrinsic variance
298+ if return_var :
299+ C_Z0 , C_Y0 , C_YZ0 = self ._compute_covariances ()
300+ if C_Y0 * C_Z0 < 1e-15 :
301+ rho_squared = 0.0
302+ else :
303+ rho_squared = (C_YZ0 ** 2 ) / (C_Y0 * C_Z0 )
304+ icck_var = (1.0 - rho_squared ) * sk_var
305+ icck_var = np .maximum (0.0 , icck_var )
306+ else :
307+ icck_var = None
308+ return icck_field , icck_var
309+
295310 def _summate (self , field , krige_var , c_slice , k_vec , return_var ):
296311 """Apply intrinsic collocated cokriging during kriging solve."""
297312 if self .algorithm == "simple" :
0 commit comments