cmflow 0.3.3__tar.gz → 0.3.3.dev2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cmflow
3
- Version: 0.3.3
3
+ Version: 0.3.3.dev2
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,8 +14,6 @@ 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
19
17
 
20
18
  START = [time.time()]
21
19
  def print_wall_time(msg, loop_stop=False, total=False):
@@ -27,34 +25,6 @@ def print_wall_time(msg, loop_stop=False, total=False):
27
25
  if loop_stop:
28
26
  START.append(t)
29
27
 
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
28
  class LeapfrogGM(object):
59
29
  def __init__(self, geometry=''):
60
30
  super(LeapfrogGM, self).__init__()
@@ -99,170 +69,6 @@ class LeapfrogGM(object):
99
69
  print('%8s Lithology found.' % len(self.litholist))
100
70
  print('%8s Blocks allocated.' % len(self.blocklitho))
101
71
 
102
- def parse_lithology_names(self):
103
- """ parse Leapfrog's Lithology names and determine the faults and pure
104
- lithos
105
-
106
- Returns a list of lithology and a list of faults.
107
-
108
- NOTE Leapfrog remove spaces from fault names, and then truncated into
109
- 7 characters. (Lithology names remain.)
110
-
111
- Observation:
112
- - a string can have two parts separated by ', '
113
- - if there is only a single part, then it can be pure litho OR faults
114
- - if there are two parts, then they are faults and pure litho
115
- - faults are one or more faults joined by '+'
116
- - fault names are shorteded by removing spaces
117
- """
118
- if len(self.litholist) == 0:
119
- return
120
-
121
- faults, rocks = [], []
122
- self.litho_fault_combos = []
123
- for lf_litho in self.litholist:
124
- parts = lf_litho.split(', ')
125
- if len(parts) == 1:
126
- if '+' in parts[0]:
127
- fault_parts = parts[0].split('+')
128
- for fault in fault_parts:
129
- if fault not in faults:
130
- faults.append(fault)
131
- self.litho_fault_combos.append(fault_parts)
132
- else:
133
- if parts[0] not in rocks:
134
- rocks.append(parts[0])
135
- self.litho_fault_combos.append([])
136
-
137
- elif len(parts) == 2:
138
- fault_parts = parts[0].split('+')
139
- for fault in fault_parts:
140
- if fault not in faults:
141
- faults.append(fault)
142
- self.litho_fault_combos.append(fault_parts)
143
-
144
- if parts[1] not in rocks:
145
- rocks.append(parts[1])
146
- else:
147
- raise Exception("Unexpected Lithology name format: '%s'" % lf_litho)
148
-
149
- for f in faults:
150
- if f in rocks:
151
- raise Exception(f"Fault {f} also found in pure litho list")
152
- for r in rocks:
153
- if r in faults:
154
- raise Exception(f"Pure litho {r} also found in fault list")
155
-
156
- self._lf_rocks = sorted(rocks)
157
- self._lf_faults = sorted(faults)
158
- return self._lf_rocks, self._lf_faults
159
-
160
- def gmf_litho_rocktype_1L(self, litho_codes={}, chars=None, report=False):
161
- """ Generates GMF style rocktype names (length of 1) based on lithologies.
162
- Returns a dict of mapping from Leapfrog lithology name to GMF rocktype name.
163
-
164
- litho_codes is a user-defined dict with keys and values a single char.
165
- The order is important, will affect how the final names are sorted.
166
- If not specified, the names will be sorted *naturally*.
167
- """
168
- if not chars:
169
- chars = string.ascii_uppercase + string.ascii_lowercase + string.digits[1:]
170
-
171
- if litho_codes:
172
- # some checks and remove used characters
173
- for l,c in litho_codes.items():
174
- if len(c) != 1:
175
- raise Exception(f"Lithology code for {l} must be a single character")
176
- if l not in self._lf_rocks:
177
- raise Exception(f"Lithology {l} not found in Leapfrog lithologies list")
178
- chars = chars.replace(c, '')
179
- # tolerate if user only specifies some of the lithologies
180
- remaining_lf_rocks = [r for r in self._lf_rocks if r not in litho_codes]
181
- new_litho_codes, chars = assign_chars(sorted(remaining_lf_rocks, key=natural_key), chars)
182
- litho_codes.update(new_litho_codes)
183
- else:
184
- litho_codes, chars = assign_chars(sorted(self._lf_rocks, key=natural_key), chars)
185
-
186
- if report:
187
- print(f"{len(self.litholist):>8} Leapfrog LithoCode\n"
188
- f"{len(litho_codes):>8} Lithology\n")
189
- print()
190
- for name, code in litho_codes.items():
191
- print(f"{code} -> {name}")
192
- print()
193
- print(litho_codes)
194
-
195
- return litho_codes
196
-
197
- def gmf_fault_rocktype_2L(self, fault_codes={}, chars=None, report=False):
198
- """ Generates GMF style rocktype names (length of 2) based on faults.
199
- Returns a dict of mapping from tuple of faults to GMF rocktype name.
200
-
201
- fault_code is a user-defined dict with keys and values a single char.
202
- The order is important, will affect how the final names are sorted.
203
- If not specified, the names will be sorted *naturally*.
204
-
205
- GMF uses:
206
- - 2nd char (3rd char is zero) single fault
207
- - 3rd char (combined with 2nd char as unique combination of faults)
208
- """
209
- if not chars:
210
- chars = string.ascii_uppercase + string.ascii_lowercase
211
- chars += string.digits[1:] # take out zero because it means no fault
212
-
213
- if fault_codes:
214
- # some checks and remove used characters
215
- for f,c in fault_codes.items():
216
- if len(c) != 1:
217
- raise Exception(f"Fault code for {f} must be a single character")
218
- if f not in fault_codes:
219
- raise Exception(f"Fault {f} not found in Leapfrog faults list")
220
- chars = chars.replace(c, '')
221
- # tolerate if user only specifies some of the faults
222
- remaining_lf_faults = [f for f in self._lf_faults if f not in fault_codes]
223
- new_fault_codes, chars = assign_chars(sorted(remaining_lf_faults, key=natural_key), chars=chars)
224
- fault_codes.update(new_fault_codes)
225
- else:
226
- fault_codes, chars = assign_chars(sorted(self._lf_faults, key=natural_key), chars=chars)
227
-
228
- fault_rank = {name: idx for idx, name in enumerate(fault_codes)}
229
-
230
- # sort combinations: by num of faults, then by ranking of 1st fault, then 2nd...
231
- def combo_id(items):
232
- # id (a tuple of faults) is also ordered by rank
233
- return tuple(sorted(items, key=lambda x: fault_rank.get(x, 0)))
234
- combinations = list({combo_id(c) for c in self.litho_fault_combos if c})
235
- combinations = sorted(combinations, key=lambda x: (len(x), tuple(fault_rank[y] for y in x)))
236
- if report:
237
- print(f"{len(self.litho_fault_combos):>8} Leapfrog LithoCode\n"
238
- f"{len(self._lf_faults):>8} Faults\n"
239
- f"{len(combinations):>8} Fault combinations\n")
240
-
241
- final_codes = {}
242
- for ci,fid in enumerate(combinations):
243
- if len(fid) == 1:
244
- # single item ones first -> fault code + '0'
245
- final_codes[fid] = fault_codes[fid[0]] + '0'
246
- elif len(fid) == 2:
247
- # two items combo -> first + second
248
- final_codes[fid] = ''
249
- for fault in fid:
250
- final_codes[fid] += fault_codes[fault]
251
- else:
252
- # three or more -> first fault code + nunique code
253
- if not chars:
254
- need_more = len(combinations) - ci
255
- raise Exception(f"Not enough characters for remaining fault combinations,"
256
- f" need {need_more} more.")
257
- code = chars[0]
258
- chars = chars[1:]
259
- final_codes[fid] = fault_codes[fid[0]] + code
260
- if report:
261
- # print(f"{fid} -> {final_codes[fid]}")
262
- print(f"{final_codes[fid]} -> ({len(fid)}) {fid}")
263
-
264
- return final_codes
265
-
266
72
  def write(self, filename):
267
73
  with open(filename, 'w') as f:
268
74
  data = {
File without changes
File without changes
File without changes
File without changes
File without changes