cmflow 0.3.3.dev2__tar.gz → 0.4.0__tar.gz
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.
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/PKG-INFO +1 -1
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/conceptual_models.py +348 -6
- cmflow-0.4.0/cmflow/name_combo.py +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/.github/workflows/pypi.yml +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/.github/workflows/unittest.yml +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/.gitignore +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/README.md +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/__init__.py +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/conceptual_models.py.bak +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/geom_3dface_utils.py +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/geom_3dface_utils.py.bak +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/geom_surface_utils.py +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/geom_surface_utils.py.bak +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/t2data_utils.py +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/t2data_utils.py.bak +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/test_conceptual_models.py +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/test_conceptual_models.py.bak +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/test_geom_3dface_utils.py +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/test_geom_3dface_utils_ex.ts +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/cmflow/test_geom_surface_utils.py +0 -0
- {cmflow-0.3.3.dev2 → cmflow-0.4.0}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cmflow
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Building TOUGH2/Waiwera models from layers of conceptual models
|
|
5
5
|
Project-URL: Homepage, https://github.com/cyeh015/cmflow
|
|
6
6
|
Project-URL: Source Code, https://github.com/cyeh015/cmflow
|
|
@@ -14,6 +14,8 @@ from cmflow.geom_surface_utils import geo_column_polygon
|
|
|
14
14
|
|
|
15
15
|
import json
|
|
16
16
|
import time
|
|
17
|
+
import string
|
|
18
|
+
import re
|
|
17
19
|
|
|
18
20
|
START = [time.time()]
|
|
19
21
|
def print_wall_time(msg, loop_stop=False, total=False):
|
|
@@ -25,14 +27,101 @@ def print_wall_time(msg, loop_stop=False, total=False):
|
|
|
25
27
|
if loop_stop:
|
|
26
28
|
START.append(t)
|
|
27
29
|
|
|
30
|
+
def assign_chars(names, chars=string.ascii_uppercase):
|
|
31
|
+
""" Assign unique characters to each name.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
tuple(dict, str)
|
|
36
|
+
A mapping from name to assigned character, and the remaining
|
|
37
|
+
unused characters.
|
|
38
|
+
"""
|
|
39
|
+
if len(names) > len(chars):
|
|
40
|
+
raise Exception("Not enough characters for names")
|
|
41
|
+
name_index = {n: chars[i] for i, n in enumerate(names)}
|
|
42
|
+
remaining_chars = chars[len(names):]
|
|
43
|
+
return name_index, remaining_chars
|
|
44
|
+
|
|
45
|
+
def natural_key(text):
|
|
46
|
+
""" Convert text chunks and number chunks into a tuple of string
|
|
47
|
+
and int, useful for sorting strings naturally. eg.
|
|
48
|
+
|
|
49
|
+
data = ["F24", "F8", "F2", "F10"]
|
|
50
|
+
sorted_data = sorted(data, key=natural_key)
|
|
51
|
+
print(sorted_data) # Output: ['F2', 'F8', 'F10', 'F24']
|
|
52
|
+
"""
|
|
53
|
+
return [
|
|
54
|
+
int(c) if c.isdigit() else c.lower()
|
|
55
|
+
for c in re.split(r'(\d+)', text)
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
class LeapfrogLitho():
|
|
59
|
+
""" Block Lithology Output generated by Leapfrog Energy """
|
|
60
|
+
def __init__(self, litho_code, litho_name):
|
|
61
|
+
self.litho_code = litho_code # Leapfrog LithoCode
|
|
62
|
+
self.litho_name = litho_name # Leapfrog Lithology, exported name
|
|
63
|
+
# as parsed from .litho_name
|
|
64
|
+
self.faults = []
|
|
65
|
+
self.rock = None # pure formation/lithology name
|
|
66
|
+
self.parseLithoName()
|
|
67
|
+
|
|
68
|
+
def __repr__(self):
|
|
69
|
+
return f"LeapfrogLitho(code={self.litho_code}, name='{self.litho_name}', faults={self.faults}, rock='{self.rock}')"
|
|
70
|
+
|
|
71
|
+
def parseLithoName(self):
|
|
72
|
+
""" parse Leapfrog's Lithology names and determine the faults and pure
|
|
73
|
+
lithos
|
|
74
|
+
|
|
75
|
+
NOTE Leapfrog remove spaces from fault names, and then truncated into
|
|
76
|
+
7 characters, not counting other non-ASCII characters. Lithology
|
|
77
|
+
names remain as they are in the Leapfrog project.
|
|
78
|
+
|
|
79
|
+
Observation:
|
|
80
|
+
- a string can have two parts separated by ', '
|
|
81
|
+
- if there is only a single part, then is pure litho (to be confirmed!?)
|
|
82
|
+
- if there are two parts, then they are faults and pure litho
|
|
83
|
+
- faults are one or more faults joined by '+'
|
|
84
|
+
- fault names are shorteded by removing spaces
|
|
85
|
+
"""
|
|
86
|
+
parts = self.litho_name.split(', ')
|
|
87
|
+
if len(parts) == 1:
|
|
88
|
+
# As of 9 Aug 2026, I have never found any single part litho_name
|
|
89
|
+
# that contains a single fault by itself. It is always:
|
|
90
|
+
# - a pure rock lithi OR
|
|
91
|
+
# - multiple faults joined by '+'
|
|
92
|
+
if '+' in parts[0]:
|
|
93
|
+
self.faults = parts[0].split('+')
|
|
94
|
+
else:
|
|
95
|
+
self.rock = parts[0]
|
|
96
|
+
elif len(parts) == 2:
|
|
97
|
+
self.faults = parts[0].split('+')
|
|
98
|
+
self.rock = parts[1]
|
|
99
|
+
else:
|
|
100
|
+
raise Exception("Unexpected Lithology name format: '%s'" % lf_litho)
|
|
101
|
+
|
|
102
|
+
|
|
28
103
|
class LeapfrogGM(object):
|
|
104
|
+
""" Tough2 Block Lithology Output generated by Leapfrog Energy (.csv)
|
|
105
|
+
|
|
106
|
+
.lithocodes is a list of leapfrog litho codes as ordered in the .csv
|
|
107
|
+
.litholist is a list of leapfrog litho names as ordered in the .csv
|
|
108
|
+
.blocklist is a dict of blocks with value of index for accessing .litholist
|
|
109
|
+
|
|
110
|
+
"""
|
|
29
111
|
def __init__(self, geometry=''):
|
|
30
112
|
super(LeapfrogGM, self).__init__()
|
|
113
|
+
self.lithocodes = []
|
|
31
114
|
self.litholist = []
|
|
32
115
|
self.blocklitho = {}
|
|
33
116
|
self.import_from = '' # optional, if imported from leapfrog
|
|
34
117
|
self.geometry = geometry # optional, matching mulgrid geometry file
|
|
35
118
|
|
|
119
|
+
# the following provides parsed info of the Leapfrog GM
|
|
120
|
+
self.lf_lithos = [] # list of LeapfrogLitho objects (ordered as litholist)
|
|
121
|
+
self.lf_block = {} # access LeapfrogLitho objects by block name
|
|
122
|
+
self.rocks = [] # sorted pure lithos found
|
|
123
|
+
self.faults = [] # sorted faults found
|
|
124
|
+
|
|
36
125
|
def import_leapfrog_csv(self, filename, report=False):
|
|
37
126
|
""" load geology info from Leapfrog's 'Generate rock types' feature. The
|
|
38
127
|
.csv file from Leapfrog usually starts with a table of 'LithoCode,Lithology'
|
|
@@ -41,9 +130,11 @@ class LeapfrogGM(object):
|
|
|
41
130
|
import csv
|
|
42
131
|
f = open(filename,'r')
|
|
43
132
|
allrows = csv.reader(f)
|
|
44
|
-
|
|
133
|
+
self.lithocodes = []
|
|
134
|
+
self.litholist = []
|
|
45
135
|
self.blocklitho = {}
|
|
46
136
|
# TODO: this file reading is ugly, need work
|
|
137
|
+
code_index = {}
|
|
47
138
|
read_litho_name, read_block_litho = False, False
|
|
48
139
|
for row in allrows:
|
|
49
140
|
if len(row) == 1: continue
|
|
@@ -57,17 +148,263 @@ class LeapfrogGM(object):
|
|
|
57
148
|
read_block_litho = True
|
|
58
149
|
continue
|
|
59
150
|
if read_litho_name:
|
|
60
|
-
if
|
|
151
|
+
if row[0] != '#': raise Exception
|
|
152
|
+
self.lithocodes.append(row[1])
|
|
61
153
|
self.litholist.append(row[2])
|
|
62
|
-
|
|
154
|
+
code_index[int(row[1])] = len(self.litholist)-1
|
|
63
155
|
if read_block_litho:
|
|
64
156
|
if len(row) != 2: break
|
|
65
|
-
self.blocklitho[row[0]] =
|
|
157
|
+
self.blocklitho[row[0]] = code_index[int(row[1])]
|
|
66
158
|
f.close()
|
|
67
159
|
self.import_from = filename
|
|
160
|
+
|
|
161
|
+
self.parse_lithology()
|
|
162
|
+
|
|
163
|
+
if report:
|
|
164
|
+
print("\n".join([
|
|
165
|
+
f"Loaded from file: {self.import_from}:",
|
|
166
|
+
f"{len(self.lf_lithos):>8} Leapfrog LithoCode",
|
|
167
|
+
f"{len(self.lf_block):>8} Blocks allocated",
|
|
168
|
+
f"{len(self.lf_faults):>8} Faults found",
|
|
169
|
+
f"{len(self.lf_rocks):>8} Pure rocks (lithos) found",
|
|
170
|
+
]) + "\n")
|
|
171
|
+
|
|
172
|
+
def parse_lithology(self):
|
|
173
|
+
""" parse Leapfrog's Lithology names and determine the faults and pure
|
|
174
|
+
lithos
|
|
175
|
+
|
|
176
|
+
Returns a list of lithology and a list of faults.
|
|
177
|
+
|
|
178
|
+
"""
|
|
179
|
+
self.lf_lithos = [] # list of LeapfrogLitho objects (ordered as litholist)
|
|
180
|
+
self.lf_block = {} # access LeapfrogLitho objects by block name
|
|
181
|
+
for code, name in zip(self.lithocodes, self.litholist):
|
|
182
|
+
lf_litho = LeapfrogLitho(code, name)
|
|
183
|
+
self.lf_lithos.append(lf_litho)
|
|
184
|
+
for block, idx in self.blocklitho.items():
|
|
185
|
+
self.lf_block[block] = self.lf_lithos[idx]
|
|
186
|
+
|
|
187
|
+
# compile lists of faults and pure (litho) rocks
|
|
188
|
+
rocks, faults = set(), set()
|
|
189
|
+
for litho in self.lf_lithos:
|
|
190
|
+
if litho.rock is not None:
|
|
191
|
+
rocks.add(litho.rock)
|
|
192
|
+
faults.update(litho.faults)
|
|
193
|
+
self.lf_rocks = sorted(rocks, key=natural_key)
|
|
194
|
+
self.lf_faults = sorted(faults, key=natural_key)
|
|
195
|
+
|
|
196
|
+
for f in self.lf_faults:
|
|
197
|
+
if f in self.lf_rocks:
|
|
198
|
+
raise Exception(f"Fault {f} also found in pure litho list")
|
|
199
|
+
for r in self.lf_rocks:
|
|
200
|
+
if r in self.lf_faults:
|
|
201
|
+
raise Exception(f"Pure litho {r} also found in fault list")
|
|
202
|
+
|
|
203
|
+
return self.lf_rocks, self.lf_faults
|
|
204
|
+
|
|
205
|
+
def gmf_litho_rocktype_1L(self, litho_codes={}, chars=None, ignore=None,
|
|
206
|
+
report=False):
|
|
207
|
+
""" Generates GMF style rocktype names (length of 1) based on lithologies.
|
|
208
|
+
Returns a dict of mapping from GMF rocktype name to Leapfrog lithology(s).
|
|
209
|
+
|
|
210
|
+
litho_codes is a user-defined dict with keys and values a single char.
|
|
211
|
+
The order is important, will affect how the final names are sorted.
|
|
212
|
+
If not specified, the names will be sorted *naturally*. Multiple litho
|
|
213
|
+
names mapping to the same code is allowed (i.e. merging rocktypes).
|
|
214
|
+
|
|
215
|
+
chars is a string containing possible characters to use. NOTE '0' is
|
|
216
|
+
special in GMF and is reserved, it will be removed from user's chars.
|
|
217
|
+
|
|
218
|
+
It is possible to ignore certain lithology by sepcifying names in ignore
|
|
219
|
+
list. Note that they will produce GMF rocktype '0'. There is a default
|
|
220
|
+
set of ignored litho names, if you want to include them, you will have
|
|
221
|
+
to set ignore=[].
|
|
222
|
+
|
|
223
|
+
Each block's rocktype code (length=1) can be accessed by:
|
|
224
|
+
GM.lf_block[block_name].rocktype_litho
|
|
225
|
+
"""
|
|
226
|
+
if chars is None:
|
|
227
|
+
chars = string.ascii_uppercase + string.ascii_lowercase + string.digits[1:]
|
|
228
|
+
else:
|
|
229
|
+
chars.replace('0', '')
|
|
230
|
+
|
|
231
|
+
default_ignore = [None, 'Water', 'Unknown', 'Outside Geological Model']
|
|
232
|
+
if ignore is None:
|
|
233
|
+
ignore = [ig for ig in default_ignore if ig in self.lf_rocks]
|
|
234
|
+
else:
|
|
235
|
+
ignore = [ig for ig in ignore if ig in self.lf_rocks]
|
|
236
|
+
|
|
237
|
+
if litho_codes:
|
|
238
|
+
# some checks and remove used characters
|
|
239
|
+
for l,c in litho_codes.items():
|
|
240
|
+
if l in ignore and c != '0':
|
|
241
|
+
print(f"Warning: ignoring lithology {l}, "
|
|
242
|
+
f"overwrite user-defined litho_codes '{c}' -> '0'")
|
|
243
|
+
if len(c) != 1:
|
|
244
|
+
raise Exception(f"Lithology code for {l} must be a single character")
|
|
245
|
+
if l not in self.lf_rocks:
|
|
246
|
+
raise Exception(f"Lithology {l} not found in Leapfrog lithologies list")
|
|
247
|
+
chars = chars.replace(c, '')
|
|
248
|
+
|
|
249
|
+
for ig in ignore:
|
|
250
|
+
litho_codes[ig] = '0'
|
|
251
|
+
|
|
252
|
+
# deal with all other rocks not specified by user
|
|
253
|
+
remaining_rocks = [r for r in self.lf_rocks if r not in litho_codes]
|
|
254
|
+
new_litho_codes, chars = assign_chars(sorted(remaining_rocks, key=natural_key), chars)
|
|
255
|
+
litho_codes.update(new_litho_codes)
|
|
256
|
+
|
|
257
|
+
# keep as a property of LeapfrogLitho object
|
|
258
|
+
for litho in self.lf_lithos:
|
|
259
|
+
if litho.rock is None:
|
|
260
|
+
litho.rocktype_litho = '0'
|
|
261
|
+
else:
|
|
262
|
+
litho.rocktype_litho = litho_codes[litho.rock]
|
|
263
|
+
|
|
264
|
+
# create reverse index for GMF
|
|
265
|
+
rocktype_litho_index = {}
|
|
266
|
+
for name, rt in litho_codes.items():
|
|
267
|
+
if rt not in rocktype_litho_index:
|
|
268
|
+
rocktype_litho_index[rt] = name
|
|
269
|
+
else:
|
|
270
|
+
rocktype_litho_index[rt] += ", " + name
|
|
271
|
+
# sort the dict for nicer output
|
|
272
|
+
rocktype_litho_index = dict(sorted(rocktype_litho_index.items()))
|
|
273
|
+
|
|
274
|
+
# GMF contents for _dict_rocktype.json
|
|
275
|
+
gmf_litho = {
|
|
276
|
+
"rank": [0],
|
|
277
|
+
"legend": rocktype_litho_index,
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if report:
|
|
281
|
+
print()
|
|
282
|
+
for code, names in rocktype_litho_index.items():
|
|
283
|
+
print(f"{code} -> {names}")
|
|
284
|
+
print()
|
|
285
|
+
|
|
286
|
+
return rocktype_litho_index, gmf_litho
|
|
287
|
+
|
|
288
|
+
def gmf_fault_rocktype_2L(self, fault_codes={}, chars=None, report=False):
|
|
289
|
+
""" Generates GMF style rocktype names (length of 2) based on faults.
|
|
290
|
+
Returns a dict of mapping from GMF rocktype name to tuple of faults.
|
|
291
|
+
|
|
292
|
+
fault_code is a user-defined dict with keys and values a single char.
|
|
293
|
+
The order is important, will affect how the final names are sorted.
|
|
294
|
+
If not specified, the names will be sorted *naturally*.
|
|
295
|
+
|
|
296
|
+
chars is a string containing possible characters to use. NOTE '0' is
|
|
297
|
+
special in GMF and is reserved, it will be removed from user's chars.
|
|
298
|
+
|
|
299
|
+
GMF uses two characters:
|
|
300
|
+
- 2nd char (3rd char is zero) single fault
|
|
301
|
+
- 3rd char (combined with 2nd char as unique combination of faults)
|
|
302
|
+
"""
|
|
303
|
+
if chars is None:
|
|
304
|
+
chars = string.ascii_uppercase + string.ascii_lowercase + string.digits[1:]
|
|
305
|
+
else:
|
|
306
|
+
chars.replace('0', '')
|
|
307
|
+
|
|
308
|
+
if fault_codes:
|
|
309
|
+
# some checks and remove used characters
|
|
310
|
+
for f,c in fault_codes.items():
|
|
311
|
+
if c == '0':
|
|
312
|
+
raise Exception(f"Fault code for {f} cannot be '0'")
|
|
313
|
+
if len(c) != 1:
|
|
314
|
+
raise Exception(f"Fault code for {f} must be a single character")
|
|
315
|
+
if f not in fault_codes:
|
|
316
|
+
raise Exception(f"Fault {f} not found in Leapfrog faults list")
|
|
317
|
+
chars = chars.replace(c, '')
|
|
318
|
+
# tolerate if user only specifies some of the faults
|
|
319
|
+
remaining_lf_faults = [f for f in self.lf_faults if f not in fault_codes]
|
|
320
|
+
new_fault_codes, chars = assign_chars(sorted(remaining_lf_faults, key=natural_key), chars=chars)
|
|
321
|
+
fault_codes.update(new_fault_codes)
|
|
322
|
+
else:
|
|
323
|
+
fault_codes, chars = assign_chars(sorted(self.lf_faults, key=natural_key), chars=chars)
|
|
324
|
+
|
|
325
|
+
fault_rank = {name: idx for idx, name in enumerate(fault_codes)}
|
|
326
|
+
|
|
327
|
+
# sort combinations: by num of faults, then by ranking of 1st fault, then 2nd...
|
|
328
|
+
def combo_id(items):
|
|
329
|
+
# id (a tuple of faults) is also ordered by rank
|
|
330
|
+
return tuple(sorted(items, key=lambda x: fault_rank.get(x, 0)))
|
|
331
|
+
# sorting the faults to ensure unique fault sets
|
|
332
|
+
combinations = list({combo_id(c.faults) for c in self.lf_lithos})
|
|
333
|
+
# sort by combo length, then by rank sequentially in combo
|
|
334
|
+
combinations = sorted(combinations, key=lambda x: (len(x), tuple(fault_rank[y] for y in x)))
|
|
335
|
+
|
|
336
|
+
final_codes = {}
|
|
337
|
+
for ii,fid in enumerate(combinations):
|
|
338
|
+
if len(fid) == 0:
|
|
339
|
+
final_codes[fid] = '00'
|
|
340
|
+
elif len(fid) == 1:
|
|
341
|
+
# single item ones first -> fault code + '0'
|
|
342
|
+
final_codes[fid] = fault_codes[fid[0]] + '0'
|
|
343
|
+
elif len(fid) == 2:
|
|
344
|
+
# two items combo -> first + second
|
|
345
|
+
final_codes[fid] = ''
|
|
346
|
+
for fault in fid:
|
|
347
|
+
final_codes[fid] += fault_codes[fault]
|
|
348
|
+
else:
|
|
349
|
+
# three or more -> first fault code + nunique code
|
|
350
|
+
if not chars:
|
|
351
|
+
need_more = len(combinations) - ii
|
|
352
|
+
raise Exception(f"Not enough characters for remaining fault combinations,"
|
|
353
|
+
f" need {need_more} more.")
|
|
354
|
+
code = chars[0]
|
|
355
|
+
chars = chars[1:]
|
|
356
|
+
final_codes[fid] = fault_codes[fid[0]] + code
|
|
357
|
+
|
|
358
|
+
for litho in self.lf_lithos:
|
|
359
|
+
fid = combo_id(litho.faults)
|
|
360
|
+
litho.rocktype_fault = final_codes[fid]
|
|
361
|
+
litho.faults_sorted = fid
|
|
362
|
+
|
|
363
|
+
# create reverse index for general use (key has two characters)
|
|
364
|
+
rocktype_fault_index = {}
|
|
365
|
+
for fid, code in final_codes.items():
|
|
366
|
+
if code in rocktype_fault_index:
|
|
367
|
+
raise Exception(f"Should be unique")
|
|
368
|
+
rocktype_fault_index[code] = ", ".join(fid)
|
|
369
|
+
# sort the dict for nicer output
|
|
370
|
+
rocktype_fault_index = dict(sorted(rocktype_fault_index.items()))
|
|
371
|
+
|
|
372
|
+
# GMF contents for _dict_rocktype.json
|
|
373
|
+
rocktype_fault_gmf1, rocktype_fault_gmf2 = {'0':''}, {'0':''}
|
|
374
|
+
for fid, code in final_codes.items():
|
|
375
|
+
if len(fid) == 1:
|
|
376
|
+
rocktype_fault_gmf1[code[0]] = fid[0]
|
|
377
|
+
elif len(fid) >= 2:
|
|
378
|
+
rocktype_fault_gmf2[code[1]] = "Intersection " + ", ".join(fid[1:])
|
|
379
|
+
# sort the dict for nicer output
|
|
380
|
+
rocktype_fault_gmf1 = dict(sorted(rocktype_fault_gmf1.items()))
|
|
381
|
+
rocktype_fault_gmf2 = dict(sorted(rocktype_fault_gmf2.items()))
|
|
382
|
+
gmf_fault = {
|
|
383
|
+
"faults": {
|
|
384
|
+
"rank": [1],
|
|
385
|
+
"lengend": rocktype_fault_gmf1,
|
|
386
|
+
"direction": {f:None for f in rocktype_fault_gmf1.keys()},
|
|
387
|
+
},
|
|
388
|
+
"intersections": {
|
|
389
|
+
"rank": [2],
|
|
390
|
+
"lengend": rocktype_fault_gmf2,
|
|
391
|
+
"direction": {f:None for f in rocktype_fault_index.keys()},
|
|
392
|
+
},
|
|
393
|
+
}
|
|
394
|
+
|
|
68
395
|
if report:
|
|
69
|
-
print(
|
|
70
|
-
|
|
396
|
+
print("\n".join([
|
|
397
|
+
f"Generating GMF fault names:",
|
|
398
|
+
f"{len(self.lf_lithos):>8} Leapfrog LithoCode",
|
|
399
|
+
f"{len(self.lf_faults):>8} Faults found",
|
|
400
|
+
f"{len(final_codes):>8} Total unique fault combinations found",
|
|
401
|
+
f"{len([fid for fid in final_codes.keys() if len(fid)==1]):>8} Single-fault combinations found",
|
|
402
|
+
f"{len([fid for fid in final_codes.keys() if len(fid)==2]):>8} Fault intersections found with 2 faults",
|
|
403
|
+
f"{len([fid for fid in final_codes.keys() if len(fid)>=3]):>8} Fault intersections found with 3 or more faults",
|
|
404
|
+
f"{max([len(fid) for fid in final_codes.keys()]):>8} Maximum number of fault intersect in one block",
|
|
405
|
+
]) + "\n")
|
|
406
|
+
|
|
407
|
+
return rocktype_fault_index, gmf_fault
|
|
71
408
|
|
|
72
409
|
def write(self, filename):
|
|
73
410
|
with open(filename, 'w') as f:
|
|
@@ -80,6 +417,8 @@ class LeapfrogGM(object):
|
|
|
80
417
|
"geometry" : self.geometry,
|
|
81
418
|
"zones": self.litholist,
|
|
82
419
|
"blocks": self.blocklitho,
|
|
420
|
+
# Non standard field, for LeapfrogGM only
|
|
421
|
+
"import_litho_codes": self.lithocodes,
|
|
83
422
|
}
|
|
84
423
|
json.dump(data, f, indent=4, sort_keys=True)
|
|
85
424
|
|
|
@@ -90,6 +429,9 @@ class LeapfrogGM(object):
|
|
|
90
429
|
self.geometry = data['geometry']
|
|
91
430
|
self.litholist = data['zones']
|
|
92
431
|
self.blocklitho = data['blocks']
|
|
432
|
+
# Non standard field, for LeapfrogGM only
|
|
433
|
+
self.lithocodes = data["import_litho_codes"]
|
|
434
|
+
self.parse_lithology()
|
|
93
435
|
|
|
94
436
|
class CM(object):
|
|
95
437
|
def populate_model(self, np_dtype):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|