resqpy 4.16.2__py3-none-any.whl → 4.16.4__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.2" # Set at build time
31
+ __version__ = "4.16.4" # Set at build time
32
32
  log = logging.getLogger(__name__)
33
33
  log.info(f"Imported resqpy version {__version__}")
@@ -553,6 +553,15 @@ def _citation_title_for_part(model, part): # duplicate functionality to title_f
553
553
  return title
554
554
 
555
555
 
556
+ def _source_for_part(model, part):
557
+ """Returns the source string from the part's extra metadata, if present, else None."""
558
+
559
+ part_extra = rqet.load_metadata_from_xml(_root_for_part(model, part))
560
+ if not part_extra:
561
+ return None
562
+ return part_extra.get('source')
563
+
564
+
556
565
  def _root_for_time_series(model, uuid = None):
557
566
  """Return root for time series part."""
558
567
 
resqpy/model/_forestry.py CHANGED
@@ -200,8 +200,8 @@ def _load_epc(model, epc_file, full_load = True, epc_subdir = None, copy_from =
200
200
  def _add_uuid_soft_relations(model, uuid_int, part):
201
201
  if "EpcExternalPart" in part:
202
202
  return
203
- value = model.rels_forest.get(rqet.rels_part_name_for_part(part))
204
- if value is not None:
203
+ rels_part = rqet.rels_part_name_for_part(part)
204
+ if rels_part in model.rels_forest:
205
205
  rels_root = m_c._root_for_part(model, rqet.rels_part_name_for_part(part), is_rels = True)
206
206
  if rels_root is not None:
207
207
  for relation_node in rels_root:
@@ -214,9 +214,10 @@ def _add_uuid_soft_relations(model, uuid_int, part):
214
214
  if relation_uuid_str is None:
215
215
  return # probably HDF5 external resource
216
216
  relation_uuid_int = _hex_to_int(relation_uuid_str)
217
- value = model.uuid_rels_dict.get(uuid_int)
218
- if value is not None and relation_uuid_int not in value[0] and relation_uuid_int not in value[1]:
219
- value[2].add(relation_uuid_int)
217
+ rels_sets = model.uuid_rels_dict.get(uuid_int)
218
+ if rels_sets is not None and relation_uuid_int not in rels_sets[
219
+ 0] and relation_uuid_int not in rels_sets[1]:
220
+ rels_sets[2].add(relation_uuid_int)
220
221
 
221
222
 
222
223
  def _add_uuid_relations(model, uuid_int, part):
@@ -719,9 +720,6 @@ def _copy_relationships_for_present_targets(model, other_model, consolidate, for
719
720
  continue
720
721
  else:
721
722
  continue
722
- if not force and resident_related_part in m_c._parts_list_filtered_by_related_uuid(
723
- model, m_c._list_of_parts(model), resident_uuid):
724
- continue
725
723
  related_node = m_c._root_for_part(model, resident_related_part)
726
724
  assert related_node is not None
727
725
 
resqpy/model/_model.py CHANGED
@@ -1108,6 +1108,66 @@ class Model():
1108
1108
 
1109
1109
  return m_c._citation_title_for_part(self, part)
1110
1110
 
1111
+ def source_for_part(self, part):
1112
+ """Returns the source string from the part's extra metadata, if present, else None.
1113
+
1114
+ arguments:
1115
+ part (str): the part for which the source information is required
1116
+
1117
+ returns:
1118
+ str being the text of the source field in the xml extra metadata of the part, or None
1119
+ """
1120
+
1121
+ return m_c._source_for_part(self, part)
1122
+
1123
+ def set_source_for_part(self, part, source):
1124
+ """Sets the source string in the part's extra metadata.
1125
+
1126
+ arguments:
1127
+ part (str): the part for which the source information is to be set
1128
+ source (str): text for the extra metadata source item
1129
+
1130
+ notes:
1131
+ this function adds the source item to the in-memory xml extra metadata;
1132
+ any previous text for the source item (if present) will be replaced;
1133
+ it will be included in the epc if store_epc() is subsequently called
1134
+ """
1135
+
1136
+ m_x._create_source(source, root = m_c._root_for_part(self, part))
1137
+ self.set_modified()
1138
+
1139
+ def source_for_obj(self, obj):
1140
+ """Returns the source string from the object's extra metadata, if present, else None.
1141
+
1142
+ arguments:
1143
+ obj (BaseResqpy): any high level resqpy object (eg. Surface)
1144
+
1145
+ returns:
1146
+ str being the text of the source extra metadata item for the object, or None
1147
+ """
1148
+
1149
+ return m_c._source_for_part(self, obj.part)
1150
+
1151
+ def set_source_for_obj(self, obj, source):
1152
+ """Sets the source string in the object's extra metadata.
1153
+
1154
+ arguments:
1155
+ part (str): the part for which the source information is to be set
1156
+ source (str): text for the extra metadata source item
1157
+
1158
+ notes:
1159
+ this function adds the source item to the in-memory xml extra metadata as well as
1160
+ the object's extra_metadata dictionary
1161
+ any previous text for the source item (if present) will be replaced;
1162
+ it will be included in the epc if store_epc() is subsequently called
1163
+ """
1164
+
1165
+ m_x._create_source(source, root = obj.root)
1166
+ if not hasattr(obj, 'extra_metadata') or obj.extra_metadata is None:
1167
+ obj.extra_metadata = {}
1168
+ obj.extra_metadata['source'] = str(source)
1169
+ self.set_modified()
1170
+
1111
1171
  def root_for_time_series(self, uuid = None):
1112
1172
  """Return root for time series part.
1113
1173
 
resqpy/model/_xml.py CHANGED
@@ -420,22 +420,39 @@ def _create_supporting_representation(model,
420
420
 
421
421
 
422
422
  def _create_source(source, root = None):
423
- """Create an extra meta data node holding information on the source of the data, optionally add to root."""
423
+ """Create an extra meta data node holding information on the source of the data, optionally add to root.
424
424
 
425
- emd_node = rqet.Element(ns['resqml2'] + 'ExtraMetadata')
426
- emd_node.set(ns['xsi'] + 'type', ns['resqml2'] + 'NameValuePair')
427
- emd_node.text = rqet.null_xml_text
425
+ note:
426
+ if the root already contains a 'source' extra metadata item, its text field is updated and the
427
+ existing extra metadata xml node is returned
428
+ """
428
429
 
429
- name_node = rqet.SubElement(emd_node, ns['resqml2'] + 'Name')
430
- name_node.set(ns['xsi'] + 'type', ns['xsd'] + 'string')
431
- name_node.text = 'source'
430
+ emd_node = None
431
+ if root is not None:
432
+ emd_node = rqet.find_metadata_item_node_in_xml(root, 'source')
432
433
 
433
- value_node = rqet.SubElement(emd_node, ns['resqml2'] + 'Value')
434
- value_node.set(ns['xsi'] + 'type', ns['xsd'] + 'string')
435
- value_node.text = source
434
+ if emd_node is None:
436
435
 
437
- if root is not None:
438
- root.append(emd_node)
436
+ emd_node = rqet.Element(ns['resqml2'] + 'ExtraMetadata')
437
+ emd_node.set(ns['xsi'] + 'type', ns['resqml2'] + 'NameValuePair')
438
+ emd_node.text = rqet.null_xml_text
439
+
440
+ name_node = rqet.SubElement(emd_node, ns['resqml2'] + 'Name')
441
+ name_node.set(ns['xsi'] + 'type', ns['xsd'] + 'string')
442
+ name_node.text = 'source'
443
+
444
+ value_node = rqet.SubElement(emd_node, ns['resqml2'] + 'Value')
445
+ value_node.set(ns['xsi'] + 'type', ns['xsd'] + 'string')
446
+ value_node.text = str(source)
447
+
448
+ if root is not None:
449
+ root.append(emd_node)
450
+
451
+ else:
452
+
453
+ value_node = rqet.find_tag(emd_node, 'Value')
454
+ assert value_node is not None
455
+ value_node.text = str(source)
439
456
 
440
457
  return emd_node
441
458
 
@@ -457,7 +474,9 @@ def _create_patch(model,
457
474
  assert ext_uuid is not None
458
475
  else:
459
476
  assert const_count is not None and const_count > 0
460
- if hdf5_type.endswith('Hdf5Array'):
477
+ if isinstance(const_value, bool):
478
+ hdf5_type = 'BooleanConstantArray' # not actually stored in hdf5
479
+ elif hdf5_type.endswith('Hdf5Array'):
461
480
  hdf5_type = hdf5_type[:-9] + 'ConstantArray'
462
481
 
463
482
  lxt = str(xsd_type).lower()
@@ -488,6 +507,7 @@ def _create_patch(model,
488
507
  outer_values_node.text = rqet.null_xml_text
489
508
 
490
509
  if discrete and const_value is None:
510
+
491
511
  if null_value is None:
492
512
  if str(xsd_type).startswith('u'):
493
513
  null_value = 4294967295 # 2^32 - 1, used as default even for 64 bit data!
@@ -507,6 +527,11 @@ def _create_patch(model,
507
527
 
508
528
  else:
509
529
 
530
+ # TODO: handle bool const_value as special case
531
+ if isinstance(const_value, bool):
532
+ const_value = str(const_value).lower()
533
+ xsd_type = 'boolean'
534
+
510
535
  const_value_node = rqet.SubElement(outer_values_node, ns['resqml2'] + 'Value')
511
536
  const_value_node.set(ns['xsi'] + 'type', ns['xsd'] + xsd_type)
512
537
  const_value_node.text = str(const_value)
resqpy/olio/xml_et.py CHANGED
@@ -778,6 +778,18 @@ def load_metadata_from_xml(node):
778
778
  return extra_metadata
779
779
 
780
780
 
781
+ def find_metadata_item_node_in_xml(node, key):
782
+ """Returns the extra metadata node for a particular key, if present."""
783
+
784
+ if node is None:
785
+ return None
786
+ meta_nodes = list_of_tag(node, 'ExtraMetadata')
787
+ for meta in meta_nodes:
788
+ if find_tag_text(meta, 'Name') == key:
789
+ return meta
790
+ return None
791
+
792
+
781
793
  def create_metadata_xml(node, extra_metadata):
782
794
  """Writes the xml for the given metadata dictionary."""
783
795
 
@@ -214,7 +214,7 @@ def _process_imported_property(collection, attributes, property_kind_uuid, strin
214
214
  p_keyword, p_discrete, string_lookup_uuid, points)
215
215
 
216
216
  p_array = _process_imported_property_get_p_array(collection, p_cached_name)
217
- p_array_bool = None if p_array is None else p_array.dtype in [bool, np.int8]
217
+ p_array_bool = isinstance(const_value, bool) if p_array is None else p_array.dtype in [bool, np.int8]
218
218
 
219
219
  add_min_max = pcga._process_imported_property_get_add_min_max(points, property_kind, string_lookup_uuid,
220
220
  local_property_kind_uuid, p_array_bool)
@@ -159,6 +159,11 @@ class ApsProperty:
159
159
  """The extra metadata for this property (synonymous with extra)."""
160
160
  return self.extra
161
161
 
162
+ @property
163
+ def source(self):
164
+ """The source extra metadata value for this property (or None)."""
165
+ return self.aps.source_for_part(self.part)
166
+
162
167
  @property
163
168
  def support_uuid(self):
164
169
  """The uuid of the supporting representation for this property."""
@@ -85,7 +85,7 @@ class PropertyCollection():
85
85
  self.realization = realization # model realization number within an ensemble
86
86
  self.null_value = None
87
87
  self.imported_list = []
88
- # above is list of (uuid, file_name, keyword, cached_name, discrete, uom, time_index, null_value,
88
+ # above is list of (uuid, source, keyword, cached_name, discrete, uom, time_index, null_value,
89
89
  # min_value, max_value, property_kind, facet_type, facet, realization,
90
90
  # indexable_element, count, local_property_kind_uuid, const_value, points,
91
91
  # time_series_uuid, string_lookup_uuid)
@@ -1300,6 +1300,12 @@ class PropertyCollection():
1300
1300
 
1301
1301
  return [self.citation_title_for_part(p) for p in self.parts()]
1302
1302
 
1303
+ def source_for_part(self, part):
1304
+ """Returns the source string from the part's extra metadata, if present, else None."""
1305
+
1306
+ assert self.model is not None
1307
+ return self.model.source_for_part(part)
1308
+
1303
1309
  def time_series_uuid_for_part(self, part):
1304
1310
  """If the property has an associated time series (is not static), returns the uuid for the time series.
1305
1311
 
@@ -2275,7 +2281,7 @@ class PropertyCollection():
2275
2281
  if cached_array is not None:
2276
2282
  min_value, max_value = pcga._min_max_of_cached_array(self, cached_name, cached_array, null_value, discrete)
2277
2283
  else:
2278
- if const_value == null_value or (not discrete and np.isnan(const_value)):
2284
+ if const_value == null_value or isinstance(const_value, bool) or (not discrete and np.isnan(const_value)):
2279
2285
  min_value = max_value = None
2280
2286
  else:
2281
2287
  min_value = max_value = const_value
@@ -2652,7 +2658,7 @@ class PropertyCollection():
2652
2658
  must cycle fastest in the array, ie. be the last index
2653
2659
  points (bool, default False): if True, this is a points property
2654
2660
  extra_metadata (dictionary, optional): if present, adds extra metadata in the xml
2655
- const_value (float or int, optional): if present, create xml for a constant array filled with this value
2661
+ const_value (float, int or bool, optional): if present, create xml for a constant array filled with this value
2656
2662
  expand_const_arrays (boolean, default False): if True, the hdf5 write must also have been called with the
2657
2663
  same argument and the xml will treat a constant array as a normal array
2658
2664
 
@@ -328,6 +328,7 @@ class BlockedWell(BaseResqpy):
328
328
 
329
329
  def __find_grid_node(self, node, unique_grid_indices):
330
330
  """Find the BlockedWell object's grid reference node(s)."""
331
+
331
332
  grid_node_list = rqet.list_of_tag(node, 'Grid')
332
333
  assert len(grid_node_list) > 0, 'blocked well grid reference(s) not found in xml'
333
334
  assert unique_grid_indices[0] >= -1 and unique_grid_indices[-1] < len(
@@ -344,6 +345,7 @@ class BlockedWell(BaseResqpy):
344
345
 
345
346
  def extract_property_collection(self, refresh = False):
346
347
  """Returns a property collection for the blocked well."""
348
+
347
349
  if self.property_collection is None or refresh:
348
350
  self.property_collection = rqp.PropertyCollection(support = self)
349
351
  return self.property_collection
@@ -407,6 +409,7 @@ class BlockedWell(BaseResqpy):
407
409
 
408
410
  def interval_for_cell(self, cell_index):
409
411
  """Returns the interval index for a given cell index (identical if there are no unblocked intervals)."""
412
+
410
413
  assert 0 <= cell_index < self.cell_count
411
414
  if self.node_count == self.cell_count + 1:
412
415
  return cell_index
@@ -424,11 +427,13 @@ class BlockedWell(BaseResqpy):
424
427
  (float, float) being the entry and exit measured depths for the cell, along the trajectory;
425
428
  uom is held in trajectory object
426
429
  """
430
+
427
431
  interval = self.interval_for_cell(cell_index)
428
432
  return (self.node_mds[interval], self.node_mds[interval + 1])
429
433
 
430
434
  def _set_cell_interval_map(self):
431
435
  """Sets up an index mapping from blocked cell index to interval index, accounting for unblocked intervals."""
436
+
432
437
  self.cell_interval_map = np.zeros(self.cell_count, dtype = int)
433
438
  ci = 0
434
439
  for ii in range(self.node_count - 1):
@@ -718,6 +723,7 @@ class BlockedWell(BaseResqpy):
718
723
 
719
724
  def __derive_from_wellspec_check_well_name(self, well_name):
720
725
  """Set the well name to be used in the wellspec file."""
726
+
721
727
  if well_name:
722
728
  self.well_name = well_name
723
729
  else:
@@ -1211,6 +1217,7 @@ class BlockedWell(BaseResqpy):
1211
1217
  @staticmethod
1212
1218
  def __verify_header_lines_in_cellio_file(fp, well_name, cellio_file):
1213
1219
  """Find and verify the information in the header lines for the specified well in the RMS cellio file."""
1220
+
1214
1221
  while True:
1215
1222
  kf.skip_blank_lines_and_comments(fp)
1216
1223
  line = fp.readline() # file format version number?
@@ -1637,41 +1644,46 @@ class BlockedWell(BaseResqpy):
1637
1644
  if skip_interval_due_to_min_kh:
1638
1645
  continue
1639
1646
 
1640
- length, radw_i, skin_i, radb, wi, wbc = BlockedWell.__get_pc_arrays_for_interval(pc = pc,
1641
- pc_timeless = pc_timeless,
1642
- pc_titles = pc_titles,
1643
- ci = ci,
1644
- length = length,
1645
- radw = radw,
1646
- skin = skin,
1647
- length_uom = length_uom,
1648
- grid = grid,
1649
- traj_crs = traj_crs)
1647
+ length, radw_i, skin_i, radb, wi, wbc, stat_i = \
1648
+ BlockedWell.__get_pc_arrays_for_interval(pc = pc,
1649
+ pc_timeless = pc_timeless,
1650
+ pc_titles = pc_titles,
1651
+ ci = ci,
1652
+ length = length,
1653
+ radw = radw,
1654
+ skin = skin,
1655
+ stat = stat,
1656
+ length_uom = length_uom,
1657
+ grid = grid,
1658
+ traj_crs = traj_crs)
1650
1659
  if skin_i is None:
1651
1660
  skin_i = 0.0
1652
1661
  if radw_i is None:
1653
1662
  radw_i = (0.33 if length_uom == 'ft' else 0.1)
1654
-
1655
- radb, wi, wbc = BlockedWell.__get_well_inflow_parameters_for_interval(do_well_inflow = do_well_inflow,
1656
- isotropic_perm = isotropic_perm,
1657
- ntg_is_one = ntg_is_one,
1658
- k_i = k_i,
1659
- k_j = k_j,
1660
- k_k = k_k,
1661
- sine_anglv = sine_anglv,
1662
- cosine_anglv = cosine_anglv,
1663
- sine_angla = sine_angla,
1664
- cosine_angla = cosine_angla,
1665
- grid = grid,
1666
- cell_kji0 = cell_kji0,
1667
- radw = radw_i,
1668
- radb = radb,
1669
- wi = wi,
1670
- wbc = wbc,
1671
- skin = skin_i,
1672
- kh = kh,
1673
- length_uom = length_uom,
1674
- column_list = column_list)
1663
+ if stat_i is None:
1664
+ stat_i = stat
1665
+
1666
+ radb, wi, wbc = \
1667
+ BlockedWell.__get_well_inflow_parameters_for_interval(do_well_inflow = do_well_inflow,
1668
+ isotropic_perm = isotropic_perm,
1669
+ ntg_is_one = ntg_is_one,
1670
+ k_i = k_i,
1671
+ k_j = k_j,
1672
+ k_k = k_k,
1673
+ sine_anglv = sine_anglv,
1674
+ cosine_anglv = cosine_anglv,
1675
+ sine_angla = sine_angla,
1676
+ cosine_angla = cosine_angla,
1677
+ grid = grid,
1678
+ cell_kji0 = cell_kji0,
1679
+ radw = radw_i,
1680
+ radb = radb,
1681
+ wi = wi,
1682
+ wbc = wbc,
1683
+ skin = skin_i,
1684
+ kh = kh,
1685
+ length_uom = length_uom,
1686
+ column_list = column_list)
1675
1687
 
1676
1688
  xyz = self.__get_xyz_for_interval(doing_xyz = doing_xyz,
1677
1689
  length_mode = length_mode,
@@ -1703,7 +1715,7 @@ class BlockedWell(BaseResqpy):
1703
1715
  kh = kh,
1704
1716
  xyz = xyz,
1705
1717
  md = md,
1706
- stat = stat,
1718
+ stat = stat_i,
1707
1719
  part_perf_fraction = part_perf_fraction,
1708
1720
  radb = radb,
1709
1721
  wi = wi,
@@ -1751,6 +1763,7 @@ class BlockedWell(BaseResqpy):
1751
1763
  fraction of wellbore frame interval in cell,
1752
1764
  fraction of cell's wellbore interval in wellbore frame interval)
1753
1765
  """
1766
+
1754
1767
  return bwf.blocked_well_frame_contributions_list(self, wbf)
1755
1768
 
1756
1769
  def add_properties_from_wellbore_frame(self,
@@ -1965,8 +1978,10 @@ class BlockedWell(BaseResqpy):
1965
1978
  stat = str(stat).upper()
1966
1979
  if 'STAT' not in column_list:
1967
1980
  column_list.append('STAT')
1968
- else:
1969
- stat = 'ON'
1981
+
1982
+
1983
+ # else:
1984
+ #  stat = 'ON'
1970
1985
 
1971
1986
  if radw is not None and 'RADW' not in column_list:
1972
1987
  column_list.append('RADW')
@@ -2418,29 +2433,37 @@ class BlockedWell(BaseResqpy):
2418
2433
  return kh
2419
2434
 
2420
2435
  @staticmethod
2421
- def __get_pc_arrays_for_interval(pc, pc_timeless, pc_titles, ci, length, radw, skin, length_uom, grid, traj_crs):
2436
+ def __get_pc_arrays_for_interval(pc, pc_timeless, pc_titles, ci, length, radw, skin, stat, length_uom, grid,
2437
+ traj_crs):
2422
2438
  """Get the property collection arrays for the interval."""
2423
2439
 
2424
2440
  def get_item(v, title, pc_titles, pc, pc_timeless, ci, uom):
2425
2441
 
2426
2442
  def pk_for_title(title):
2427
- d = {'RADW': 'wellbore radius', 'RADB': 'block equivalent radius', 'SKIN': 'skin'}
2443
+ d = {
2444
+ 'RADW': 'wellbore radius',
2445
+ 'RADB': 'block equivalent radius',
2446
+ 'SKIN': 'skin',
2447
+ 'STAT': 'well connection open'
2448
+ }
2428
2449
  return d.get(title)
2429
2450
 
2451
+ p = None
2452
+ pk = pk_for_title(title)
2430
2453
  pc_uom = None
2431
- if title in pc_titles:
2432
- p = pc.singleton(citation_title = title)
2433
- v = pc.cached_part_array_ref(p)[ci]
2434
- pc_uom = pc.uom_for_part(p)
2435
- elif pc_timeless is not None:
2436
- p = pc_timeless.singleton(citation_title = title)
2437
- if p is None:
2438
- pk = pk_for_title(title)
2439
- if pk is not None:
2440
- p = pc_timeless.singleton(property_kind = pk)
2454
+ for try_pc in [pc, pc_timeless]:
2455
+ if try_pc is None:
2456
+ continue
2457
+ if title in pc_titles:
2458
+ p = try_pc.singleton(citation_title = title)
2459
+ if p is None and pk is not None:
2460
+ p = try_pc.singleton(property_kind = pk)
2441
2461
  if p is not None:
2442
- v = pc_timeless.cached_part_array_ref(p)[ci]
2443
- pc_uom = pc.uom_for_part(p)
2462
+ v = try_pc.cached_part_array_ref(p)[ci]
2463
+ pc_uom = try_pc.uom_for_part(p)
2464
+ break
2465
+ if (title == 'STAT' or pk == 'well connection open') and v is not None and not isinstance(v, str):
2466
+ v = 'ON' if v else 'OFF'
2444
2467
  if pc_uom is not None and uom is not None and pc_uom != uom:
2445
2468
  v = wam.convert_lengths(v, pc_uom, uom)
2446
2469
  return v
@@ -2453,6 +2476,7 @@ class BlockedWell(BaseResqpy):
2453
2476
  r_uom = length_uom
2454
2477
  length = get_item(length, 'LENGTH', pc_titles, pc, pc_timeless, ci, l_uom)
2455
2478
  radw = get_item(radw, 'RADW', pc_titles, pc, pc_timeless, ci, r_uom)
2479
+ stat = get_item(stat, 'STAT', pc_titles, pc, pc_timeless, ci, None)
2456
2480
  assert radw is None or radw > 0.0 # todo: allow zero for inactive intervals?
2457
2481
  skin = get_item(skin, 'SKIN', pc_titles, pc, pc_timeless, ci, None)
2458
2482
  if skin is None:
@@ -2463,7 +2487,7 @@ class BlockedWell(BaseResqpy):
2463
2487
  wi = get_item(None, 'WI', pc_titles, pc, pc_timeless, ci, None)
2464
2488
  wbc = get_item(None, 'WBC', pc_titles, pc, pc_timeless, ci, None)
2465
2489
 
2466
- return length, radw, skin, radb, wi, wbc
2490
+ return length, radw, skin, radb, wi, wbc, stat
2467
2491
 
2468
2492
  @staticmethod
2469
2493
  def __get_well_inflow_parameters_for_interval(do_well_inflow, isotropic_perm, ntg_is_one, k_i, k_j, k_k, sine_anglv,
@@ -2689,6 +2713,7 @@ class BlockedWell(BaseResqpy):
2689
2713
  this method currently only handles single grid situations;
2690
2714
  dataframe rows must be in the same order as the cells in the blocked well
2691
2715
  """
2716
+
2692
2717
  # todo: enhance to handle multiple grids
2693
2718
  assert len(self.grid_list) == 1
2694
2719
  if columns is None or len(columns) == 0 or len(df) == 0:
@@ -2713,8 +2738,9 @@ class BlockedWell(BaseResqpy):
2713
2738
  # 'SKIN': use defaults for now; todo: create local property kind for skin
2714
2739
  if column == 'STAT':
2715
2740
  col_as_list = list(df[column])
2716
- expanded = np.array([(0 if (str(st).upper() in ['OFF', '0']) else 1) for st in col_as_list],
2717
- dtype = int)
2741
+ expanded = np.array([(0 if (str(st).upper() in ['OFF', '0', 'FALSE']) else 1) for st in col_as_list],
2742
+ dtype = np.int8)
2743
+ dtype = np.int8
2718
2744
  else:
2719
2745
  expanded = df[column].to_numpy(dtype = dtype, copy = True, na_value = na_value)
2720
2746
  extra_pc.add_cached_array_to_imported_list(
@@ -2739,6 +2765,7 @@ class BlockedWell(BaseResqpy):
2739
2765
 
2740
2766
  def _get_uom_pk_discrete_for_df_properties(self, extra, length_uom, temperature_uom = None):
2741
2767
  """Set the property kind and unit of measure for all properties in the dataframe."""
2768
+
2742
2769
  # todo: this is horribly inefficient, building a whole dictionary for every call but only using one entry
2743
2770
  if length_uom not in ['m', 'ft']:
2744
2771
  raise ValueError(f"The length_uom {length_uom} must be either 'm' or 'ft'.")
@@ -3297,6 +3324,7 @@ class BlockedWell(BaseResqpy):
3297
3324
  def __create_trajectory_xml_if_needed(self, create_for_trajectory_if_needed, add_as_part, add_relationships,
3298
3325
  originator, ext_uuid, title):
3299
3326
  """Create root node for associated Trajectory object if necessary."""
3327
+
3300
3328
  if create_for_trajectory_if_needed and self.trajectory_to_be_written and self.trajectory.root is None:
3301
3329
  md_datum_root = self.trajectory.md_datum.create_xml(add_as_part = add_as_part,
3302
3330
  add_relationships = add_relationships,
@@ -3311,6 +3339,7 @@ class BlockedWell(BaseResqpy):
3311
3339
 
3312
3340
  def __create_bw_node_sub_elements(self, bw_node):
3313
3341
  """Append sub-elements to the BlockedWell object's root node."""
3342
+
3314
3343
  nc_node = rqet.SubElement(bw_node, ns['resqml2'] + 'NodeCount')
3315
3344
  nc_node.set(ns['xsi'] + 'type', ns['xsd'] + 'positiveInteger')
3316
3345
  nc_node.text = str(self.node_count)
@@ -3369,7 +3398,8 @@ class BlockedWell(BaseResqpy):
3369
3398
  fis_values_node.set(ns['xsi'] + 'type', ns['eml'] + 'Hdf5Dataset')
3370
3399
  fis_values_node.text = rqet.null_xml_text
3371
3400
 
3372
- return nc_node, mds_node, mds_values_node, cc_node, cis_node, cnull_node, cis_values_node, gis_node, gnull_node, gis_values_node, fis_node, fnull_node, fis_values_node
3401
+ return (nc_node, mds_node, mds_values_node, cc_node, cis_node, cnull_node, cis_values_node, gis_node,
3402
+ gnull_node, gis_values_node, fis_node, fnull_node, fis_values_node)
3373
3403
 
3374
3404
  def __create_trajectory_grid_wellbore_interpretation_reference_nodes(self, bw_node):
3375
3405
  """Create nodes and add to BlockedWell object root node."""
@@ -419,8 +419,13 @@ class Trajectory(BaseResqpy):
419
419
  set_tangent_vectors = True):
420
420
  """Load MD and control points (xyz) data directly from numpy arrays."""
421
421
 
422
+ if isinstance(mds, list) or isinstance(mds, tuple):
423
+ mds = np.array(mds, dtype = float)
424
+ assert mds.ndim == 1
422
425
  self.knot_count = len(mds)
423
426
  assert self.knot_count >= 2 # vertical well could be hamdled by allowing a single station in survey?
427
+ if isinstance(control_points, list) or isinstance(control_points, tuple):
428
+ control_points = np.array(control_points, dtype = float)
424
429
  assert control_points.shape == (self.knot_count, 3)
425
430
  self.line_kind_index = 5 # assume minimum curvature spline
426
431
  self.md_uom = wam.rq_length_unit(md_uom)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: resqpy
3
- Version: 4.16.2
3
+ Version: 4.16.4
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=V_KEFWKpjsovnzmh69UtTXTHOi6WVOZzJJZCMhJBFxs,556
1
+ resqpy/__init__.py,sha256=BXevcj5cpaKQi4covmL8avFA-mmrCtOHy_bpSrVAVik,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
@@ -58,13 +58,13 @@ resqpy/lines/_common.py,sha256=nlgVgZ8utHXuHDyzuP0IpkUJViDNmmy7EBqwLzxSI2M,11960
58
58
  resqpy/lines/_polyline.py,sha256=37E0pGDDaBuqtmvXq2zHyYO97uUIUWljZoSWNibQemc,42659
59
59
  resqpy/lines/_polyline_set.py,sha256=3K3z_G9l_3mfjLdCL-YVscyj1FA6DHh1uj9rXPtWFOY,27986
60
60
  resqpy/model/__init__.py,sha256=hbxO-IpCOH_82TZqj6e1FjrWxO0tZu2gj2HCN9x-Svw,378
61
- resqpy/model/_catalogue.py,sha256=ZSzLaZWBLgrna6DPPfEQK72hmuRieLrWr5jhj51YIsI,30205
61
+ resqpy/model/_catalogue.py,sha256=duvZlNlTPjAfXyqae0J9lMSEx_8-WIcrw2MYxnNEg_Q,30487
62
62
  resqpy/model/_context.py,sha256=0tLBVMcuuIj3i87Ig8lhFMLHE5GHgEA2PEl1NjKaohc,2840
63
- resqpy/model/_forestry.py,sha256=HRKCGnfqSb-J9zbeUcVMbYb7oUzIrvxmzxe3jFh5lvk,34670
63
+ resqpy/model/_forestry.py,sha256=QYE3P9uSsh77J6ghcgp2cBQP6UKrs8edF-m05sqgboo,34518
64
64
  resqpy/model/_grids.py,sha256=d7hRQRmni5pJrm1CY31D2icJV1XDar7xTmUexq_eVGY,3371
65
65
  resqpy/model/_hdf5.py,sha256=2iQdyUp5eNLfWhmvl_XuFO8j7dCdtTBjtrr6-9mq3Qg,14238
66
- resqpy/model/_model.py,sha256=8CTqYCxSoiA8ONrxEQ_vq20qTJfHkhhF-tiJUTwfEEk,103055
67
- resqpy/model/_xml.py,sha256=n042AUhJU8AV1S4GdeeAdXikSqu2UGoSYT6AMXS2_bA,23589
66
+ resqpy/model/_model.py,sha256=MMiYRrBUitraEg7IeM09PagTP3hZ_e1JJv_C7E6zZX4,105370
67
+ resqpy/model/_xml.py,sha256=TiZKHZezMdcjRvHSa-HzzrYe9kyDdd8L4hacNV0bjEg,24402
68
68
  resqpy/multi_processing/__init__.py,sha256=ZRudHfN9aaZjxvat7t8BZr6mwMi9baiCNjczwwT0WjI,909
69
69
  resqpy/multi_processing/_multiprocessing.py,sha256=bnCKfSC1tWwvZmZ7BZqCyje0C93m6q7HZPxNpx8xoxA,7301
70
70
  resqpy/multi_processing/wrappers/__init__.py,sha256=7vjuTWdHnp3rN9Ud8ljpDnt1NbBAyhA08lv-sQ9Kf3o,72
@@ -103,7 +103,7 @@ resqpy/olio/volume.py,sha256=F1pMnDoJ4ricy4dfdLuLuK1xkVgJckL9V06tUeyK6Wc,6185
103
103
  resqpy/olio/wellspec_keywords.py,sha256=MqrXRSzVfXAKUhiJkop3EwoVdKEjgZRwHYBJjx2IW8Q,26265
104
104
  resqpy/olio/write_data.py,sha256=bIX7ilMkXWCMz_zQh-Gqk56sNzng4W5l4BahW2EV7Kw,5142
105
105
  resqpy/olio/write_hdf5.py,sha256=KXB2L6Qz3TFb9yDjT-Ty0CXgjyq0nhVp3GADlekWhMQ,19055
106
- resqpy/olio/xml_et.py,sha256=Rt0i1ZTALebB4KKT9AQAXUUxc3QrjEp409RNktEZiow,24988
106
+ resqpy/olio/xml_et.py,sha256=aPjxr2XWvYcwlnckL_UiZmd5EGEoIfy_JxeOKOk3vwQ,25322
107
107
  resqpy/olio/xml_namespaces.py,sha256=PiQi2W7gRLxhMSEs26ahT4MlagYqsjMWJlNpIiZupoA,1824
108
108
  resqpy/olio/zmap_reader.py,sha256=YuhZjde_DZszVFsUKfu0XieKGg7ONXtJWskRV9Pw7VE,5709
109
109
  resqpy/organize/__init__.py,sha256=H1c7B4aRJf5peaGUTvGrCVug4Xr_JW38zeWL-LyCDtk,2206
@@ -128,14 +128,14 @@ resqpy/organize/tectonic_boundary_feature.py,sha256=mMSsIW8YJ6qpeOe-6gRFaQbK39-W
128
128
  resqpy/organize/wellbore_feature.py,sha256=E2gFwgPZGr8nkhRHAIR0wTZ8uwcze7y2WBIV7AXeW2A,1843
129
129
  resqpy/organize/wellbore_interpretation.py,sha256=jRAHq90tR2dCQSXsZicujXhSVHOEPoGjFgh5S87SMAI,6973
130
130
  resqpy/property/__init__.py,sha256=KegXDizklsMB-EnGFrzhCSszrXAHXEIoStdC5XmyifQ,2294
131
- resqpy/property/_collection_add_part.py,sha256=s0xXU5aLRqvZoHxbRUNY-ItiNPqmUoEMcnWqQh011jk,17076
131
+ resqpy/property/_collection_add_part.py,sha256=7GRtiYVP5nmIj48Xynbu19evhQslnbbfiM8Wfnz3vcU,17101
132
132
  resqpy/property/_collection_create_xml.py,sha256=E48fu8h64T_bz5k3OEqIzPvZAOYRTBgvQ75wreqMwZc,12915
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=AI0rS3bn38DJLQ_5XLdkeol8Oa9tGpTrX57d3435byQ,12132
136
+ resqpy/property/attribute_property_set.py,sha256=gATFe-vI00GrgaJNMHSKbM0xmlxIsO5DT1qRSU9snYI,12295
137
137
  resqpy/property/grid_property_collection.py,sha256=bLWCTfhmpDsagBaXXb8XXHL46Cy78HL_NGWpPFZAgdw,66946
138
- resqpy/property/property_collection.py,sha256=b4J_bzigN-P5nag49Y6IG4mq3s1KbelsQC5frYg2ij8,151911
138
+ resqpy/property/property_collection.py,sha256=i5YL4GdehsI1cR8gtnfERMwcXsT964AMmegey3hNfww,152167
139
139
  resqpy/property/property_common.py,sha256=wf429weNtgf6J4gCNNoRwj64elQvUPI_ZWzg4qI7l6c,35993
140
140
  resqpy/property/property_kind.py,sha256=6SK0La09eisHPYOSgdx69K_Ordrq0x2tz4IAyurVyY8,5557
141
141
  resqpy/property/string_lookup.py,sha256=eH-lbLRbb8JKtO91FTtyuYseRGwPkmsghcXbKUTvVgI,7737
@@ -184,17 +184,17 @@ resqpy/weights_and_measures/__init__.py,sha256=Kp1pPZFH4rS5_PkCERZBEzGoat6n_dSS0
184
184
  resqpy/weights_and_measures/nexus_units.py,sha256=pHqcFbe-8nyqozFgN5Ce-W-UvEnXQ6yiaX3dP1ONtAQ,5434
185
185
  resqpy/weights_and_measures/weights_and_measures.py,sha256=1WnrmhtcyCgY2crClNddmfwtf33JdVZy9wNhqPQIvuc,16210
186
186
  resqpy/well/__init__.py,sha256=v5_gd7sSPRM9q2KsLiLWaw3jbnXFZkou38qeB7_HSN4,990
187
- resqpy/well/_blocked_well.py,sha256=JIe65xusZUd4tpo2TZaUSynUoDExHG783VYApZ6nZtY,191992
187
+ resqpy/well/_blocked_well.py,sha256=07Do0oKvxPFc5HL4SWEwjWE0xYIeJHQCOB2DtI6YgzI,192034
188
188
  resqpy/well/_deviation_survey.py,sha256=d3u31JbBqMCsaz6MUrtZium90wrC3omtm46A755fvgk,23115
189
189
  resqpy/well/_md_datum.py,sha256=rRrDQckTJwZtIEh28dlgXj32kcBSu-ZvHFYZOiQsyqg,7154
190
- resqpy/well/_trajectory.py,sha256=tycBqo24z58As1tier64gobapDc60zVWhIVhRu_Viic,52484
190
+ resqpy/well/_trajectory.py,sha256=QGFgdYVvA_gJth3n-RaFPS71S5KGgECnNMx0dp1r4Tw,52771
191
191
  resqpy/well/_wellbore_frame.py,sha256=rzWsBnM-L2NbSpdk-6F5BKYeaqDWbwrIrlpkPjtt0kE,15192
192
192
  resqpy/well/_wellbore_marker.py,sha256=ZqcZC5Xmta3IJOAaZXZAWAQX9iaS312WjhnJSge8yks,8403
193
193
  resqpy/well/_wellbore_marker_frame.py,sha256=xvYH2_2Ie3a18LReFymbUrZboOx7Rhv5DODEVO4-B-k,20933
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.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,,
197
+ resqpy-4.16.4.dist-info/LICENSE,sha256=2duHPIkKQyESMdQ4hKjL8CYEsYRHXaYxt0YQkzsUYE4,1059
198
+ resqpy-4.16.4.dist-info/METADATA,sha256=E_5Hy6Q2M8CAe6ESaa0CaNgOl76upOfLan60-EWrxwY,4028
199
+ resqpy-4.16.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
200
+ resqpy-4.16.4.dist-info/RECORD,,