pyetp 0.0.39__py3-none-any.whl → 0.0.43__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.
- pyetp/__init__.py +10 -2
- pyetp/_version.py +34 -0
- pyetp/client.py +561 -620
- pyetp/config.py +9 -10
- pyetp/{resqml_objects/__init__.py → resqml_objects.py} +82 -72
- pyetp/types.py +130 -93
- pyetp/uri.py +28 -16
- pyetp/utils_arrays.py +224 -98
- pyetp/utils_xml.py +44 -678
- pyetp-0.0.43.dist-info/METADATA +88 -0
- pyetp-0.0.43.dist-info/RECORD +21 -0
- {pyetp-0.0.39.dist-info → pyetp-0.0.43.dist-info}/WHEEL +2 -1
- pyetp-0.0.43.dist-info/top_level.txt +2 -0
- resqml_objects/__init__.py +7 -0
- resqml_objects/epc_readers.py +114 -0
- resqml_objects/parsers.py +12 -0
- resqml_objects/serializers.py +10 -0
- resqml_objects/v201/__init__.py +1847 -0
- {pyetp/resqml_objects → resqml_objects/v201}/generated.py +2244 -2185
- resqml_objects/v201/utils.py +46 -0
- pyetp/utils.py +0 -15
- pyetp-0.0.39.dist-info/METADATA +0 -56
- pyetp-0.0.39.dist-info/RECORD +0 -14
- {pyetp-0.0.39.dist-info → pyetp-0.0.43.dist-info/licenses}/LICENSE.md +0 -0
pyetp/utils_xml.py
CHANGED
|
@@ -1,63 +1,22 @@
|
|
|
1
1
|
import datetime
|
|
2
|
-
import logging
|
|
3
2
|
import typing as T
|
|
4
3
|
from uuid import uuid4
|
|
5
|
-
|
|
6
|
-
import lxml.etree as ET
|
|
7
|
-
from xsdata.formats.dataclass.context import XmlContext
|
|
8
|
-
from xsdata.formats.dataclass.parsers import XmlParser
|
|
9
|
-
from xsdata.formats.dataclass.serializers import XmlSerializer
|
|
10
|
-
from xsdata.formats.dataclass.serializers.config import SerializerConfig
|
|
4
|
+
|
|
11
5
|
from xsdata.models.datatype import XmlDateTime
|
|
12
|
-
import resqpy.model as rq
|
|
13
|
-
import resqpy.time_series as rts
|
|
14
|
-
import resqpy.unstructured as rug
|
|
15
6
|
|
|
16
|
-
import
|
|
17
|
-
import pyetp.resqml_objects as ro
|
|
18
|
-
#import energyml.resqml.v2_0_1.resqmlv2 as ro
|
|
19
|
-
#import energyml.eml.v2_0.commonv2 as roc
|
|
7
|
+
import resqml_objects.v201 as ro
|
|
20
8
|
from pyetp.config import SETTINGS
|
|
21
|
-
from
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
9
|
+
from resqml_objects.v201.utils import (
|
|
10
|
+
common_schema_version,
|
|
11
|
+
get_data_object_reference,
|
|
12
|
+
resqml_schema_version,
|
|
13
|
+
)
|
|
25
14
|
|
|
26
15
|
if T.TYPE_CHECKING:
|
|
27
16
|
from xtgeo import RegularSurface
|
|
28
17
|
|
|
29
18
|
|
|
30
|
-
schema_version = "2.0.1"
|
|
31
|
-
|
|
32
|
-
def get_data_object_type(obj: ro.AbstractObject):
|
|
33
|
-
return obj.__class__.__name__
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def parse_resqml_objects(data_objects: T.List[DataObject]):
|
|
37
|
-
# This function creates a list of resqml-objects from the returned xml from
|
|
38
|
-
# the ETP-server. It dynamically finds the relevant resqml dataclass using
|
|
39
|
-
# the object name found in the xml. Its intention is to be used after
|
|
40
|
-
# calling the get_data_objects-protocol.
|
|
41
|
-
|
|
42
|
-
# Set up an XML-parser from xsdata.
|
|
43
|
-
parser = XmlParser(context=XmlContext())
|
|
44
|
-
|
|
45
|
-
return [
|
|
46
|
-
parser.from_bytes(
|
|
47
|
-
data_object.data,
|
|
48
|
-
getattr(ro, ET.QName(ET.fromstring(data_object.data).tag).localname),
|
|
49
|
-
)
|
|
50
|
-
for data_object in data_objects
|
|
51
|
-
]
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def resqml_to_xml(obj: ro.AbstractObject):
|
|
55
|
-
serializer = XmlSerializer(config=SerializerConfig())
|
|
56
|
-
return str.encode(serializer.render(obj))
|
|
57
|
-
|
|
58
|
-
|
|
59
19
|
def create_common_citation(title: str):
|
|
60
|
-
|
|
61
20
|
return ro.Citation(
|
|
62
21
|
title=title,
|
|
63
22
|
creation=XmlDateTime.from_string(
|
|
@@ -68,10 +27,15 @@ def create_common_citation(title: str):
|
|
|
68
27
|
)
|
|
69
28
|
|
|
70
29
|
|
|
71
|
-
def create_common_crs(
|
|
30
|
+
def create_common_crs(
|
|
31
|
+
title: str,
|
|
32
|
+
projected_epsg,
|
|
33
|
+
rotation: float = 0.0,
|
|
34
|
+
resqml_schema_version: str = resqml_schema_version,
|
|
35
|
+
):
|
|
72
36
|
return ro.LocalDepth3dCrs(
|
|
73
37
|
citation=create_common_citation(f"CRS for {title}"),
|
|
74
|
-
schema_version=
|
|
38
|
+
schema_version=resqml_schema_version,
|
|
75
39
|
uuid=str(uuid4()),
|
|
76
40
|
# NOTE: I assume that we let the CRS have no offset, and add any offset
|
|
77
41
|
# in the grid instead.
|
|
@@ -88,25 +52,23 @@ def create_common_crs(title: str, projected_epsg, rotation: float = 0.0):
|
|
|
88
52
|
projected_uom=ro.LengthUom.M,
|
|
89
53
|
vertical_uom=ro.LengthUom.M,
|
|
90
54
|
zincreasing_downward=True,
|
|
91
|
-
vertical_crs=ro.VerticalCrsEpsgCode(
|
|
92
|
-
epsg_code=projected_epsg
|
|
93
|
-
),
|
|
55
|
+
vertical_crs=ro.VerticalCrsEpsgCode(epsg_code=projected_epsg),
|
|
94
56
|
projected_crs=ro.ProjectedCrsEpsgCode(
|
|
95
57
|
epsg_code=projected_epsg,
|
|
96
58
|
),
|
|
97
59
|
)
|
|
98
60
|
|
|
99
61
|
|
|
100
|
-
def create_epc(
|
|
62
|
+
def create_epc(common_schema_version: str = common_schema_version):
|
|
101
63
|
return ro.EpcExternalPartReference(
|
|
102
64
|
citation=create_common_citation("Hdf Proxy"),
|
|
103
|
-
schema_version=
|
|
65
|
+
schema_version=common_schema_version,
|
|
104
66
|
uuid=str(uuid4()),
|
|
105
67
|
mime_type="application/x-hdf5",
|
|
106
68
|
)
|
|
107
69
|
|
|
108
70
|
|
|
109
|
-
def parse_xtgeo_surface_to_resqml_grid(surf:
|
|
71
|
+
def parse_xtgeo_surface_to_resqml_grid(surf: "RegularSurface", projected_epsg: int):
|
|
110
72
|
# Build the RESQML-objects "manually" from the generated dataclasses.
|
|
111
73
|
# Their content is described also in the RESQML v2.0.1 standard that is
|
|
112
74
|
# available for download here:
|
|
@@ -119,18 +81,38 @@ def parse_xtgeo_surface_to_resqml_grid(surf: 'RegularSurface', projected_epsg: i
|
|
|
119
81
|
# slowest changing axis, and we have surf.values.shape == (surf.ncol,
|
|
120
82
|
# surf.nrow). The author of this note finds that confusing, but such is
|
|
121
83
|
# life.
|
|
122
|
-
epc, crs, gri = instantiate_resqml_grid(
|
|
84
|
+
epc, crs, gri = instantiate_resqml_grid(
|
|
85
|
+
title,
|
|
86
|
+
surf.get_rotation(),
|
|
87
|
+
surf.xori,
|
|
88
|
+
surf.yori,
|
|
89
|
+
surf.xinc,
|
|
90
|
+
surf.yinc,
|
|
91
|
+
surf.ncol,
|
|
92
|
+
surf.nrow,
|
|
93
|
+
projected_epsg,
|
|
94
|
+
)
|
|
123
95
|
return epc, crs, gri
|
|
124
96
|
|
|
125
97
|
|
|
126
|
-
def instantiate_resqml_grid(
|
|
127
|
-
|
|
98
|
+
def instantiate_resqml_grid(
|
|
99
|
+
name: str,
|
|
100
|
+
rotation: float,
|
|
101
|
+
x0: float,
|
|
102
|
+
y0: float,
|
|
103
|
+
dx: float,
|
|
104
|
+
dy: float,
|
|
105
|
+
nx: int,
|
|
106
|
+
ny: int,
|
|
107
|
+
epsg: int,
|
|
108
|
+
resqml_schema_version: str = resqml_schema_version,
|
|
109
|
+
):
|
|
128
110
|
epc = create_epc()
|
|
129
111
|
crs = create_common_crs(name, epsg, rotation)
|
|
130
112
|
|
|
131
113
|
gri = ro.Grid2dRepresentation(
|
|
132
114
|
uuid=(grid_uuid := str(uuid4())),
|
|
133
|
-
schema_version=
|
|
115
|
+
schema_version=resqml_schema_version,
|
|
134
116
|
surface_role=ro.SurfaceRole.MAP,
|
|
135
117
|
citation=create_common_citation(name),
|
|
136
118
|
grid2d_patch=ro.Grid2dPatch(
|
|
@@ -144,16 +126,7 @@ def instantiate_resqml_grid(name: str, rotation: float, x0: float, y0: float, dx
|
|
|
144
126
|
fastest_axis_count=ny,
|
|
145
127
|
slowest_axis_count=nx,
|
|
146
128
|
geometry=ro.PointGeometry(
|
|
147
|
-
local_crs=
|
|
148
|
-
# NOTE: See Energistics Identifier Specification 4.0
|
|
149
|
-
# (it is downloaded alongside the RESQML v2.0.1
|
|
150
|
-
# standard) section 4.1 for an explanation on the
|
|
151
|
-
# format of content_type.
|
|
152
|
-
content_type=f"application/x-resqml+xml;version={schema_version};type={get_data_object_type(crs)}",
|
|
153
|
-
title=crs.citation.title,
|
|
154
|
-
uuid=crs.uuid,
|
|
155
|
-
)
|
|
156
|
-
,
|
|
129
|
+
local_crs=get_data_object_reference(crs),
|
|
157
130
|
points=ro.Point3dZValueArray(
|
|
158
131
|
supporting_geometry=ro.Point3dLatticeArray(
|
|
159
132
|
origin=ro.Point3d(
|
|
@@ -200,11 +173,7 @@ def instantiate_resqml_grid(name: str, rotation: float, x0: float, y0: float, dx
|
|
|
200
173
|
zvalues=ro.DoubleHdf5Array(
|
|
201
174
|
values=ro.Hdf5Dataset(
|
|
202
175
|
path_in_hdf_file=f"/RESQML/{grid_uuid}/zvalues",
|
|
203
|
-
hdf_proxy=
|
|
204
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
205
|
-
title=epc.citation.title,
|
|
206
|
-
uuid=epc.uuid,
|
|
207
|
-
),
|
|
176
|
+
hdf_proxy=get_data_object_reference(epc),
|
|
208
177
|
),
|
|
209
178
|
),
|
|
210
179
|
),
|
|
@@ -212,606 +181,3 @@ def instantiate_resqml_grid(name: str, rotation: float, x0: float, y0: float, dx
|
|
|
212
181
|
),
|
|
213
182
|
)
|
|
214
183
|
return epc, crs, gri
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
def uom_for_prop_title(pt: str):
|
|
218
|
-
if (pt == "Age"):
|
|
219
|
-
return ro.ResqmlUom.A_1
|
|
220
|
-
if (pt == "Temperature"):
|
|
221
|
-
return ro.ResqmlUom.DEG_C
|
|
222
|
-
if (pt == "LayerID"):
|
|
223
|
-
return ro.ResqmlUom.EUC
|
|
224
|
-
if (pt == "Porosity_initial"):
|
|
225
|
-
return ro.ResqmlUom.M3_M3
|
|
226
|
-
if (pt == "Porosity_decay"):
|
|
227
|
-
return ro.ResqmlUom.VALUE_1_M
|
|
228
|
-
if (pt == "Density_solid"):
|
|
229
|
-
return ro.ResqmlUom.KG_M3
|
|
230
|
-
if (pt == "insulance_thermal"):
|
|
231
|
-
return ro.ThermalInsulanceUom.DELTA_K_M2_W
|
|
232
|
-
if (pt == "Radiogenic_heat_production"):
|
|
233
|
-
return ro.ResqmlUom.U_W_M3
|
|
234
|
-
if (pt == 'dynamic nodes') or (pt=='points'):
|
|
235
|
-
return ro.ResqmlUom.M
|
|
236
|
-
if (pt == 'thermal_conductivity'):
|
|
237
|
-
return ro.ResqmlUom.W_M_K
|
|
238
|
-
if (pt == 'Vitrinite reflectance' or pt == '%Ro'):
|
|
239
|
-
return ro.ResqmlUom.VALUE
|
|
240
|
-
if ("Expelled" in pt):
|
|
241
|
-
return ro.ResqmlUom.KG_M3
|
|
242
|
-
if ("Transformation" in pt):
|
|
243
|
-
return ro.ResqmlUom.VALUE
|
|
244
|
-
return ro.ResqmlUom.EUC
|
|
245
|
-
|
|
246
|
-
def create_resqml_property(prop_title:str, continuous: bool, indexable_element: ro.IndexableElements, uns: ro.UnstructuredGridRepresentation, epc: ro.EpcExternalPartReference, min_val=0.0, max_val=1.0,
|
|
247
|
-
timeseries=None, time_index=-1, pre_existing_propertykind = None):
|
|
248
|
-
timeindex_ref = None
|
|
249
|
-
use_timeseries = timeseries is not None
|
|
250
|
-
if use_timeseries:
|
|
251
|
-
# time_index = time_indices[i]
|
|
252
|
-
timeindex_ref = ro.TimeIndex(
|
|
253
|
-
index = time_index,
|
|
254
|
-
time_series = ro.DataObjectReference(
|
|
255
|
-
content_type=f"application/x-resqml+xml;version={schema_version};type={get_data_object_type(timeseries)}",
|
|
256
|
-
title=timeseries.citation.title,
|
|
257
|
-
uuid=timeseries.uuid,
|
|
258
|
-
)
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
r_uom = ro.ResqmlUom( value= uom_for_prop_title(prop_title) )
|
|
262
|
-
|
|
263
|
-
if (pre_existing_propertykind is None):
|
|
264
|
-
pk_uuid = uuid4()
|
|
265
|
-
propertykind0 = ro.PropertyKind(
|
|
266
|
-
schema_version=schema_version,
|
|
267
|
-
citation=create_common_citation(f"{prop_title}"),
|
|
268
|
-
naming_system="urn:resqml:bp.com:resqpy",
|
|
269
|
-
is_abstract=False,
|
|
270
|
-
representative_uom=uom_for_prop_title(prop_title),
|
|
271
|
-
parent_property_kind=ro.StandardPropertyKind(
|
|
272
|
-
kind=ro.ResqmlPropertyKind.CONTINUOUS if continuous else ro.ResqmlPropertyKind.DISCRETE
|
|
273
|
-
),
|
|
274
|
-
uuid=str(pk_uuid),
|
|
275
|
-
)
|
|
276
|
-
else:
|
|
277
|
-
propertykind0 = pre_existing_propertykind
|
|
278
|
-
|
|
279
|
-
prop_uuid = uuid4()
|
|
280
|
-
|
|
281
|
-
pov = ro.PatchOfValues(
|
|
282
|
-
values=ro.DoubleHdf5Array(
|
|
283
|
-
values=ro.Hdf5Dataset(
|
|
284
|
-
path_in_hdf_file=f"/RESQML/{str(prop_uuid)}/values",
|
|
285
|
-
hdf_proxy=ro.DataObjectReference(
|
|
286
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
287
|
-
title=epc.citation.title,
|
|
288
|
-
uuid=str(epc.uuid),
|
|
289
|
-
),
|
|
290
|
-
)
|
|
291
|
-
) if continuous else
|
|
292
|
-
ro.IntegerHdf5Array(
|
|
293
|
-
values=ro.Hdf5Dataset(
|
|
294
|
-
path_in_hdf_file=f"/RESQML/{str(prop_uuid)}/values",
|
|
295
|
-
hdf_proxy=ro.DataObjectReference(
|
|
296
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
297
|
-
title=epc.citation.title,
|
|
298
|
-
uuid=str(epc.uuid),
|
|
299
|
-
),
|
|
300
|
-
),
|
|
301
|
-
null_value=int(1e30),
|
|
302
|
-
)
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
if (continuous):
|
|
306
|
-
cprop0 = ro.ContinuousProperty(
|
|
307
|
-
schema_version=schema_version,
|
|
308
|
-
citation=create_common_citation(f"{prop_title}"),
|
|
309
|
-
uuid=str(prop_uuid),
|
|
310
|
-
uom = r_uom,
|
|
311
|
-
count=1,
|
|
312
|
-
indexable_element=indexable_element,
|
|
313
|
-
supporting_representation=ro.DataObjectReference(
|
|
314
|
-
content_type=f"application/x-resqml+xml;version={schema_version};type={get_data_object_type(uns)}",
|
|
315
|
-
title=uns.citation.title,
|
|
316
|
-
uuid=uns.uuid,
|
|
317
|
-
),
|
|
318
|
-
property_kind= propertykind0 if pre_existing_propertykind is not None else ro.LocalPropertyKind(
|
|
319
|
-
local_property_kind=ro.DataObjectReference(
|
|
320
|
-
content_type=f"application/x-resqml+xml;version={schema_version};type={get_data_object_type(propertykind0)}",
|
|
321
|
-
title=propertykind0.citation.title,
|
|
322
|
-
uuid=propertykind0.uuid,
|
|
323
|
-
)
|
|
324
|
-
), # if (propertykind0 is not None) else ro.StandardPropertyKind(kind=prop.property_kind()),
|
|
325
|
-
minimum_value=[min_val],
|
|
326
|
-
maximum_value=[max_val],
|
|
327
|
-
facet=[ro.PropertyKindFacet(
|
|
328
|
-
facet=ro.Facet.WHAT,
|
|
329
|
-
value=prop_title, # prop.facet(),
|
|
330
|
-
)],
|
|
331
|
-
patch_of_values=[pov],
|
|
332
|
-
time_index=timeindex_ref,
|
|
333
|
-
)
|
|
334
|
-
else:
|
|
335
|
-
cprop0 = ro.DiscreteProperty(
|
|
336
|
-
schema_version=schema_version,
|
|
337
|
-
citation=create_common_citation(f"{prop_title}"),
|
|
338
|
-
uuid=str(prop_uuid),
|
|
339
|
-
# uom = prop.uom(),
|
|
340
|
-
count=1,
|
|
341
|
-
indexable_element=indexable_element,
|
|
342
|
-
supporting_representation=ro.DataObjectReference(
|
|
343
|
-
content_type=f"application/x-resqml+xml;version={schema_version};type={get_data_object_type(uns)}",
|
|
344
|
-
title=uns.citation.title,
|
|
345
|
-
uuid=uns.uuid,
|
|
346
|
-
),
|
|
347
|
-
property_kind=propertykind0 if pre_existing_propertykind is not None else ro.LocalPropertyKind(
|
|
348
|
-
local_property_kind=ro.DataObjectReference(
|
|
349
|
-
content_type=f"application/x-resqml+xml;version={schema_version};type={get_data_object_type(propertykind0)}",
|
|
350
|
-
title=propertykind0.citation.title,
|
|
351
|
-
uuid=propertykind0.uuid,
|
|
352
|
-
)
|
|
353
|
-
), # if (propertykind0 is not None) else ro.StandardPropertyKind(kind=prop.property_kind()),
|
|
354
|
-
minimum_value=[int(min_val)],
|
|
355
|
-
maximum_value=[int(max_val)],
|
|
356
|
-
facet=[ro.PropertyKindFacet(
|
|
357
|
-
facet=ro.Facet.WHAT,
|
|
358
|
-
value=prop_title, # prop.facet(),
|
|
359
|
-
)],
|
|
360
|
-
patch_of_values=[pov],
|
|
361
|
-
time_index=timeindex_ref,
|
|
362
|
-
)
|
|
363
|
-
return cprop0, propertykind0
|
|
364
|
-
|
|
365
|
-
def create_resqml_mesh(rmdi, rmdts, geotimes, projected_epsg: int): #(rddms_mesh_data_initial, rddms_upload_data_timestep)
|
|
366
|
-
|
|
367
|
-
ro_timestamps = []
|
|
368
|
-
for i in geotimes:
|
|
369
|
-
ro_timestamps.append(
|
|
370
|
-
ro.Timestamp(
|
|
371
|
-
date_time=XmlDateTime.from_string("0001-01-01T00:00:00.00+00:00"),
|
|
372
|
-
year_offset=int(i),
|
|
373
|
-
)
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
gts_citation_title = "warmth simulation"
|
|
377
|
-
gts_uuid = uuid4()
|
|
378
|
-
|
|
379
|
-
timeseries = ro.TimeSeries(
|
|
380
|
-
citation=create_common_citation(str(gts_citation_title)),
|
|
381
|
-
schema_version=schema_version,
|
|
382
|
-
uuid=str(gts_uuid),
|
|
383
|
-
time = ro_timestamps,
|
|
384
|
-
)
|
|
385
|
-
crs = create_common_crs(gts_citation_title, projected_epsg)
|
|
386
|
-
epc = ro.EpcExternalPartReference(
|
|
387
|
-
citation=create_common_citation("Hdf Proxy"),
|
|
388
|
-
schema_version=schema_version,
|
|
389
|
-
uuid=str(uuid4()),
|
|
390
|
-
mime_type="application/x-hdf5",
|
|
391
|
-
)
|
|
392
|
-
cellshape = ro.CellShape.HEXAHEDRAL ## if (hexa.cell_shape == "hexahedral") else ro.CellShape.TETRAHEDRAL
|
|
393
|
-
cells = rmdi.hexa_renumbered
|
|
394
|
-
nodes_time_0 = rmdts.points_cached
|
|
395
|
-
node_count = nodes_time_0.shape[0]
|
|
396
|
-
faces_per_cell = []
|
|
397
|
-
nodes_per_face = []
|
|
398
|
-
faces_dict = {}
|
|
399
|
-
faces_repeat = np.zeros(node_count*100, dtype = bool)
|
|
400
|
-
cell_face_is_right_handed = np.zeros( len(cells)*6, dtype = bool)
|
|
401
|
-
|
|
402
|
-
for ih,hexa in enumerate(cells):
|
|
403
|
-
faces= [[0,3,2,1], [0,1,5,4], [1,2,6,5], [2,3,7,6], [3,0,4,7], [4,5,6,7]]
|
|
404
|
-
for iq,quad in enumerate(faces):
|
|
405
|
-
face0 = [hexa[x] for x in quad ]
|
|
406
|
-
assert -1 not in face0
|
|
407
|
-
fkey0 = ( x for x in sorted(face0) )
|
|
408
|
-
#
|
|
409
|
-
# keep track of which faces are encountered once vs. more than once
|
|
410
|
-
# faces that are encountered the second time will need to use the reverse handedness
|
|
411
|
-
#
|
|
412
|
-
face_is_repeated = False
|
|
413
|
-
if (fkey0 not in faces_dict):
|
|
414
|
-
faces_dict[fkey0] = len(nodes_per_face)
|
|
415
|
-
nodes_per_face.extend(face0)
|
|
416
|
-
cell_face_is_right_handed[(ih*6 + iq)] = False
|
|
417
|
-
else:
|
|
418
|
-
face_is_repeated = True
|
|
419
|
-
cell_face_is_right_handed[(ih*6 + iq)] = True
|
|
420
|
-
fidx0 = faces_dict.get(fkey0)
|
|
421
|
-
faces_per_cell.append(fidx0/4)
|
|
422
|
-
faces_repeat[int(fidx0/4)] = face_is_repeated
|
|
423
|
-
set_cell_count = int(len(faces_per_cell)/6)
|
|
424
|
-
face_count = int(len(nodes_per_face)/4)
|
|
425
|
-
|
|
426
|
-
node_count=node_count
|
|
427
|
-
face_count=face_count
|
|
428
|
-
cell_count=set_cell_count
|
|
429
|
-
|
|
430
|
-
hexa_uuid = uuid4()
|
|
431
|
-
geom = ro.UnstructuredGridGeometry(
|
|
432
|
-
local_crs=ro.DataObjectReference(
|
|
433
|
-
content_type=f"application/x-resqml+xml;version={schema_version};type={get_data_object_type(crs)}",
|
|
434
|
-
title=crs.citation.title,
|
|
435
|
-
uuid=crs.uuid,
|
|
436
|
-
),
|
|
437
|
-
node_count=node_count,
|
|
438
|
-
face_count=face_count,
|
|
439
|
-
cell_shape=cellshape,
|
|
440
|
-
points=ro.Point3dHdf5Array(
|
|
441
|
-
coordinates=ro.Hdf5Dataset(
|
|
442
|
-
path_in_hdf_file=f"/RESQML/{str(hexa_uuid)}/points",
|
|
443
|
-
hdf_proxy=ro.DataObjectReference(
|
|
444
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
445
|
-
title=epc.citation.title,
|
|
446
|
-
uuid=str(epc.uuid),
|
|
447
|
-
),
|
|
448
|
-
)
|
|
449
|
-
),
|
|
450
|
-
nodes_per_face=ro.ResqmlJaggedArray(
|
|
451
|
-
elements=ro.IntegerHdf5Array(
|
|
452
|
-
null_value=-1,
|
|
453
|
-
values=ro.Hdf5Dataset(
|
|
454
|
-
path_in_hdf_file=f"/RESQML/{str(hexa_uuid)}/nodes_per_face",
|
|
455
|
-
hdf_proxy=ro.DataObjectReference(
|
|
456
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
457
|
-
title=epc.citation.title,
|
|
458
|
-
uuid=str(epc.uuid),
|
|
459
|
-
),
|
|
460
|
-
)
|
|
461
|
-
),
|
|
462
|
-
cumulative_length=ro.IntegerHdf5Array(
|
|
463
|
-
null_value=-1,
|
|
464
|
-
values=ro.Hdf5Dataset(
|
|
465
|
-
path_in_hdf_file=f"/RESQML/{str(hexa_uuid)}/nodes_per_face_cl",
|
|
466
|
-
hdf_proxy=ro.DataObjectReference(
|
|
467
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
468
|
-
title=epc.citation.title,
|
|
469
|
-
uuid=str(epc.uuid),
|
|
470
|
-
),
|
|
471
|
-
)
|
|
472
|
-
),
|
|
473
|
-
),
|
|
474
|
-
faces_per_cell=ro.ResqmlJaggedArray(
|
|
475
|
-
elements=ro.IntegerHdf5Array(
|
|
476
|
-
null_value=-1,
|
|
477
|
-
values=ro.Hdf5Dataset(
|
|
478
|
-
path_in_hdf_file=f"/RESQML/{str(hexa_uuid)}/faces_per_cell",
|
|
479
|
-
hdf_proxy=ro.DataObjectReference(
|
|
480
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
481
|
-
title=epc.citation.title,
|
|
482
|
-
uuid=str(epc.uuid),
|
|
483
|
-
),
|
|
484
|
-
)
|
|
485
|
-
),
|
|
486
|
-
cumulative_length=ro.IntegerHdf5Array(
|
|
487
|
-
null_value=-1,
|
|
488
|
-
values=ro.Hdf5Dataset(
|
|
489
|
-
path_in_hdf_file=f"/RESQML/{str(hexa_uuid)}/faces_per_cell_cl",
|
|
490
|
-
hdf_proxy=ro.DataObjectReference(
|
|
491
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
492
|
-
title=epc.citation.title,
|
|
493
|
-
uuid=str(epc.uuid),
|
|
494
|
-
),
|
|
495
|
-
)
|
|
496
|
-
),
|
|
497
|
-
),
|
|
498
|
-
cell_face_is_right_handed=ro.BooleanHdf5Array(
|
|
499
|
-
values=ro.Hdf5Dataset(
|
|
500
|
-
path_in_hdf_file=f"/RESQML/{str(hexa_uuid)}/cell_face_is_right_handed",
|
|
501
|
-
hdf_proxy=ro.DataObjectReference(
|
|
502
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
503
|
-
title=epc.citation.title,
|
|
504
|
-
uuid=str(epc.uuid),
|
|
505
|
-
),
|
|
506
|
-
)
|
|
507
|
-
)
|
|
508
|
-
)
|
|
509
|
-
|
|
510
|
-
#
|
|
511
|
-
uns = ro.UnstructuredGridRepresentation(
|
|
512
|
-
uuid=str(hexa_uuid),
|
|
513
|
-
schema_version=schema_version,
|
|
514
|
-
# surface_role=resqml_objects.SurfaceRole.MAP,
|
|
515
|
-
citation=create_common_citation(gts_citation_title),
|
|
516
|
-
cell_count=cell_count,
|
|
517
|
-
geometry=geom,
|
|
518
|
-
)
|
|
519
|
-
return uns, crs, epc, timeseries
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
def convert_epc_mesh_to_resqml_mesh(epc_filename: str, title_in: str, projected_epsg: int):
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
title = title_in or "hexamesh"
|
|
526
|
-
|
|
527
|
-
model = rq.Model(epc_filename)
|
|
528
|
-
assert model is not None
|
|
529
|
-
|
|
530
|
-
#
|
|
531
|
-
# read mesh: vertex positions and cell definitions
|
|
532
|
-
#
|
|
533
|
-
hexa_uuid = model.uuid(obj_type='UnstructuredGridRepresentation', title=title_in)
|
|
534
|
-
assert hexa_uuid is not None
|
|
535
|
-
hexa = rug.HexaGrid(model, uuid=hexa_uuid)
|
|
536
|
-
assert hexa is not None
|
|
537
|
-
assert hexa.cell_shape == 'hexahedral'
|
|
538
|
-
hexa.check_hexahedral()
|
|
539
|
-
|
|
540
|
-
ts_uuid = model.uuid(obj_type="TimeSeries")
|
|
541
|
-
# ts_uuid_2 = model.uuid(obj_type='GeologicTimeSeries')
|
|
542
|
-
# logger.debug(f"TS UUIDs: {ts_uuid} {ts_uuid_2}")
|
|
543
|
-
gts = rts.GeologicTimeSeries(model, uuid=ts_uuid)
|
|
544
|
-
logger.debug(f"gts: {gts}")
|
|
545
|
-
timeseries = None
|
|
546
|
-
if (ts_uuid is not None) and (gts is not None):
|
|
547
|
-
ro_timestamps = []
|
|
548
|
-
for i in gts.iter_timestamps(as_string=False):
|
|
549
|
-
ro_timestamps.append(
|
|
550
|
-
ro.Timestamp(
|
|
551
|
-
date_time=XmlDateTime.from_string("0001-01-01T00:00:00.00+00:00"),
|
|
552
|
-
year_offset=int(i),
|
|
553
|
-
)
|
|
554
|
-
)
|
|
555
|
-
logger.info(f"Generating time series with {len(ro_timestamps)} indices, year offsets: {ro_timestamps[0].year_offset} -- {ro_timestamps[-1].year_offset}.")
|
|
556
|
-
timeseries = ro.TimeSeries(
|
|
557
|
-
citation=create_common_citation(str(gts.citation_title)),
|
|
558
|
-
schema_version=schema_version,
|
|
559
|
-
uuid=str(gts.uuid),
|
|
560
|
-
time=ro_timestamps,
|
|
561
|
-
)
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
crs = create_common_crs(title, projected_epsg)
|
|
565
|
-
|
|
566
|
-
epc = ro.EpcExternalPartReference(
|
|
567
|
-
citation=create_common_citation("Hdf Proxy"),
|
|
568
|
-
schema_version=schema_version,
|
|
569
|
-
uuid=str(uuid4()),
|
|
570
|
-
mime_type="application/x-hdf5",
|
|
571
|
-
)
|
|
572
|
-
|
|
573
|
-
cellshape = ro.CellShape.HEXAHEDRAL if (hexa.cell_shape == "hexahedral") else ro.CellShape.TETRAHEDRAL
|
|
574
|
-
|
|
575
|
-
geom = ro.UnstructuredGridGeometry(
|
|
576
|
-
local_crs=ro.DataObjectReference(
|
|
577
|
-
content_type=f"application/x-resqml+xml;version={schema_version};type={get_data_object_type(crs)}",
|
|
578
|
-
title=crs.citation.title,
|
|
579
|
-
uuid=crs.uuid,
|
|
580
|
-
),
|
|
581
|
-
node_count=hexa.node_count or -1,
|
|
582
|
-
face_count=hexa.face_count or -1,
|
|
583
|
-
cell_shape=cellshape,
|
|
584
|
-
points=ro.Point3dHdf5Array(
|
|
585
|
-
coordinates=ro.Hdf5Dataset(
|
|
586
|
-
path_in_hdf_file=f"/RESQML/{str(hexa_uuid)}/points",
|
|
587
|
-
hdf_proxy=ro.DataObjectReference(
|
|
588
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
589
|
-
title=epc.citation.title,
|
|
590
|
-
uuid=str(epc.uuid),
|
|
591
|
-
),
|
|
592
|
-
)
|
|
593
|
-
),
|
|
594
|
-
nodes_per_face=ro.ResqmlJaggedArray(
|
|
595
|
-
elements=ro.IntegerHdf5Array(
|
|
596
|
-
null_value=-1,
|
|
597
|
-
values=ro.Hdf5Dataset(
|
|
598
|
-
path_in_hdf_file=f"/RESQML/{str(hexa_uuid)}/nodes_per_face",
|
|
599
|
-
hdf_proxy=ro.DataObjectReference(
|
|
600
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
601
|
-
title=epc.citation.title,
|
|
602
|
-
uuid=str(epc.uuid),
|
|
603
|
-
),
|
|
604
|
-
)
|
|
605
|
-
),
|
|
606
|
-
cumulative_length=ro.IntegerHdf5Array(
|
|
607
|
-
null_value=-1,
|
|
608
|
-
values=ro.Hdf5Dataset(
|
|
609
|
-
path_in_hdf_file=f"/RESQML/{str(hexa_uuid)}/nodes_per_face_cl",
|
|
610
|
-
hdf_proxy=ro.DataObjectReference(
|
|
611
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
612
|
-
title=epc.citation.title,
|
|
613
|
-
uuid=str(epc.uuid),
|
|
614
|
-
),
|
|
615
|
-
)
|
|
616
|
-
),
|
|
617
|
-
),
|
|
618
|
-
faces_per_cell=ro.ResqmlJaggedArray(
|
|
619
|
-
elements=ro.IntegerHdf5Array(
|
|
620
|
-
null_value=-1,
|
|
621
|
-
values=ro.Hdf5Dataset(
|
|
622
|
-
path_in_hdf_file=f"/RESQML/{str(hexa_uuid)}/faces_per_cell",
|
|
623
|
-
hdf_proxy=ro.DataObjectReference(
|
|
624
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
625
|
-
title=epc.citation.title,
|
|
626
|
-
uuid=str(epc.uuid),
|
|
627
|
-
),
|
|
628
|
-
)
|
|
629
|
-
),
|
|
630
|
-
cumulative_length=ro.IntegerHdf5Array(
|
|
631
|
-
null_value=-1,
|
|
632
|
-
values=ro.Hdf5Dataset(
|
|
633
|
-
path_in_hdf_file=f"/RESQML/{str(hexa_uuid)}/faces_per_cell_cl",
|
|
634
|
-
hdf_proxy=ro.DataObjectReference(
|
|
635
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
636
|
-
title=epc.citation.title,
|
|
637
|
-
uuid=str(epc.uuid),
|
|
638
|
-
),
|
|
639
|
-
)
|
|
640
|
-
),
|
|
641
|
-
),
|
|
642
|
-
cell_face_is_right_handed=ro.BooleanHdf5Array(
|
|
643
|
-
values=ro.Hdf5Dataset(
|
|
644
|
-
path_in_hdf_file=f"/RESQML/{str(hexa_uuid)}/cell_face_is_right_handed",
|
|
645
|
-
hdf_proxy=ro.DataObjectReference(
|
|
646
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
647
|
-
title=epc.citation.title,
|
|
648
|
-
uuid=str(epc.uuid),
|
|
649
|
-
),
|
|
650
|
-
)
|
|
651
|
-
)
|
|
652
|
-
)
|
|
653
|
-
|
|
654
|
-
#
|
|
655
|
-
uns = ro.UnstructuredGridRepresentation(
|
|
656
|
-
uuid=str(hexa.uuid),
|
|
657
|
-
schema_version=schema_version,
|
|
658
|
-
# surface_role=resqml_objects.SurfaceRole.MAP,
|
|
659
|
-
citation=create_common_citation(hexa.title),
|
|
660
|
-
cell_count=hexa.cell_count or -1,
|
|
661
|
-
geometry=geom,
|
|
662
|
-
)
|
|
663
|
-
|
|
664
|
-
return uns, crs, epc, timeseries, hexa
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
def convert_epc_mesh_property_to_resqml_mesh(epc_filename, hexa, prop_title, uns:ro.UnstructuredGridRepresentation, epc:ro.EpcExternalPartReference, timeseries=None, time_indices: list[int] = []):
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
model = rq.Model(epc_filename)
|
|
671
|
-
assert model is not None
|
|
672
|
-
prop_types = ['obj_ContinuousProperty', 'obj_DiscreteProperty', 'obj_CategoricalProperty', 'obj_PointsProperty']
|
|
673
|
-
p = []
|
|
674
|
-
for i in prop_types:
|
|
675
|
-
p1 = model.uuids(title=prop_title, obj_type=i)
|
|
676
|
-
p.extend(p1)
|
|
677
|
-
p_test = rqp.Property(model, uuid=p[0])
|
|
678
|
-
|
|
679
|
-
use_timeseries = isinstance(p_test.time_index(), int)
|
|
680
|
-
if not use_timeseries:
|
|
681
|
-
prop_uuid0 = p[0]
|
|
682
|
-
prop0 = rqp.Property(model, uuid=prop_uuid0)
|
|
683
|
-
else:
|
|
684
|
-
prop_uuids = p
|
|
685
|
-
prop_uuid0 = prop_uuids[time_indices[0]]
|
|
686
|
-
prop0 = rqp.Property(model, uuid=prop_uuid0) # a prop representative of all in the timeseries
|
|
687
|
-
|
|
688
|
-
continuous = prop0.is_continuous()
|
|
689
|
-
|
|
690
|
-
if (prop0.local_property_kind_uuid() is None):
|
|
691
|
-
propertykind0 = None
|
|
692
|
-
else:
|
|
693
|
-
pk = rqp.PropertyKind(model, uuid=prop0.local_property_kind_uuid())
|
|
694
|
-
propertykind0 = ro.PropertyKind(
|
|
695
|
-
schema_version=schema_version,
|
|
696
|
-
citation=create_common_citation(f"{prop_title}"),
|
|
697
|
-
naming_system="urn:resqml:bp.com:resqpy",
|
|
698
|
-
is_abstract=False,
|
|
699
|
-
representative_uom=uom_for_prop_title(prop_title),
|
|
700
|
-
parent_property_kind=ro.StandardPropertyKind(
|
|
701
|
-
kind=ro.ResqmlPropertyKind.CONTINUOUS if continuous else ro.ResqmlPropertyKind.DISCRETE
|
|
702
|
-
),
|
|
703
|
-
uuid=str(pk.uuid),
|
|
704
|
-
)
|
|
705
|
-
|
|
706
|
-
cprop0s, props = [], []
|
|
707
|
-
|
|
708
|
-
for i in range(len(time_indices) if use_timeseries else 1):
|
|
709
|
-
if (not use_timeseries):
|
|
710
|
-
prop_uuid = prop_uuid0
|
|
711
|
-
prop = prop0
|
|
712
|
-
else:
|
|
713
|
-
prop_uuid = prop_uuids[time_indices[i]]
|
|
714
|
-
prop = rqp.Property(model, uuid=prop_uuid)
|
|
715
|
-
|
|
716
|
-
# def create_resqml_property(prop_title, continuous, indexable_element, uns, epc, min_val=0.0, max_val=1.0, timeseries=None, time_index=-1):
|
|
717
|
-
|
|
718
|
-
pov = ro.PatchOfValues(
|
|
719
|
-
values=ro.DoubleHdf5Array(
|
|
720
|
-
values=ro.Hdf5Dataset(
|
|
721
|
-
path_in_hdf_file=f"/RESQML/{str(prop_uuid)}/values",
|
|
722
|
-
hdf_proxy=ro.DataObjectReference(
|
|
723
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
724
|
-
title=epc.citation.title,
|
|
725
|
-
uuid=str(epc.uuid),
|
|
726
|
-
),
|
|
727
|
-
)
|
|
728
|
-
) if continuous else
|
|
729
|
-
ro.IntegerHdf5Array(
|
|
730
|
-
values=ro.Hdf5Dataset(
|
|
731
|
-
path_in_hdf_file=f"/RESQML/{str(prop_uuid)}/values",
|
|
732
|
-
hdf_proxy=ro.DataObjectReference(
|
|
733
|
-
content_type=f"application/x-eml+xml;version={schema_version};type={get_data_object_type(epc)}",
|
|
734
|
-
title=epc.citation.title,
|
|
735
|
-
uuid=str(epc.uuid),
|
|
736
|
-
),
|
|
737
|
-
),
|
|
738
|
-
null_value=int(1e30),
|
|
739
|
-
)
|
|
740
|
-
)
|
|
741
|
-
|
|
742
|
-
timeindex_ref = None
|
|
743
|
-
if use_timeseries:
|
|
744
|
-
time_index = time_indices[i]
|
|
745
|
-
timeindex_ref = ro.TimeIndex(
|
|
746
|
-
index=time_index,
|
|
747
|
-
time_series=ro.DataObjectReference(
|
|
748
|
-
content_type=f"application/x-resqml+xml;version={schema_version};type={get_data_object_type(timeseries)}",
|
|
749
|
-
title=timeseries.citation.title,
|
|
750
|
-
uuid=timeseries.uuid,
|
|
751
|
-
)
|
|
752
|
-
)
|
|
753
|
-
|
|
754
|
-
r_uom = ro.ResqmlUom(value=uom_for_prop_title(prop_title)) if (prop.uom() is None) else prop.uom()
|
|
755
|
-
|
|
756
|
-
if (continuous):
|
|
757
|
-
cprop0 = ro.ContinuousProperty(
|
|
758
|
-
schema_version=schema_version,
|
|
759
|
-
citation=create_common_citation(f"{prop_title}"),
|
|
760
|
-
uuid=str(prop.uuid),
|
|
761
|
-
uom=r_uom,
|
|
762
|
-
count=1,
|
|
763
|
-
indexable_element=prop.indexable_element(),
|
|
764
|
-
supporting_representation=ro.DataObjectReference(
|
|
765
|
-
content_type=f"application/x-resqml+xml;version={schema_version};type={get_data_object_type(uns)}",
|
|
766
|
-
title=uns.citation.title,
|
|
767
|
-
uuid=uns.uuid,
|
|
768
|
-
),
|
|
769
|
-
property_kind=ro.LocalPropertyKind(
|
|
770
|
-
local_property_kind=ro.DataObjectReference(
|
|
771
|
-
content_type=f"application/x-resqml+xml;version={schema_version};type={get_data_object_type(propertykind0)}",
|
|
772
|
-
title=propertykind0.citation.title,
|
|
773
|
-
uuid=propertykind0.uuid,
|
|
774
|
-
)
|
|
775
|
-
) if (propertykind0 is not None) else ro.StandardPropertyKind(kind=prop.property_kind()),
|
|
776
|
-
minimum_value=[prop.minimum_value() or 0.0],
|
|
777
|
-
maximum_value=[prop.maximum_value() or 1.0],
|
|
778
|
-
facet=[ro.PropertyKindFacet(
|
|
779
|
-
facet=ro.Facet.WHAT,
|
|
780
|
-
value=prop_title, # prop.facet(),
|
|
781
|
-
)],
|
|
782
|
-
patch_of_values=[pov],
|
|
783
|
-
time_index=timeindex_ref,
|
|
784
|
-
)
|
|
785
|
-
else:
|
|
786
|
-
cprop0 = ro.DiscreteProperty(
|
|
787
|
-
schema_version=schema_version,
|
|
788
|
-
citation=create_common_citation(f"{prop_title}"),
|
|
789
|
-
uuid=str(prop.uuid),
|
|
790
|
-
# uom = prop.uom(),
|
|
791
|
-
count=1,
|
|
792
|
-
indexable_element=prop.indexable_element(),
|
|
793
|
-
supporting_representation=ro.DataObjectReference(
|
|
794
|
-
content_type=f"application/x-resqml+xml;version={schema_version};type={get_data_object_type(uns)}",
|
|
795
|
-
title=uns.citation.title,
|
|
796
|
-
uuid=uns.uuid,
|
|
797
|
-
),
|
|
798
|
-
property_kind=ro.LocalPropertyKind(
|
|
799
|
-
local_property_kind=ro.DataObjectReference(
|
|
800
|
-
content_type=f"application/x-resqml+xml;version={schema_version};type={get_data_object_type(propertykind0)}",
|
|
801
|
-
title=propertykind0.citation.title,
|
|
802
|
-
uuid=propertykind0.uuid,
|
|
803
|
-
)
|
|
804
|
-
) if (propertykind0 is not None) else ro.StandardPropertyKind(kind=prop.property_kind()),
|
|
805
|
-
minimum_value=[int(prop.minimum_value() or 0)],
|
|
806
|
-
maximum_value=[int(prop.maximum_value() or 1)],
|
|
807
|
-
facet=[ro.PropertyKindFacet(
|
|
808
|
-
facet=ro.Facet.WHAT,
|
|
809
|
-
value=prop_title, # prop.facet(),
|
|
810
|
-
)],
|
|
811
|
-
patch_of_values=[pov],
|
|
812
|
-
time_index=timeindex_ref,
|
|
813
|
-
)
|
|
814
|
-
cprop0s.append(cprop0)
|
|
815
|
-
props.append(prop)
|
|
816
|
-
|
|
817
|
-
return cprop0s, props, propertykind0
|