resqpy 4.5.0__py3-none-any.whl → 4.6.3__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -24,7 +24,7 @@ import resqpy.olio.vector_utilities as vec
24
24
  # note: resqpy.grid_surface._grid_surface_cuda will be imported by the find_faces_to_represent_surface() function if needed
25
25
 
26
26
 
27
- def find_faces_to_represent_surface_staffa(grid, surface, name, feature_type = 'fault', progress_fn = None):
27
+ def find_faces_to_represent_surface_staffa(grid, surface, name, feature_type = "fault", progress_fn = None):
28
28
  """Returns a grid connection set containing those cell faces which are deemed to represent the surface.
29
29
 
30
30
  note:
@@ -92,11 +92,11 @@ def find_faces_to_represent_surface_staffa(grid, surface, name, feature_type = '
92
92
  # log.debug('finding surface triangle boxes')
93
93
  t, p = surface.triangles_and_points()
94
94
  if not bu.matching_uuids(grid.crs_uuid, surface.crs_uuid):
95
- log.debug('converting from surface crs to grid crs')
95
+ log.debug("converting from surface crs to grid crs")
96
96
  s_crs = rqc.Crs(surface.model, uuid = surface.crs_uuid)
97
97
  s_crs.convert_array_to(grid.crs, p)
98
98
  triangles = p[t]
99
- assert triangles.size > 0, 'no triangles in surface'
99
+ assert triangles.size > 0, "no triangles in surface"
100
100
  triangle_boxes = np.empty((triangles.shape[0], 2, 3))
101
101
  triangle_boxes[:, 0, :] = np.amin(triangles, axis = 1)
102
102
  triangle_boxes[:, 1, :] = np.amax(triangles, axis = 1)
@@ -120,26 +120,32 @@ def find_faces_to_represent_surface_staffa(grid, surface, name, feature_type = '
120
120
  progress_fn(progress_base + progress_batch * (float(j) / float(grid.nj)))
121
121
  for i in range(grid.ni):
122
122
  if column_k_vector_boxes is not None and bx.boxes_overlap(batch_box, column_k_vector_boxes[j, i]):
123
- full_intersects = meet.line_set_triangles_intersects(centre_points[:-1, j, i],
124
- k_vectors[:, j, i],
125
- triangles[ti_base:ti_end],
126
- line_segment = True)
123
+ full_intersects = meet.line_set_triangles_intersects(
124
+ centre_points[:-1, j, i],
125
+ k_vectors[:, j, i],
126
+ triangles[ti_base:ti_end],
127
+ line_segment = True,
128
+ )
127
129
  distilled_intersects, _, _ = meet.distilled_intersects(full_intersects)
128
130
  k_faces[distilled_intersects, j, i] = True
129
- if j < grid.nj - 1 and column_j_vector_boxes is not None and bx.boxes_overlap(
130
- batch_box, column_j_vector_boxes[j, i]):
131
- full_intersects = meet.line_set_triangles_intersects(centre_points[:, j, i],
132
- j_vectors[:, j, i],
133
- triangles[ti_base:ti_end],
134
- line_segment = True)
131
+ if (j < grid.nj - 1 and column_j_vector_boxes is not None and
132
+ bx.boxes_overlap(batch_box, column_j_vector_boxes[j, i])):
133
+ full_intersects = meet.line_set_triangles_intersects(
134
+ centre_points[:, j, i],
135
+ j_vectors[:, j, i],
136
+ triangles[ti_base:ti_end],
137
+ line_segment = True,
138
+ )
135
139
  distilled_intersects, _, _ = meet.distilled_intersects(full_intersects)
136
140
  j_faces[distilled_intersects, j, i] = True
137
- if i < grid.ni - 1 and column_i_vector_boxes is not None and bx.boxes_overlap(
138
- batch_box, column_i_vector_boxes[j, i]):
139
- full_intersects = meet.line_set_triangles_intersects(centre_points[:, j, i],
140
- i_vectors[:, j, i],
141
- triangles[ti_base:ti_end],
142
- line_segment = True)
141
+ if (i < grid.ni - 1 and column_i_vector_boxes is not None and
142
+ bx.boxes_overlap(batch_box, column_i_vector_boxes[j, i])):
143
+ full_intersects = meet.line_set_triangles_intersects(
144
+ centre_points[:, j, i],
145
+ i_vectors[:, j, i],
146
+ triangles[ti_base:ti_end],
147
+ line_segment = True,
148
+ )
143
149
  distilled_intersects, _, _ = meet.distilled_intersects(full_intersects)
144
150
  i_faces[distilled_intersects, j, i] = True
145
151
  ti_base = ti_end
@@ -152,14 +158,16 @@ def find_faces_to_represent_surface_staffa(grid, surface, name, feature_type = '
152
158
  # log.debug('face counts: K: ' + str(np.count_nonzero(k_faces)) +
153
159
  # '; J: ' + str(np.count_nonzero(j_faces)) +
154
160
  # '; I: ' + str(np.count_nonzero(i_faces)))
155
- gcs = rqf.GridConnectionSet(grid.model,
156
- grid = grid,
157
- k_faces = k_faces,
158
- j_faces = j_faces,
159
- i_faces = i_faces,
160
- feature_name = name,
161
- feature_type = feature_type,
162
- create_organizing_objects_where_needed = True)
161
+ gcs = rqf.GridConnectionSet(
162
+ grid.model,
163
+ grid = grid,
164
+ k_faces = k_faces,
165
+ j_faces = j_faces,
166
+ i_faces = i_faces,
167
+ feature_name = name,
168
+ feature_type = feature_type,
169
+ create_organizing_objects_where_needed = True,
170
+ )
163
171
 
164
172
  if progress_fn is not None:
165
173
  progress_fn(1.0)
@@ -167,16 +175,18 @@ def find_faces_to_represent_surface_staffa(grid, surface, name, feature_type = '
167
175
  return gcs
168
176
 
169
177
 
170
- def find_faces_to_represent_surface_regular(grid,
171
- surface,
172
- name,
173
- title = None,
174
- centres = None,
175
- agitate = False,
176
- feature_type = 'fault',
177
- progress_fn = None,
178
- consistent_side = False,
179
- return_properties = None):
178
+ def find_faces_to_represent_surface_regular(
179
+ grid,
180
+ surface,
181
+ name,
182
+ title = None,
183
+ centres = None,
184
+ agitate = False,
185
+ feature_type = "fault",
186
+ progress_fn = None,
187
+ consistent_side = False,
188
+ return_properties = None,
189
+ ):
180
190
  """Returns a grid connection set containing those cell faces which are deemed to represent the surface.
181
191
 
182
192
  arguments:
@@ -224,9 +234,9 @@ def find_faces_to_represent_surface_regular(grid,
224
234
  return_normal_vectors = False
225
235
  return_offsets = False
226
236
  if return_properties:
227
- assert all([p in ['offset', 'normal vector'] for p in return_properties])
228
- return_normal_vectors = ('normal vector' in return_properties)
229
- return_offsets = ('offset' in return_properties)
237
+ assert all([p in ["offset", "normal vector"] for p in return_properties])
238
+ return_normal_vectors = "normal vector" in return_properties
239
+ return_offsets = "offset" in return_properties
230
240
 
231
241
  if title is None:
232
242
  title = name
@@ -234,34 +244,38 @@ def find_faces_to_represent_surface_regular(grid,
234
244
  if progress_fn is not None:
235
245
  progress_fn(0.0)
236
246
 
237
- log.debug(f'intersecting surface {surface.title} with regular grid {grid.title}')
238
- log.debug(f'grid extent kji: {grid.extent_kji}')
247
+ log.debug(f"intersecting surface {surface.title} with regular grid {grid.title}")
248
+ log.debug(f"grid extent kji: {grid.extent_kji}")
239
249
 
240
- grid_dxyz = (grid.block_dxyz_dkji[2, 0], grid.block_dxyz_dkji[1, 1], grid.block_dxyz_dkji[0, 2])
250
+ grid_dxyz = (
251
+ grid.block_dxyz_dkji[2, 0],
252
+ grid.block_dxyz_dkji[1, 1],
253
+ grid.block_dxyz_dkji[0, 2],
254
+ )
241
255
  if centres is None:
242
256
  centres = grid.centre_point(use_origin = True)
243
257
  if consistent_side:
244
- log.debug('making all triangles clockwise')
258
+ log.debug("making all triangles clockwise")
245
259
  # note: following will shuffle order of vertices within t cached in surface
246
260
  surface.make_all_clockwise_xy(reorient = True)
247
261
  t, p = surface.triangles_and_points()
248
- assert t is not None and p is not None, f'surface {surface.title} is empty'
262
+ assert t is not None and p is not None, f"surface {surface.title} is empty"
249
263
  if agitate:
250
264
  p += 1.0e-5 * (np.random.random(p.shape) - 0.5)
251
- log.debug(f'surface: {surface.title}; p0: {p[0]}; crs uuid: {surface.crs_uuid}')
252
- log.debug(f'surface min xyz: {np.min(p, axis = 0)}')
253
- log.debug(f'surface max xyz: {np.max(p, axis = 0)}')
265
+ log.debug(f"surface: {surface.title}; p0: {p[0]}; crs uuid: {surface.crs_uuid}")
266
+ log.debug(f"surface min xyz: {np.min(p, axis = 0)}")
267
+ log.debug(f"surface max xyz: {np.max(p, axis = 0)}")
254
268
  if not bu.matching_uuids(grid.crs_uuid, surface.crs_uuid):
255
- log.debug('converting from surface crs to grid crs')
269
+ log.debug("converting from surface crs to grid crs")
256
270
  s_crs = rqc.Crs(surface.model, uuid = surface.crs_uuid)
257
271
  s_crs.convert_array_to(grid.crs, p)
258
272
  surface.crs_uuid = grid.crs.uuid
259
- log.debug(f'surface: {surface.title}; p0: {p[0]}; crs uuid: {surface.crs_uuid}')
260
- log.debug(f'surface min xyz: {np.min(p, axis = 0)}')
261
- log.debug(f'surface max xyz: {np.max(p, axis = 0)}')
273
+ log.debug(f"surface: {surface.title}; p0: {p[0]}; crs uuid: {surface.crs_uuid}")
274
+ log.debug(f"surface min xyz: {np.min(p, axis = 0)}")
275
+ log.debug(f"surface max xyz: {np.max(p, axis = 0)}")
262
276
 
263
- log.debug(f'centres min xyz: {np.min(centres.reshape((-1, 3)), axis = 0)}')
264
- log.debug(f'centres max xyz: {np.max(centres.reshape((-1, 3)), axis = 0)}')
277
+ log.debug(f"centres min xyz: {np.min(centres.reshape((-1, 3)), axis = 0)}")
278
+ log.debug(f"centres max xyz: {np.max(centres.reshape((-1, 3)), axis = 0)}")
265
279
 
266
280
  t_count = len(t)
267
281
 
@@ -269,23 +283,25 @@ def find_faces_to_represent_surface_regular(grid,
269
283
 
270
284
  # K direction (xy projection)
271
285
  if grid.nk > 1:
272
- log.debug('searching for k faces')
286
+ log.debug("searching for k faces")
273
287
  k_faces = np.zeros((grid.nk - 1, grid.nj, grid.ni), dtype = bool)
274
288
  k_sides = np.zeros((grid.nk - 1, grid.nj, grid.ni), dtype = bool)
275
- k_offsets = np.full((grid.nk - 1, grid.nj, grid.ni), np.nan) if return_offsets else None
276
- k_normals = np.full((grid.nk - 1, grid.nj, grid.ni, 3), np.nan) if return_normal_vectors else None
289
+ k_offsets = (np.full((grid.nk - 1, grid.nj, grid.ni), np.nan) if return_offsets else None)
290
+ k_normals = (np.full((grid.nk - 1, grid.nj, grid.ni, 3), np.nan) if return_normal_vectors else None)
277
291
  k_centres = centres[0, :, :].reshape((-1, 3))
278
- k_hits = vec.points_in_triangles(p, t, k_centres, projection = 'xy', edged = True).reshape(
292
+ k_hits = vec.points_in_triangles(p, t, k_centres, projection = "xy", edged = True).reshape(
279
293
  (t_count, grid.nj, grid.ni))
280
294
  del k_centres
281
295
  if consistent_side:
282
- cwt = (vec.clockwise_triangles(p, t, projection = 'xy') >= 0.0)
296
+ cwt = vec.clockwise_triangles(p, t, projection = "xy") >= 0.0
283
297
  for k_t, k_j, k_i in np.stack(np.where(k_hits), axis = -1):
284
- xyz = meet.line_triangle_intersect(centres[0, k_j, k_i],
285
- centres[-1, k_j, k_i] - centres[0, k_j, k_i],
286
- p[t[k_t]],
287
- line_segment = True,
288
- t_tol = 1.0e-6)
298
+ xyz = meet.line_triangle_intersect(
299
+ centres[0, k_j, k_i],
300
+ centres[-1, k_j, k_i] - centres[0, k_j, k_i],
301
+ p[t[k_t]],
302
+ line_segment = True,
303
+ t_tol = 1.0e-6,
304
+ )
289
305
  if xyz is None: # meeting point is outwith grid
290
306
  continue
291
307
  k_face = int((xyz[2] - centres[0, k_j, k_i, 2]) / grid_dxyz[2])
@@ -299,15 +315,15 @@ def find_faces_to_represent_surface_regular(grid,
299
315
  k_sides[k_face, k_j, k_i] = cwt[k_t]
300
316
  if return_offsets:
301
317
  # compute offset as z diff between xyz and face
302
- k_offsets[k_face, k_j, k_i] = \
303
- xyz[2] - 0.5 * (centres[k_face, k_j, k_i, 2] + centres[k_face + 1, k_j, k_i, 2])
318
+ k_offsets[k_face, k_j,
319
+ k_i] = xyz[2] - 0.5 * (centres[k_face, k_j, k_i, 2] + centres[k_face + 1, k_j, k_i, 2])
304
320
  if return_normal_vectors:
305
321
  k_normals[k_face, k_j, k_i] = vec.triangle_normal_vector(p[t[k_t]])
306
322
  # todo: if consistent side, could deliver information about horizon surface inversion
307
323
  if k_normals[k_face, k_j, k_i, 2] > 0.0:
308
324
  k_normals[k_face, k_j, k_i] = -k_normals[k_face, k_j, k_i] # -ve z hemisphere normal
309
325
  del k_hits
310
- log.debug(f'k face count: {np.count_nonzero(k_faces)}')
326
+ log.debug(f"k face count: {np.count_nonzero(k_faces)}")
311
327
  else:
312
328
  k_faces = None
313
329
  k_sides = None
@@ -318,23 +334,25 @@ def find_faces_to_represent_surface_regular(grid,
318
334
 
319
335
  # J direction (xz projection)
320
336
  if grid.nj > 1:
321
- log.debug('searching for j faces')
337
+ log.debug("searching for j faces")
322
338
  j_faces = np.zeros((grid.nk, grid.nj - 1, grid.ni), dtype = bool)
323
339
  j_sides = np.zeros((grid.nk, grid.nj - 1, grid.ni), dtype = bool)
324
- j_offsets = np.full((grid.nk, grid.nj - 1, grid.ni), np.nan) if return_offsets else None
325
- j_normals = np.full((grid.nk, grid.nj - 1, grid.ni, 3), np.nan) if return_normal_vectors else None
340
+ j_offsets = (np.full((grid.nk, grid.nj - 1, grid.ni), np.nan) if return_offsets else None)
341
+ j_normals = (np.full((grid.nk, grid.nj - 1, grid.ni, 3), np.nan) if return_normal_vectors else None)
326
342
  j_centres = centres[:, 0, :].reshape((-1, 3))
327
- j_hits = vec.points_in_triangles(p, t, j_centres, projection = 'xz', edged = True).reshape(
343
+ j_hits = vec.points_in_triangles(p, t, j_centres, projection = "xz", edged = True).reshape(
328
344
  (t_count, grid.nk, grid.ni))
329
345
  del j_centres
330
346
  if consistent_side:
331
- cwt = (vec.clockwise_triangles(p, t, projection = 'xz') >= 0.0)
347
+ cwt = vec.clockwise_triangles(p, t, projection = "xz") >= 0.0
332
348
  for j_t, j_k, j_i in np.stack(np.where(j_hits), axis = -1):
333
- xyz = meet.line_triangle_intersect(centres[j_k, 0, j_i],
334
- centres[j_k, -1, j_i] - centres[j_k, 0, j_i],
335
- p[t[j_t]],
336
- line_segment = True,
337
- t_tol = 1.0e-6)
349
+ xyz = meet.line_triangle_intersect(
350
+ centres[j_k, 0, j_i],
351
+ centres[j_k, -1, j_i] - centres[j_k, 0, j_i],
352
+ p[t[j_t]],
353
+ line_segment = True,
354
+ t_tol = 1.0e-6,
355
+ )
338
356
  if xyz is None: # meeting point is outwith grid
339
357
  continue
340
358
  j_face = int((xyz[1] - centres[j_k, 0, j_i, 1]) / grid_dxyz[1])
@@ -348,14 +366,14 @@ def find_faces_to_represent_surface_regular(grid,
348
366
  j_sides[j_k, j_face, j_i] = cwt[j_t]
349
367
  if return_offsets:
350
368
  # compute offset as y diff between xyz and face
351
- j_offsets[j_k, j_face, j_i] = \
352
- xyz[1] - 0.5 * (centres[j_k, j_face, j_i, 1] + centres[j_k, j_face + 1, j_i, 1])
369
+ j_offsets[j_k, j_face,
370
+ j_i] = xyz[1] - 0.5 * (centres[j_k, j_face, j_i, 1] + centres[j_k, j_face + 1, j_i, 1])
353
371
  if return_normal_vectors:
354
372
  j_normals[j_k, j_face, j_i] = vec.triangle_normal_vector(p[t[j_t]])
355
373
  if j_normals[j_k, j_face, j_i, 2] > 0.0:
356
374
  j_normals[j_k, j_face, j_i] = -j_normals[j_k, j_face, j_i] # -ve z hemisphere normal
357
375
  del j_hits
358
- log.debug(f'j face count: {np.count_nonzero(j_faces)}')
376
+ log.debug(f"j face count: {np.count_nonzero(j_faces)}")
359
377
  else:
360
378
  j_faces = None
361
379
  j_sides = None
@@ -366,23 +384,25 @@ def find_faces_to_represent_surface_regular(grid,
366
384
 
367
385
  # I direction (yz projection)
368
386
  if grid.ni > 1:
369
- log.debug('searching for i faces')
387
+ log.debug("searching for i faces")
370
388
  i_faces = np.zeros((grid.nk, grid.nj, grid.ni - 1), dtype = bool)
371
389
  i_sides = np.zeros((grid.nk, grid.nj, grid.ni - 1), dtype = bool)
372
- i_offsets = np.full((grid.nk, grid.nj, grid.ni - 1), np.nan) if return_offsets else None
373
- i_normals = np.full((grid.nk, grid.nj, grid.ni - 1, 3), np.nan) if return_normal_vectors else None
390
+ i_offsets = (np.full((grid.nk, grid.nj, grid.ni - 1), np.nan) if return_offsets else None)
391
+ i_normals = (np.full((grid.nk, grid.nj, grid.ni - 1, 3), np.nan) if return_normal_vectors else None)
374
392
  i_centres = centres[:, :, 0].reshape((-1, 3))
375
- i_hits = vec.points_in_triangles(p, t, i_centres, projection = 'yz', edged = True).reshape(
393
+ i_hits = vec.points_in_triangles(p, t, i_centres, projection = "yz", edged = True).reshape(
376
394
  (t_count, grid.nk, grid.nj))
377
395
  del i_centres
378
396
  if consistent_side:
379
- cwt = (vec.clockwise_triangles(p, t, projection = 'yz') >= 0.0)
397
+ cwt = vec.clockwise_triangles(p, t, projection = "yz") >= 0.0
380
398
  for i_t, i_k, i_j in np.stack(np.where(i_hits), axis = -1):
381
- xyz = meet.line_triangle_intersect(centres[i_k, i_j, 0],
382
- centres[i_k, i_j, -1] - centres[i_k, i_j, 0],
383
- p[t[i_t]],
384
- line_segment = True,
385
- t_tol = 1.0e-6)
399
+ xyz = meet.line_triangle_intersect(
400
+ centres[i_k, i_j, 0],
401
+ centres[i_k, i_j, -1] - centres[i_k, i_j, 0],
402
+ p[t[i_t]],
403
+ line_segment = True,
404
+ t_tol = 1.0e-6,
405
+ )
386
406
  if xyz is None: # meeting point is outwith grid
387
407
  continue
388
408
  i_face = int((xyz[0] - centres[i_k, i_j, 0, 0]) / grid_dxyz[0])
@@ -396,14 +416,14 @@ def find_faces_to_represent_surface_regular(grid,
396
416
  i_sides[i_k, i_j, i_face] = cwt[i_t]
397
417
  if return_offsets:
398
418
  # compute offset as x diff between xyz and face
399
- i_offsets[i_k, i_j, i_face] = \
400
- xyz[0] - 0.5 * (centres[i_k, i_j, i_face, 0] + centres[i_k, i_j, i_face + 1, 0])
419
+ i_offsets[i_k, i_j,
420
+ i_face] = xyz[0] - 0.5 * (centres[i_k, i_j, i_face, 0] + centres[i_k, i_j, i_face + 1, 0])
401
421
  if return_normal_vectors:
402
422
  i_normals[i_k, i_j, i_face] = vec.triangle_normal_vector(p[t[i_t]])
403
423
  if i_normals[i_k, i_j, i_face, 2] > 0.0:
404
424
  i_normals[i_k, i_j, i_face] = -i_normals[i_k, i_j, i_face] # -ve z hemisphere normal
405
425
  del i_hits
406
- log.debug(f'i face count: {np.count_nonzero(i_faces)}')
426
+ log.debug(f"i face count: {np.count_nonzero(i_faces)}")
407
427
  else:
408
428
  i_faces = None
409
429
  i_sides = None
@@ -417,36 +437,38 @@ def find_faces_to_represent_surface_regular(grid,
417
437
  j_sides = None
418
438
  i_sides = None
419
439
 
420
- log.debug('converting face sets into grid connection set')
421
- gcs = rqf.GridConnectionSet(grid.model,
422
- grid = grid,
423
- k_faces = k_faces,
424
- j_faces = j_faces,
425
- i_faces = i_faces,
426
- k_sides = k_sides,
427
- j_sides = j_sides,
428
- i_sides = i_sides,
429
- feature_name = name,
430
- feature_type = feature_type,
431
- title = title,
432
- create_organizing_objects_where_needed = True)
440
+ log.debug("converting face sets into grid connection set")
441
+ gcs = rqf.GridConnectionSet(
442
+ grid.model,
443
+ grid = grid,
444
+ k_faces = k_faces,
445
+ j_faces = j_faces,
446
+ i_faces = i_faces,
447
+ k_sides = k_sides,
448
+ j_sides = j_sides,
449
+ i_sides = i_sides,
450
+ feature_name = name,
451
+ feature_type = feature_type,
452
+ title = title,
453
+ create_organizing_objects_where_needed = True,
454
+ )
433
455
 
434
456
  # NB. following assumes faces have been added to gcs in a particular order!
435
457
  if return_offsets:
436
- k_offsets_list = np.empty((0,)) if k_offsets is None else k_offsets[np.where(k_faces)]
437
- j_offsets_list = np.empty((0,)) if j_offsets is None else j_offsets[np.where(j_faces)]
438
- i_offsets_list = np.empty((0,)) if i_offsets is None else i_offsets[np.where(i_faces)]
458
+ k_offsets_list = (np.empty((0,)) if k_offsets is None else k_offsets[np.where(k_faces)])
459
+ j_offsets_list = (np.empty((0,)) if j_offsets is None else j_offsets[np.where(j_faces)])
460
+ i_offsets_list = (np.empty((0,)) if i_offsets is None else i_offsets[np.where(i_faces)])
439
461
  all_offsets = _all_offsets(grid.crs, k_offsets_list, j_offsets_list, i_offsets_list)
440
- log.debug(f'gcs count: {gcs.count}; all offsets shape: {all_offsets.shape}')
462
+ log.debug(f"gcs count: {gcs.count}; all offsets shape: {all_offsets.shape}")
441
463
  assert all_offsets.shape == (gcs.count,)
442
464
 
443
465
  # NB. following assumes faces have been added to gcs in a particular order!
444
466
  if return_normal_vectors:
445
- k_normals_list = np.empty((0, 3)) if k_normals is None else k_normals[np.where(k_faces)]
446
- j_normals_list = np.empty((0, 3)) if j_normals is None else j_normals[np.where(j_faces)]
447
- i_normals_list = np.empty((0, 3)) if i_normals is None else i_normals[np.where(i_faces)]
467
+ k_normals_list = (np.empty((0, 3)) if k_normals is None else k_normals[np.where(k_faces)])
468
+ j_normals_list = (np.empty((0, 3)) if j_normals is None else j_normals[np.where(j_faces)])
469
+ i_normals_list = (np.empty((0, 3)) if i_normals is None else i_normals[np.where(i_faces)])
448
470
  all_normals = np.concatenate((k_normals_list, j_normals_list, i_normals_list), axis = 0)
449
- log.debug(f'gcs count: {gcs.count}; all normals shape: {all_normals.shape}')
471
+ log.debug(f"gcs count: {gcs.count}; all normals shape: {all_normals.shape}")
450
472
  assert all_normals.shape == (gcs.count, 3)
451
473
  if grid.crs.xy_units != grid.crs.z_units:
452
474
  wam.convert_lengths(all_normals[:, 2], grid.crs.z_units, grid.crs.xy_units)
@@ -459,24 +481,26 @@ def find_faces_to_represent_surface_regular(grid,
459
481
  if return_properties:
460
482
  props_dict = {}
461
483
  if return_offsets:
462
- props_dict['offset'] = all_offsets
484
+ props_dict["offset"] = all_offsets
463
485
  if return_normal_vectors:
464
- props_dict['normal vector'] = all_normals
486
+ props_dict["normal vector"] = all_normals
465
487
  return (gcs, props_dict)
466
488
 
467
489
  return gcs
468
490
 
469
491
 
470
- def find_faces_to_represent_surface_regular_optimised(grid,
471
- surface,
472
- name,
473
- title = None,
474
- agitate = False,
475
- feature_type = 'fault',
476
- is_curtain = False,
477
- progress_fn = None,
478
- return_properties = None,
479
- raw_bisector = False):
492
+ def find_faces_to_represent_surface_regular_optimised(
493
+ grid,
494
+ surface,
495
+ name,
496
+ title = None,
497
+ agitate = False,
498
+ feature_type = "fault",
499
+ is_curtain = False,
500
+ progress_fn = None,
501
+ return_properties = None,
502
+ raw_bisector = False,
503
+ ):
480
504
  """Returns a grid connection set containing those cell faces which are deemed to represent the surface.
481
505
 
482
506
  argumants:
@@ -532,15 +556,21 @@ def find_faces_to_represent_surface_regular_optimised(grid,
532
556
  return_flange_bool = False
533
557
  if return_properties:
534
558
  assert all([
535
- p in ['triangle', 'depth', 'offset', 'grid bisector', 'grid shadow', 'flange bool']
536
- for p in return_properties
559
+ p in [
560
+ "triangle",
561
+ "depth",
562
+ "offset",
563
+ "grid bisector",
564
+ "grid shadow",
565
+ "flange bool",
566
+ ] for p in return_properties
537
567
  ])
538
- return_triangles = ('triangle' in return_properties)
539
- return_depths = ('depth' in return_properties)
540
- return_offsets = ('offset' in return_properties)
541
- return_bisector = ('grid bisector' in return_properties)
542
- return_shadow = ('grid shadow' in return_properties)
543
- return_flange_bool = ('flange bool' in return_properties)
568
+ return_triangles = "triangle" in return_properties
569
+ return_depths = "depth" in return_properties
570
+ return_offsets = "offset" in return_properties
571
+ return_bisector = "grid bisector" in return_properties
572
+ return_shadow = "grid shadow" in return_properties
573
+ return_flange_bool = "flange bool" in return_properties
544
574
  if return_flange_bool:
545
575
  return_triangles = True
546
576
 
@@ -550,25 +580,29 @@ def find_faces_to_represent_surface_regular_optimised(grid,
550
580
  if progress_fn is not None:
551
581
  progress_fn(0.0)
552
582
 
553
- log.debug(f'intersecting surface {surface.title} with regular grid {grid.title}')
583
+ log.debug(f"intersecting surface {surface.title} with regular grid {grid.title}")
554
584
  # log.debug(f'grid extent kji: {grid.extent_kji}')
555
585
 
556
- grid_dxyz = (grid.block_dxyz_dkji[2, 0], grid.block_dxyz_dkji[1, 1], grid.block_dxyz_dkji[0, 2])
586
+ grid_dxyz = (
587
+ grid.block_dxyz_dkji[2, 0],
588
+ grid.block_dxyz_dkji[1, 1],
589
+ grid.block_dxyz_dkji[0, 2],
590
+ )
557
591
  triangles, points = surface.triangles_and_points()
558
- assert triangles is not None and points is not None, f'surface {surface.title} is empty'
592
+ assert (triangles is not None and points is not None), f"surface {surface.title} is empty"
559
593
  if agitate:
560
594
  points += 1.0e-5 * (np.random.random(points.shape) - 0.5)
561
- # log.debug(f'surface: {surface.title}; p0: {points[0]}; crs uuid: {surface.crs_uuid}')
595
+ # log.debug(f'surface: {surface.title}; p0: {points[0]}; crs uuid: {surface.crs_uuid}')
562
596
  # log.debug(f'surface min xyz: {np.min(points, axis = 0)}')
563
597
  # log.debug(f'surface max xyz: {np.max(points, axis = 0)}')
564
598
  if not bu.matching_uuids(grid.crs_uuid, surface.crs_uuid):
565
- log.debug('converting from surface crs to grid crs')
599
+ log.debug("converting from surface crs to grid crs")
566
600
  s_crs = rqc.Crs(surface.model, uuid = surface.crs_uuid)
567
601
  s_crs.convert_array_to(grid.crs, points)
568
602
  surface.crs_uuid = grid.crs.uuid
569
603
  # log.debug(f'surface: {surface.title}; p0: {points[0]}; crs uuid: {surface.crs_uuid}')
570
604
  # log.debug(f'surface min xyz: {np.min(points, axis = 0)}')
571
- # log.debug(f'surface max xyz: {np.max(points, axis = 0)}')
605
+ # log.debug(f'surface max xyz: {np.max(points, axis = 0)}')
572
606
 
573
607
  nk = 1 if is_curtain else grid.nk
574
608
  # K direction (xy projection)
@@ -586,11 +620,23 @@ def find_faces_to_represent_surface_regular_optimised(grid,
586
620
  axis = 2
587
621
  index1 = 1
588
622
  index2 = 2
589
- k_faces, k_offsets, k_triangles = \
590
- intersect_numba(axis, index1, index2, k_hits, nk, points,
591
- triangles, grid_dxyz, k_faces,
592
- return_depths, k_depths,
593
- return_offsets, k_offsets, return_triangles, k_triangles)
623
+ k_faces, k_offsets, k_triangles = intersect_numba(
624
+ axis,
625
+ index1,
626
+ index2,
627
+ k_hits,
628
+ nk,
629
+ points,
630
+ triangles,
631
+ grid_dxyz,
632
+ k_faces,
633
+ return_depths,
634
+ k_depths,
635
+ return_offsets,
636
+ k_offsets,
637
+ return_triangles,
638
+ k_triangles,
639
+ )
594
640
  del k_hits
595
641
  del p_xy
596
642
  log.debug(f"k face count: {np.count_nonzero(k_faces)}")
@@ -617,11 +663,23 @@ def find_faces_to_represent_surface_regular_optimised(grid,
617
663
  axis = 1
618
664
  index1 = 0
619
665
  index2 = 2
620
- j_faces, j_offsets, j_triangles = \
621
- intersect_numba(axis, index1, index2, j_hits, grid.nj, points,
622
- triangles, grid_dxyz, j_faces,
623
- return_depths, j_depths,
624
- return_offsets, j_offsets, return_triangles, j_triangles)
666
+ j_faces, j_offsets, j_triangles = intersect_numba(
667
+ axis,
668
+ index1,
669
+ index2,
670
+ j_hits,
671
+ grid.nj,
672
+ points,
673
+ triangles,
674
+ grid_dxyz,
675
+ j_faces,
676
+ return_depths,
677
+ j_depths,
678
+ return_offsets,
679
+ j_offsets,
680
+ return_triangles,
681
+ j_triangles,
682
+ )
625
683
  del j_hits
626
684
  del p_xz
627
685
  if is_curtain and grid.nk > 1: # expand arrays to all layers
@@ -653,11 +711,23 @@ def find_faces_to_represent_surface_regular_optimised(grid,
653
711
  axis = 0
654
712
  index1 = 0
655
713
  index2 = 1
656
- i_faces, i_offsets, i_triangles = \
657
- intersect_numba(axis, index1, index2, i_hits, grid.ni, points,
658
- triangles, grid_dxyz, i_faces,
659
- return_depths, i_depths,
660
- return_offsets, i_offsets, return_triangles, i_triangles)
714
+ i_faces, i_offsets, i_triangles = intersect_numba(
715
+ axis,
716
+ index1,
717
+ index2,
718
+ i_hits,
719
+ grid.ni,
720
+ points,
721
+ triangles,
722
+ grid_dxyz,
723
+ i_faces,
724
+ return_depths,
725
+ i_depths,
726
+ return_offsets,
727
+ i_offsets,
728
+ return_triangles,
729
+ i_triangles,
730
+ )
661
731
  del i_hits
662
732
  del p_yz
663
733
  if is_curtain and grid.nk > 1: # expand arrays to all layers
@@ -696,9 +766,9 @@ def find_faces_to_represent_surface_regular_optimised(grid,
696
766
  # NB. following assumes faces have been added to gcs in a particular order!
697
767
  if return_triangles:
698
768
  # log.debug('preparing triangles array')
699
- k_tri_list = np.empty((0,)) if k_triangles is None else k_triangles[where_true(k_faces)]
700
- j_tri_list = np.empty((0,)) if j_triangles is None else j_triangles[where_true(j_faces)]
701
- i_tri_list = np.empty((0,)) if i_triangles is None else i_triangles[where_true(i_faces)]
769
+ k_tri_list = (np.empty((0,)) if k_triangles is None else k_triangles[_where_true(k_faces)])
770
+ j_tri_list = (np.empty((0,)) if j_triangles is None else j_triangles[_where_true(j_faces)])
771
+ i_tri_list = (np.empty((0,)) if i_triangles is None else i_triangles[_where_true(i_faces)])
702
772
  all_tris = np.concatenate((k_tri_list, j_tri_list, i_tri_list), axis = 0)
703
773
  # log.debug(f'gcs count: {gcs.count}; all triangles shape: {all_tris.shape}')
704
774
  assert all_tris.shape == (gcs.count,)
@@ -706,9 +776,9 @@ def find_faces_to_represent_surface_regular_optimised(grid,
706
776
  # NB. following assumes faces have been added to gcs in a particular order!
707
777
  if return_depths:
708
778
  # log.debug('preparing depths array')
709
- k_depths_list = np.empty((0,)) if k_depths is None else k_depths[where_true(k_faces)]
710
- j_depths_list = np.empty((0,)) if j_depths is None else j_depths[where_true(j_faces)]
711
- i_depths_list = np.empty((0,)) if i_depths is None else i_depths[where_true(i_faces)]
779
+ k_depths_list = (np.empty((0,)) if k_depths is None else k_depths[_where_true(k_faces)])
780
+ j_depths_list = (np.empty((0,)) if j_depths is None else j_depths[_where_true(j_faces)])
781
+ i_depths_list = (np.empty((0,)) if i_depths is None else i_depths[_where_true(i_faces)])
712
782
  all_depths = np.concatenate((k_depths_list, j_depths_list, i_depths_list), axis = 0)
713
783
  # log.debug(f'gcs count: {gcs.count}; all depths shape: {all_depths.shape}')
714
784
  assert all_depths.shape == (gcs.count,)
@@ -716,19 +786,19 @@ def find_faces_to_represent_surface_regular_optimised(grid,
716
786
  # NB. following assumes faces have been added to gcs in a particular order!
717
787
  if return_offsets:
718
788
  # log.debug('preparing offsets array')
719
- k_offsets_list = np.empty((0,)) if k_offsets is None else k_offsets[where_true(k_faces)]
720
- j_offsets_list = np.empty((0,)) if j_offsets is None else j_offsets[where_true(j_faces)]
721
- i_offsets_list = np.empty((0,)) if i_offsets is None else i_offsets[where_true(i_faces)]
789
+ k_offsets_list = (np.empty((0,)) if k_offsets is None else k_offsets[_where_true(k_faces)])
790
+ j_offsets_list = (np.empty((0,)) if j_offsets is None else j_offsets[_where_true(j_faces)])
791
+ i_offsets_list = (np.empty((0,)) if i_offsets is None else i_offsets[_where_true(i_faces)])
722
792
  all_offsets = _all_offsets(grid.crs, k_offsets_list, j_offsets_list, i_offsets_list)
723
793
  # log.debug(f'gcs count: {gcs.count}; all offsets shape: {all_offsets.shape}')
724
794
  assert all_offsets.shape == (gcs.count,)
725
795
 
726
796
  if return_flange_bool:
727
797
  # log.debug('preparing flange array')
728
- flange_bool_uuid = surface.model.uuid(title = 'flange bool',
729
- obj_type = 'DiscreteProperty',
798
+ flange_bool_uuid = surface.model.uuid(title = "flange bool",
799
+ obj_type = "DiscreteProperty",
730
800
  related_uuid = surface.uuid)
731
- assert flange_bool_uuid is not None, f"No flange bool property found for surface: {surface.title}"
801
+ assert (flange_bool_uuid is not None), f"No flange bool property found for surface: {surface.title}"
732
802
  flange_bool = rqp.Property(surface.model, uuid = flange_bool_uuid)
733
803
  flange_array = flange_bool.array_ref()
734
804
  all_flange = np.take(flange_array, all_tris)
@@ -737,46 +807,46 @@ def find_faces_to_represent_surface_regular_optimised(grid,
737
807
  # note: following is a grid cells property, not a gcs property
738
808
  if return_bisector:
739
809
  if is_curtain:
740
- log.debug('preparing columns bisector')
810
+ log.debug("preparing columns bisector")
741
811
  bisector = column_bisector_from_faces((grid.nj, grid.ni), j_faces[0], i_faces[0])
742
812
  # log.debug('finished preparing columns bisector')
743
813
  else:
744
- log.debug('preparing cells bisector')
814
+ log.debug("preparing cells bisector")
745
815
  bisector, is_curtain = bisector_from_faces(tuple(grid.extent_kji), k_faces, j_faces, i_faces, raw_bisector)
746
816
  if is_curtain:
747
817
  bisector = bisector[0] # reduce to a columns property
748
818
 
749
819
  # note: following is a grid cells property, not a gcs property
750
820
  if return_shadow:
751
- log.debug('preparing cells shadow')
821
+ log.debug("preparing cells shadow")
752
822
  shadow = shadow_from_faces(tuple(grid.extent_kji), k_faces)
753
823
 
754
824
  if progress_fn is not None:
755
825
  progress_fn(1.0)
756
826
 
757
- log.debug(f'finishing find_faces_to_represent_surface_regular_optimised for {name}')
827
+ log.debug(f"finishing find_faces_to_represent_surface_regular_optimised for {name}")
758
828
 
759
829
  # if returning properties, construct dictionary
760
830
  if return_properties:
761
831
  props_dict = {}
762
832
  if return_triangles:
763
- props_dict['triangle'] = all_tris
833
+ props_dict["triangle"] = all_tris
764
834
  if return_depths:
765
- props_dict['depth'] = all_depths
835
+ props_dict["depth"] = all_depths
766
836
  if return_offsets:
767
- props_dict['offset'] = all_offsets
837
+ props_dict["offset"] = all_offsets
768
838
  if return_bisector:
769
- props_dict['grid bisector'] = (bisector, is_curtain)
839
+ props_dict["grid bisector"] = (bisector, is_curtain)
770
840
  if return_shadow:
771
- props_dict['grid shadow'] = shadow
841
+ props_dict["grid shadow"] = shadow
772
842
  if return_flange_bool:
773
- props_dict['flange bool'] = all_flange
843
+ props_dict["flange bool"] = all_flange
774
844
  return (gcs, props_dict)
775
845
 
776
846
  return gcs
777
847
 
778
848
 
779
- def find_faces_to_represent_surface(grid, surface, name, mode = 'auto', feature_type = 'fault', progress_fn = None):
849
+ def find_faces_to_represent_surface(grid, surface, name, mode = "auto", feature_type = "fault", progress_fn = None):
780
850
  """Returns a grid connection set containing those cell faces which are deemed to represent the surface.
781
851
 
782
852
  arguments:
@@ -797,44 +867,49 @@ def find_faces_to_represent_surface(grid, surface, name, mode = 'auto', feature_
797
867
  this is a wrapper function selecting between more specialised functions; use those directly for more options
798
868
  """
799
869
 
800
- log.debug('finding cell faces for surface')
801
- if mode == 'auto':
870
+ log.debug("finding cell faces for surface")
871
+ if mode == "auto":
802
872
  if isinstance(grid, grr.RegularGrid) and grid.is_aligned:
803
- mode = 'regular_optimised'
873
+ mode = "regular_optimised"
804
874
  else:
805
- mode = 'staffa'
806
- if mode == 'staffa':
875
+ mode = "staffa"
876
+ if mode == "staffa":
807
877
  return find_faces_to_represent_surface_staffa(grid,
808
878
  surface,
809
879
  name,
810
880
  feature_type = feature_type,
811
881
  progress_fn = progress_fn)
812
- elif mode == 'regular':
882
+ elif mode == "regular":
813
883
  return find_faces_to_represent_surface_regular(grid,
814
884
  surface,
815
885
  name,
816
886
  feature_type = feature_type,
817
887
  progress_fn = progress_fn)
818
- elif mode == 'regular_optimised':
888
+ elif mode == "regular_optimised":
819
889
  return find_faces_to_represent_surface_regular_optimised(grid,
820
890
  surface,
821
891
  name,
822
892
  feature_type = feature_type,
823
893
  progress_fn = progress_fn)
824
- elif mode == 'regular_cuda':
894
+ elif mode == "regular_cuda":
825
895
  import resqpy.grid_surface.grid_surface_cuda as rgs_c
896
+
826
897
  return rgs_c.find_faces_to_represent_surface_regular_cuda_mgpu(grid,
827
898
  surface,
828
899
  name,
829
900
  feature_type = feature_type,
830
901
  progress_fn = progress_fn)
831
- log.critical('unrecognised mode: ' + str(mode))
902
+ log.critical("unrecognised mode: " + str(mode))
832
903
  return None
833
904
 
834
905
 
835
906
  def bisector_from_faces( # type: ignore
836
- grid_extent_kji: Tuple[int, int, int], k_faces: np.ndarray, j_faces: np.ndarray, i_faces: np.ndarray,
837
- raw_bisector: bool) -> Tuple[np.ndarray, bool]:
907
+ grid_extent_kji: Tuple[int, int, int],
908
+ k_faces: np.ndarray,
909
+ j_faces: np.ndarray,
910
+ i_faces: np.ndarray,
911
+ raw_bisector: bool,
912
+ ) -> Tuple[np.ndarray, bool]:
838
913
  """Creates a boolean array denoting the bisection of the grid by the face sets.
839
914
 
840
915
  arguments:
@@ -861,9 +936,14 @@ def bisector_from_faces( # type: ignore
861
936
  boundary = get_boundary(k_faces, j_faces, i_faces, grid_extent_kji)
862
937
 
863
938
  # Setting up the bisector array for the bounding box.
864
- bounding_array = np.zeros((boundary["k_max"] - boundary["k_min"] + 1, boundary["j_max"] - boundary["j_min"] + 1,
865
- boundary["i_max"] - boundary["i_min"] + 1),
866
- dtype = np.bool_)
939
+ bounding_array = np.zeros(
940
+ (
941
+ boundary["k_max"] - boundary["k_min"] + 1,
942
+ boundary["j_max"] - boundary["j_min"] + 1,
943
+ boundary["i_max"] - boundary["i_min"] + 1,
944
+ ),
945
+ dtype = np.bool_,
946
+ )
867
947
 
868
948
  # Seeding the bisector array from (0, 0, 0) up to the first faces that represent the surface.
869
949
  boundary_values = tuple(boundary.values())
@@ -886,19 +966,25 @@ def bisector_from_faces( # type: ignore
886
966
  point = tuple(point) # type: ignore
887
967
  if point not in points:
888
968
  points.add(point)
889
- bounding_array, _, _, _ = _seed_array(point, k_faces, j_faces, i_faces, boundary_values,
890
- bounding_array)
969
+ bounding_array, _, _, _ = _seed_array(
970
+ point,
971
+ k_faces,
972
+ j_faces,
973
+ i_faces,
974
+ boundary_values,
975
+ bounding_array,
976
+ )
891
977
 
892
978
  # Setting up the array for the changing values.
893
979
  changing_array = np.zeros_like(bounding_array, dtype = np.bool_)
894
980
 
895
981
  # Repeatedly spreading True values to neighbouring cells that are not the other side of a face.
896
982
  open_k = np.logical_not(k_faces)[boundary["k_min"]:boundary["k_max"], boundary["j_min"]:boundary["j_max"] + 1,
897
- boundary["i_min"]:boundary["i_max"] + 1]
983
+ boundary["i_min"]:boundary["i_max"] + 1,]
898
984
  open_j = np.logical_not(j_faces)[boundary["k_min"]:boundary["k_max"] + 1, boundary["j_min"]:boundary["j_max"],
899
- boundary["i_min"]:boundary["i_max"] + 1]
985
+ boundary["i_min"]:boundary["i_max"] + 1,]
900
986
  open_i = np.logical_not(i_faces)[boundary["k_min"]:boundary["k_max"] + 1, boundary["j_min"]:boundary["j_max"] + 1,
901
- boundary["i_min"]:boundary["i_max"]]
987
+ boundary["i_min"]:boundary["i_max"],]
902
988
  while True:
903
989
  changing_array[:] = False
904
990
 
@@ -927,7 +1013,7 @@ def bisector_from_faces( # type: ignore
927
1013
  # Setting up the full bisectors array and assigning the bounding box values.
928
1014
  array = np.zeros(grid_extent_kji, dtype = np.bool_)
929
1015
  array[boundary["k_min"]:boundary["k_max"] + 1, boundary["j_min"]:boundary["j_max"] + 1,
930
- boundary["i_min"]:boundary["i_max"] + 1] = bounding_array
1016
+ boundary["i_min"]:boundary["i_max"] + 1,] = bounding_array
931
1017
 
932
1018
  # Setting values outside of the bounding box.
933
1019
  if boundary["k_max"] != grid_extent_kji[0] - 1 and np.any(bounding_array[-1, :, :]):
@@ -946,7 +1032,7 @@ def bisector_from_faces( # type: ignore
946
1032
  # Check all array elements are not the same.
947
1033
  true_count = np.count_nonzero(array)
948
1034
  cell_count = array.size
949
- assert 0 < true_count < cell_count, 'Face set for surface is leaky or empty (surface does not intersect grid).'
1035
+ assert (0 < true_count < cell_count), "Face set for surface is leaky or empty (surface does not intersect grid)."
950
1036
 
951
1037
  # Negate the array if it minimises the mean k and determine if the surface is a curtain.
952
1038
  layer_cell_count = grid_extent_kji[1] * grid_extent_kji[2]
@@ -1005,7 +1091,7 @@ def column_bisector_from_faces(grid_extent_ji: Tuple[int, int], j_faces: np.ndar
1005
1091
  # j faces
1006
1092
  c[1:, :] = np.logical_or(c[1:, :], np.logical_and(a[:-1, :], open_j))
1007
1093
  c[:-1, :] = np.logical_or(c[:-1, :], np.logical_and(a[1:, :], open_j))
1008
- # i faces
1094
+ # i faces
1009
1095
  c[:, 1:] = np.logical_or(c[:, 1:], np.logical_and(a[:, :-1], open_i))
1010
1096
  c[:, :-1] = np.logical_or(c[:, :-1], np.logical_and(a[:, 1:], open_i))
1011
1097
  c[:] = np.logical_and(c, np.logical_not(a))
@@ -1013,7 +1099,7 @@ def column_bisector_from_faces(grid_extent_ji: Tuple[int, int], j_faces: np.ndar
1013
1099
  break
1014
1100
  a[:] = np.logical_or(a, c)
1015
1101
  if np.all(a):
1016
- log.warning('curtain is leaky or misses grid when setting column bisector')
1102
+ log.warning("curtain is leaky or misses grid when setting column bisector")
1017
1103
  # log.debug(f'returning bisector with count: {np.count_nonzero(a)} of {a.size}; shape: {a.shape}')
1018
1104
  return a
1019
1105
 
@@ -1055,8 +1141,11 @@ def shadow_from_faces(extent_kji, k_faces):
1055
1141
 
1056
1142
 
1057
1143
  def get_boundary( # type: ignore
1058
- k_faces: np.ndarray, j_faces: np.ndarray, i_faces: np.ndarray, grid_extent_kji: Tuple[int, int,
1059
- int]) -> Dict[str, int]:
1144
+ k_faces: np.ndarray,
1145
+ j_faces: np.ndarray,
1146
+ i_faces: np.ndarray,
1147
+ grid_extent_kji: Tuple[int, int, int],
1148
+ ) -> Dict[str, int]:
1060
1149
  """Cretaes a dictionary of the indices that bound the surface (where the faces are True).
1061
1150
 
1062
1151
  arguments:
@@ -1086,24 +1175,24 @@ def get_boundary( # type: ignore
1086
1175
 
1087
1176
  for f_i, faces in enumerate([k_faces, j_faces, i_faces]):
1088
1177
 
1089
- # NB. k, j & i for rest of loop refer to indices of faces, regardless of which face set is being processed
1178
+ # NB. k, j & i for rest of loop refer to indices of faces, regardless of which face set is being processed
1090
1179
 
1091
- where_k, where_j, where_i = where_true(faces)
1180
+ where_k, where_j, where_i = _where_true(faces)
1092
1181
 
1093
1182
  if len(where_k) == 0:
1094
1183
  continue
1095
1184
 
1096
1185
  min_k = where_k[0] # optimisation if np.where() guaranteed to return results in natural order
1097
1186
  max_k = where_k[-1]
1098
- # min_k = np.amin(where_k)
1099
- # max_k = np.amax(where_k)
1187
+ # min_k = np.amin(where_k)
1188
+ # max_k = np.amax(where_k)
1100
1189
  min_j = np.amin(where_j)
1101
1190
  max_j = np.amax(where_j)
1102
1191
  min_i = np.amin(where_i)
1103
1192
  max_i = np.amax(where_i)
1104
1193
 
1105
1194
  # include cells on both sides of internal faces
1106
- # and add buffer slice where edge of grid not reached by surface faces
1195
+ # and add buffer slice where edge of grid not reached by surface faces
1107
1196
  if f_i == 0:
1108
1197
  max_k += 1
1109
1198
  else:
@@ -1127,38 +1216,38 @@ def get_boundary( # type: ignore
1127
1216
  max_i += 1
1128
1217
 
1129
1218
  if starting:
1130
- boundary['k_min'] = min_k
1131
- boundary['k_max'] = max_k
1132
- boundary['j_min'] = min_j
1133
- boundary['j_max'] = max_j
1134
- boundary['i_min'] = min_i
1135
- boundary['i_max'] = max_i
1219
+ boundary["k_min"] = min_k
1220
+ boundary["k_max"] = max_k
1221
+ boundary["j_min"] = min_j
1222
+ boundary["j_max"] = max_j
1223
+ boundary["i_min"] = min_i
1224
+ boundary["i_max"] = max_i
1136
1225
  starting = False
1137
1226
  else:
1138
- if min_k < boundary['k_min']:
1139
- boundary['k_min'] = min_k
1140
- if max_k > boundary['k_max']:
1141
- boundary['k_max'] = max_k
1142
- if min_j < boundary['j_min']:
1143
- boundary['j_min'] = min_j
1144
- if max_j > boundary['j_max']:
1145
- boundary['j_max'] = max_j
1146
- if min_i < boundary['i_min']:
1147
- boundary['i_min'] = min_i
1148
- if max_i > boundary['i_max']:
1149
- boundary['i_max'] = max_i
1227
+ if min_k < boundary["k_min"]:
1228
+ boundary["k_min"] = min_k
1229
+ if max_k > boundary["k_max"]:
1230
+ boundary["k_max"] = max_k
1231
+ if min_j < boundary["j_min"]:
1232
+ boundary["j_min"] = min_j
1233
+ if max_j > boundary["j_max"]:
1234
+ boundary["j_max"] = max_j
1235
+ if min_i < boundary["i_min"]:
1236
+ boundary["i_min"] = min_i
1237
+ if max_i > boundary["i_max"]:
1238
+ boundary["i_max"] = max_i
1150
1239
 
1151
1240
  return boundary # type: ignore
1152
1241
 
1153
1242
 
1154
- @njit
1155
- def where_true(data: np.ndarray):
1243
+ @njit # pragma: no cover
1244
+ def _where_true(data: np.ndarray):
1156
1245
  """Jitted NumPy 'where' function to improve performance on subsequent calls."""
1157
1246
  return np.where(data)
1158
1247
 
1159
1248
 
1160
- @njit
1161
- def first_true(array: np.ndarray) -> Optional[int]: # type: ignore
1249
+ @njit # pragma: no cover
1250
+ def _first_true(array: np.ndarray) -> Optional[int]: # type: ignore
1162
1251
  """Returns the index + 1 of the first True value in the array."""
1163
1252
  for idx, val in np.ndenumerate(array):
1164
1253
  if val:
@@ -1166,11 +1255,24 @@ def first_true(array: np.ndarray) -> Optional[int]: # type: ignore
1166
1255
  return array.size
1167
1256
 
1168
1257
 
1169
- @njit
1170
- def intersect_numba(axis: int, index1: int, index2: int, hits: np.ndarray, n_axis: int, points: np.ndarray,
1171
- triangles: np.ndarray, grid_dxyz: Tuple[float], faces: np.ndarray, return_depths: bool,
1172
- depths: np.ndarray, return_offsets: bool, offsets: np.ndarray, return_triangles: bool,
1173
- triangle_per_face: np.ndarray) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
1258
+ @njit # pragma: no cover
1259
+ def intersect_numba(
1260
+ axis: int,
1261
+ index1: int,
1262
+ index2: int,
1263
+ hits: np.ndarray,
1264
+ n_axis: int,
1265
+ points: np.ndarray,
1266
+ triangles: np.ndarray,
1267
+ grid_dxyz: Tuple[float],
1268
+ faces: np.ndarray,
1269
+ return_depths: bool,
1270
+ depths: np.ndarray,
1271
+ return_offsets: bool,
1272
+ offsets: np.ndarray,
1273
+ return_triangles: bool,
1274
+ triangle_per_face: np.ndarray,
1275
+ ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
1174
1276
  """Finds the faces that intersect the surface in 3D.
1175
1277
 
1176
1278
  arguments:
@@ -1204,12 +1306,12 @@ def intersect_numba(axis: int, index1: int, index2: int, hits: np.ndarray, n_axi
1204
1306
  for i in prange(len(hits)):
1205
1307
  tri, d1, d2 = hits[i]
1206
1308
 
1207
- # Line start point in 3D which had a projection hit.
1309
+ # Line start point in 3D which had a projection hit.
1208
1310
  centre_point_start = np.zeros(3, dtype = np.float64) + grid_dxyz[axis] / 2
1209
1311
  centre_point_start[2 - index1] = (d1 + 0.5) * grid_dxyz[2 - index1]
1210
1312
  centre_point_start[2 - index2] = (d2 + 0.5) * grid_dxyz[2 - index2]
1211
1313
 
1212
- # Line end point in 3D.
1314
+ # Line end point in 3D.
1213
1315
  centre_point_end = np.copy(centre_point_start)
1214
1316
  centre_point_end[axis] = grid_dxyz[axis] * (n_axis - 0.5)
1215
1317
 
@@ -1248,9 +1350,15 @@ def intersect_numba(axis: int, index1: int, index2: int, hits: np.ndarray, n_axi
1248
1350
  return faces, offsets, triangle_per_face
1249
1351
 
1250
1352
 
1251
- @njit
1252
- def _seed_array(point: Tuple[int, int, int], k_faces: np.ndarray, j_faces: np.ndarray, i_faces: np.ndarray,
1253
- boundary: Tuple[int, int, int, int, int, int], array: np.ndarray) -> Tuple[np.ndarray, int, int, int]:
1353
+ @njit # pragma: no cover
1354
+ def _seed_array(
1355
+ point: Tuple[int, int, int],
1356
+ k_faces: np.ndarray,
1357
+ j_faces: np.ndarray,
1358
+ i_faces: np.ndarray,
1359
+ boundary: Tuple[int, int, int, int, int, int],
1360
+ array: np.ndarray,
1361
+ ) -> Tuple[np.ndarray, int, int, int]:
1254
1362
  """Sets values of the array True up until a face is hit in each direction.
1255
1363
 
1256
1364
  arguments:
@@ -1279,17 +1387,17 @@ def _seed_array(point: Tuple[int, int, int], k_faces: np.ndarray, j_faces: np.nd
1279
1387
 
1280
1388
  first_k = 0
1281
1389
  if k == 0:
1282
- first_k = first_true(k_faces[boundary[0]:boundary[1], boundary[2] + j, boundary[4] + i])
1390
+ first_k = _first_true(k_faces[boundary[0]:boundary[1], boundary[2] + j, boundary[4] + i])
1283
1391
  array[:first_k, j, i] = True
1284
1392
 
1285
1393
  first_j = 0
1286
1394
  if j == 0:
1287
- first_j = first_true(j_faces[boundary[0] + k, boundary[2]:boundary[3], boundary[4] + i])
1395
+ first_j = _first_true(j_faces[boundary[0] + k, boundary[2]:boundary[3], boundary[4] + i])
1288
1396
  array[k, :first_j, i] = True
1289
1397
 
1290
1398
  first_i = 0
1291
1399
  if i == 0:
1292
- first_i = first_true(i_faces[boundary[0] + k, boundary[2] + j, boundary[4]:boundary[5]])
1400
+ first_i = _first_true(i_faces[boundary[0] + k, boundary[2] + j, boundary[4]:boundary[5]])
1293
1401
  array[k, j, :first_i] = True
1294
1402
 
1295
1403
  return array, first_k, first_j, first_i