resqpy 4.16.0__py3-none-any.whl → 4.16.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
resqpy/__init__.py CHANGED
@@ -28,6 +28,6 @@
28
28
 
29
29
  import logging
30
30
 
31
- __version__ = "4.16.0" # Set at build time
31
+ __version__ = "4.16.2" # Set at build time
32
32
  log = logging.getLogger(__name__)
33
33
  log.info(f"Imported resqpy version {__version__}")
@@ -439,29 +439,29 @@ def combined_tr_mult_from_gcs_mults(model,
439
439
 
440
440
  # merge in each of the three directional face arrays for this gcs with combined arrays
441
441
  for (combo_trm, gcs_trm) in [(combo_trm_k, gcs_trm_k), (combo_trm_j, gcs_trm_j), (combo_trm_i, gcs_trm_i)]:
442
- mask = np.logical_not(np.isnan(gcs_trm)) # true where this tr mult is present
442
+ mask = np.logical_not(np.isnan(gcs_trm)).astype(bool) # true where this tr mult is present
443
443
  clash_mask = np.logical_and(mask, np.logical_not(np.isnan(combo_trm))) # true where combined value clashes
444
444
  if np.any(clash_mask):
445
445
  if merge_mode == 'exception':
446
446
  raise ValueError('gcs transmissibility multiplier conflict when merging')
447
447
  if merge_mode == 'minimum':
448
- combo_trm[:] = np.where(clash_mask, np.minimum(combo_trm, gcs_trm), combo_trm)
448
+ combo_trm[clash_mask] = np.minimum(combo_trm, gcs_trm)[clash_mask]
449
449
  elif merge_mode == 'maximum':
450
- combo_trm[:] = np.where(clash_mask, np.maximum(combo_trm, gcs_trm), combo_trm)
450
+ combo_trm[clash_mask] = np.maximum(combo_trm, gcs_trm)[clash_mask]
451
451
  elif merge_mode == 'multiply':
452
- combo_trm[:] = np.where(clash_mask, combo_trm * gcs_trm, combo_trm)
452
+ combo_trm[clash_mask] = (combo_trm * gcs_trm)[clash_mask]
453
453
  else:
454
454
  raise Exception(f'code failure with unrecognised merge mode {merge_mode}')
455
455
  mask = np.logical_and(mask,
456
456
  np.logical_not(clash_mask)) # remove clash faces from mask (already handled)
457
457
  if np.any(mask):
458
- combo_trm[:] = np.where(mask, gcs_trm, combo_trm) # update combined array from individual array
458
+ combo_trm[mask] = gcs_trm[mask] # update combined array from individual array
459
459
 
460
460
  # for each of the 3 combined tr mult arrays, replace unused values with the default fill value
461
461
  # also check that any set values are non-negative
462
462
  for combo_trm in (combo_trm_k, combo_trm_j, combo_trm_i):
463
463
  if fill_value is not None and not np.isnan(fill_value):
464
- combo_trm[:] = np.where(np.isnan(combo_trm), fill_value, combo_trm)
464
+ combo_trm[:][np.isnan(combo_trm).astype(bool)] = fill_value
465
465
  assert np.all(combo_trm >= 0.0)
466
466
  else:
467
467
  assert np.all(np.logical_or(np.isnan(combo_trm), combo_trm >= 0.0))
@@ -2100,9 +2100,6 @@ class GridConnectionSet(BaseResqpy):
2100
2100
  ak = np.full((nk + 1, nj, ni), default_value, dtype = dtype)
2101
2101
  aj = np.full((nk, nj + 1, ni), default_value, dtype = dtype)
2102
2102
  ai = np.full((nk, nj, ni + 1), default_value, dtype = dtype)
2103
- # mk = np.zeros((nk + 1, nj, ni), dtype = bool)
2104
- # mj = np.zeros((nk, nj + 1, ni), dtype = bool)
2105
- # mi = np.zeros((nk, nj, ni + 1), dtype = bool)
2106
2103
 
2107
2104
  # populate arrays from faces of gcs, optionally filtered by feature index
2108
2105
  cip, fip = self.list_of_cell_face_pairs_for_feature_index(None)
@@ -2113,42 +2110,36 @@ class GridConnectionSet(BaseResqpy):
2113
2110
  else:
2114
2111
  indices = self.indices_for_feature_index(feature_index)
2115
2112
 
2116
- # opposing_count = 0
2117
2113
  side_list = ([0] if lazy else [0, 1])
2118
- for fi in indices:
2119
- # fi = int(i)
2120
- if active_mask is not None and not active_mask[fi]:
2121
- continue
2122
- value = gcs_prop_array[fi]
2123
- if baffle_mask is not None and baffle_mask[fi]:
2124
- value = 0 # will be cast to float (or bool) if needed when assigned below
2125
- for side in side_list:
2126
- cell_kji0 = cip[fi, side].copy()
2127
- # opposing = cell_kji0.copy()
2128
- axis, polarity = fip[fi, side]
2129
- assert 0 <= axis <= 2 and 0 <= polarity <= 1
2130
- cell_kji0[axis] += polarity
2131
- # opposing[axis] += (1 - polarity)
2132
- if axis == 0:
2133
- ak[tuple(cell_kji0)] = value
2134
- # mk[tuple(cell_kji0)] = True
2135
- # if mk[tuple(opposing)]:
2136
- # opposing_count += 1
2137
- elif axis == 1:
2138
- aj[tuple(cell_kji0)] = value
2139
- # mj[tuple(cell_kji0)] = True
2140
- # if mj[tuple(opposing)]:
2141
- # opposing_count += 1
2142
- else:
2143
- ai[tuple(cell_kji0)] = value
2144
- # mi[tuple(cell_kji0)] = True
2145
- # if mi[tuple(opposing)]:
2146
- # opposing_count += 1
2147
-
2148
- # if opposing_count:
2149
- # log.warning(f'{opposing_count} suspicious opposing faces of {len(indices)} detected in gcs: {self.title}')
2150
- # else:
2151
- # log.debug(f'no suspicious opposing faces detected in gcs: {self.title}')
2114
+
2115
+ value_array = gcs_prop_array.copy()
2116
+ if baffle_mask is not None:
2117
+ value_array[baffle_mask] = 0 # will be cast to float (or bool) if needed
2118
+ if active_mask is not None:
2119
+ cip = cip[active_mask, :, :]
2120
+ value_array = value_array[active_mask]
2121
+
2122
+ for side in side_list:
2123
+ cell_kji0 = cip[:, side].copy() # shape (N, 3)
2124
+ axis = fip[:, side, 0] # shape (N,)
2125
+ polarity = fip[:, side, 1] # shape (N,)
2126
+ assert 0 <= np.min(axis) and np.max(axis) <= 2
2127
+ assert 0 <= np.min(polarity) and np.max(polarity) <= 1
2128
+
2129
+ axis_mask = (axis == 0).astype(bool)
2130
+ ak_kji0 = cell_kji0[axis_mask, :]
2131
+ ak_kji0[:, 0] += polarity[axis_mask]
2132
+ ak[ak_kji0[:, 0], ak_kji0[:, 1], ak_kji0[:, 2]] = value_array[axis_mask]
2133
+
2134
+ axis_mask = (axis == 1).astype(bool)
2135
+ aj_kji0 = cell_kji0[axis_mask, :]
2136
+ aj_kji0[:, 1] += polarity[axis_mask]
2137
+ aj[aj_kji0[:, 0], aj_kji0[:, 1], aj_kji0[:, 2]] = value_array[axis_mask]
2138
+
2139
+ axis_mask = (axis == 2).astype(bool)
2140
+ ai_kji0 = cell_kji0[axis_mask, :]
2141
+ ai_kji0[:, 2] += polarity[axis_mask]
2142
+ ai[ai_kji0[:, 0], ai_kji0[:, 1], ai_kji0[:, 2]] = value_array[axis_mask]
2152
2143
 
2153
2144
  return (ak, aj, ai)
2154
2145
 
resqpy/grid/_grid.py CHANGED
@@ -2255,9 +2255,9 @@ class Grid(BaseResqpy):
2255
2255
  pc = self.extract_property_collection()
2256
2256
 
2257
2257
  if baffle_triplet is not None:
2258
- trm_k[1:-1] = np.where(baffle_triplet[0], 0.0, trm_k[1:-1])
2259
- trm_j[:, 1:-1] = np.where(baffle_triplet[1], 0.0, trm_j[:, 1:-1])
2260
- trm_i[:, :, 1:-1] = np.where(baffle_triplet[2], 0.0, trm_i[:, :, 1:-1])
2258
+ trm_k[1:-1][baffle_triplet[0]] = 0.0
2259
+ trm_j[:, 1:-1][baffle_triplet[1]] = 0.0
2260
+ trm_i[:, :, 1:-1][baffle_triplet[2]] = 0.0
2261
2261
 
2262
2262
  if composite_property:
2263
2263
  tr_composite = np.concatenate((trm_k.flat, trm_j.flat, trm_i.flat))
@@ -183,7 +183,14 @@ class ApsProperty:
183
183
  class AttributePropertySet(rqp.PropertyCollection):
184
184
  """Class for set of RESQML properties for any supporting representation, using attribute syntax."""
185
185
 
186
- def __init__(self, model = None, support = None, property_set_uuid = None, realization = None, key_mode = 'pk'):
186
+ def __init__(self,
187
+ model = None,
188
+ support = None,
189
+ property_set_uuid = None,
190
+ realization = None,
191
+ key_mode = 'pk',
192
+ indexable = None,
193
+ multiple_handling = 'warn'):
187
194
  """Initialise an empty property set, optionally populate properties from a supporting representation.
188
195
 
189
196
  arguments:
@@ -198,6 +205,11 @@ class AttributePropertySet(rqp.PropertyCollection):
198
205
  if None, then the collection is either covering a whole ensemble (individual properties can each be flagged with a
199
206
  realisation number), or is for properties that do not have multiple realizations
200
207
  key_mode (str, default 'pk'): either 'pk' (for property kind) or 'title', identifying the basis of property attribute keys
208
+ indexable (str, optional): if present and key_mode is 'pk', properties with indexable element other than this will
209
+ have their indexable element included in their key
210
+ multiple_handling (str, default 'warn'): either 'ignore', 'warn' ,or 'exception'; if 'warn' or 'ignore', and properties
211
+ exist that generate the same key, then only the first is visible in the attribute property set (and a warning is given
212
+ for each of the others in the case of 'warn'); if 'exception', a KeyError is raised if there are any duplicate keys
201
213
 
202
214
  note:
203
215
  at present, if the collection is being initialised from a property set, the support argument must also be specified;
@@ -214,9 +226,12 @@ class AttributePropertySet(rqp.PropertyCollection):
214
226
  property_set_root = None
215
227
  else:
216
228
  property_set_root = model.root_for_uuid(property_set_uuid)
229
+ assert multiple_handling in ['ignore', 'warn', 'exception']
217
230
 
218
231
  super().__init__(support = support, property_set_root = property_set_root, realization = realization)
219
232
  self.key_mode = key_mode
233
+ self.indexable_mode = indexable
234
+ self.multiple_handling = multiple_handling
220
235
  self._make_attributes()
221
236
 
222
237
  def keys(self):
@@ -241,12 +256,21 @@ class AttributePropertySet(rqp.PropertyCollection):
241
256
  title = self.citation_title_for_part(part),
242
257
  facet = self.facet_for_part(part),
243
258
  time_index = self.time_index_for_part(part),
244
- realization = self.realization_for_part(part))
259
+ realization = self.realization_for_part(part),
260
+ indexable_mode = self.indexable_mode,
261
+ indexable = self.indexable_for_part(part))
245
262
 
246
263
  def _make_attributes(self):
247
264
  """Setup individual properties with attribute style read access to metadata."""
248
265
  for part in self.parts():
249
266
  key = self._key(part)
267
+ if getattr(self, key, None) is not None:
268
+ if self.multiple_handling == 'warn':
269
+ log.warning(f'duplicate key in AttributePropertySet; only first instance included: {key}')
270
+ continue
271
+ if self.multiple_handling == 'ignore':
272
+ continue
273
+ raise KeyError(f'duplicate key in attribute property set: {key}')
250
274
  aps_property = ApsProperty(self, part)
251
275
  setattr(self, key, aps_property)
252
276
 
@@ -255,11 +279,20 @@ class AttributePropertySet(rqp.PropertyCollection):
255
279
  return self.number_of_parts()
256
280
 
257
281
 
258
- def make_aps_key(key_mode, property_kind = None, title = None, facet = None, time_index = None, realization = None):
282
+ def make_aps_key(key_mode,
283
+ property_kind = None,
284
+ title = None,
285
+ facet = None,
286
+ time_index = None,
287
+ realization = None,
288
+ indexable_mode = None,
289
+ indexable = None):
259
290
  """Contructs the key (attribute name) for a property based on metadata items."""
260
291
  if key_mode == 'pk':
261
292
  assert property_kind is not None
262
293
  key = property_kind
294
+ if indexable_mode is not None and indexable is not None and indexable != indexable_mode:
295
+ key += f'_{indexable}'
263
296
  if facet is not None:
264
297
  key += f'_{facet}'
265
298
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: resqpy
3
- Version: 4.16.0
3
+ Version: 4.16.2
4
4
  Summary: Python API for working with RESQML models
5
5
  Home-page: https://github.com/bp/resqpy
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
- resqpy/__init__.py,sha256=g4-311FByk_0L-xOs-Nz6K7tDFGQMse3zy72TBlnqB0,556
1
+ resqpy/__init__.py,sha256=V_KEFWKpjsovnzmh69UtTXTHOi6WVOZzJJZCMhJBFxs,556
2
2
  resqpy/crs.py,sha256=R7DfcTP5xGv5pu9Y8RHA2WVM9DjBCSVMoHcz4RmQ7Yw,27646
3
3
  resqpy/derived_model/__init__.py,sha256=NFvMSOKI3cxmH7lAbddV43JjoUj-r2G7ExEfOqinD1I,1982
4
4
  resqpy/derived_model/_add_edges_per_column_property_array.py,sha256=cpW3gwp6MSYIrtvFmCjoJXcyUsgGuCDbgmwlJCJebUs,6410
@@ -24,8 +24,8 @@ resqpy/derived_model/_unsplit_grid.py,sha256=aqcdyn4WhDy7Kys3wnb55QwVXehmir4VW7T
24
24
  resqpy/derived_model/_zonal_grid.py,sha256=H-IGMudUV-tiRHZqvl9B1wxMQNjeAM2zHvTllNiagPA,18825
25
25
  resqpy/derived_model/_zone_layer_ranges_from_array.py,sha256=4pHkp7yqvkif_pC59VEK0g0JeFx7kt8mqhqADTOcucI,4358
26
26
  resqpy/fault/__init__.py,sha256=IStX_EhPnppIExf_mgYrBddC4KP26VcqblcfXiBT614,996
27
- resqpy/fault/_gcs_functions.py,sha256=T3syDK7okT-xmx-ipk4xda1gwHEnaVn5VYmQAn1R5pE,29616
28
- resqpy/fault/_grid_connection_set.py,sha256=vpEFTXn5fvy2X6jgR-a4UUVbiU-ShOZ8MuSGEZklbos,117671
27
+ resqpy/fault/_gcs_functions.py,sha256=wMP4gnzT6Smv1RKGex0fdcadzj2xmanyusNA-D_ebfI,29569
28
+ resqpy/fault/_grid_connection_set.py,sha256=IEG9duPzPmVge-7yX70mcfmFSdU8MSR1sbQmRoSjlyE,117203
29
29
  resqpy/grid/__init__.py,sha256=WsfOnR5lHcnpJEx8ZZ3lhd4dImEiieJLM7eFPxMi3u8,692
30
30
  resqpy/grid/_cell_properties.py,sha256=pbyAK2eV9n4teOxm2q5hyBinohEbevFPrCfMcpGiqUU,20689
31
31
  resqpy/grid/_connection_sets.py,sha256=-277bh9pMoeESSzy9oZehL-vc82aMGZuSLQs2KJ4Wfg,10120
@@ -34,7 +34,7 @@ resqpy/grid/_defined_geometry.py,sha256=qkkfiSh8hzhTaW3vEot77mFWgApZl92Cb552b-xy
34
34
  resqpy/grid/_extract_functions.py,sha256=n_SmfkvEssX09SrlvUMe7y-4eOuVckhL_M6tFTg1bRg,28203
35
35
  resqpy/grid/_face_functions.py,sha256=0I7O6DDz7nJWczi_G2bE3L2XUr4acxREwKygXWEp6F4,16516
36
36
  resqpy/grid/_faults.py,sha256=OmukVoLpdrndqDxwE6Rj7Ul5tj3FUQVPhE0raH2FHpg,12236
37
- resqpy/grid/_grid.py,sha256=kG5YwHmflK30-26r4v2QCjvfXL1DVImMD6bpH_TN_Ng,132130
37
+ resqpy/grid/_grid.py,sha256=JIY88o5Df2y0rCDXZCpV2c0cf4I5sdXhbN30oF-FubM,132052
38
38
  resqpy/grid/_grid_types.py,sha256=vN_mMAEvcQ7HGxDQ8VMJilGXADPfJ_2rgebOJ__2P8E,3572
39
39
  resqpy/grid/_intervals_info.py,sha256=ODjDz22n8U9pSpO0Muj8mJr2hYWauFDzgcVQ0RM3csQ,338
40
40
  resqpy/grid/_moved_functions.py,sha256=XboxA0pE55j-P_x5g051WheVamxkAatQGbU5aq2GkaE,604
@@ -133,7 +133,7 @@ resqpy/property/_collection_create_xml.py,sha256=E48fu8h64T_bz5k3OEqIzPvZAOYRTBg
133
133
  resqpy/property/_collection_get_attributes.py,sha256=MlontPfGo00lxt0SpB49YG9PRsi5oXPqduDgCSOSmzs,32441
134
134
  resqpy/property/_collection_support.py,sha256=77_DG-0pzhMWdG_mNDiGfihXD7Pp-CvDSGCV8ZlDjj4,5889
135
135
  resqpy/property/_property.py,sha256=JcG7h6k4cJ4l3WC_VCsvoqHM3FBxrnUuxbIK2Ono1M0,24426
136
- resqpy/property/attribute_property_set.py,sha256=YveiTAIeNUXZRJ8m1-SZEvtae83SOW-D0E3Wz4GNqSs,10382
136
+ resqpy/property/attribute_property_set.py,sha256=AI0rS3bn38DJLQ_5XLdkeol8Oa9tGpTrX57d3435byQ,12132
137
137
  resqpy/property/grid_property_collection.py,sha256=bLWCTfhmpDsagBaXXb8XXHL46Cy78HL_NGWpPFZAgdw,66946
138
138
  resqpy/property/property_collection.py,sha256=b4J_bzigN-P5nag49Y6IG4mq3s1KbelsQC5frYg2ij8,151911
139
139
  resqpy/property/property_common.py,sha256=wf429weNtgf6J4gCNNoRwj64elQvUPI_ZWzg4qI7l6c,35993
@@ -194,7 +194,7 @@ resqpy/well/_wellbore_marker_frame.py,sha256=xvYH2_2Ie3a18LReFymbUrZboOx7Rhv5DOD
194
194
  resqpy/well/blocked_well_frame.py,sha256=Lg7TgynfPv9WkklXTLt9VN6uBXWUqX1LI-Xmv_FBqYk,22555
195
195
  resqpy/well/well_object_funcs.py,sha256=LYTcC07ezlBxClfrug_B4iXXZUkXDPgsVufNzp361Wo,24703
196
196
  resqpy/well/well_utils.py,sha256=zwpYjT85nXAwWBhYB1Pygu2SgouZ-44k6hEOnpoMfBI,5969
197
- resqpy-4.16.0.dist-info/LICENSE,sha256=2duHPIkKQyESMdQ4hKjL8CYEsYRHXaYxt0YQkzsUYE4,1059
198
- resqpy-4.16.0.dist-info/METADATA,sha256=jF_cAuOlMIbcM_n2fTddd40ATOW6f9Me8AlJ4LfIg7A,4028
199
- resqpy-4.16.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
200
- resqpy-4.16.0.dist-info/RECORD,,
197
+ resqpy-4.16.2.dist-info/LICENSE,sha256=2duHPIkKQyESMdQ4hKjL8CYEsYRHXaYxt0YQkzsUYE4,1059
198
+ resqpy-4.16.2.dist-info/METADATA,sha256=I-u5Mm5YX9_vhfM4XERgozLlbtan3_91gKZkE9jpFiw,4028
199
+ resqpy-4.16.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
200
+ resqpy-4.16.2.dist-info/RECORD,,