@@ -130,6 +130,8 @@ def _compute_nnc_table(
130130 crop_origin : tuple [int , int , int ],
131131 refinement : tuple [int , int , int ],
132132 coarse_ncol : int ,
133+ lmap1 : np .ndarray ,
134+ lmap2 : np .ndarray ,
133135) -> pd .DataFrame :
134136 """Compute NNC cell-pair mapping between mother and refined cells.
135137
@@ -155,6 +157,9 @@ def _compute_nnc_table(
155157 crop_origin: 0-based ``(i0, j0, k0)`` origin of the crop box.
156158 refinement: ``(rcol, rrow, rlay)`` refinement factors.
157159 coarse_ncol: Number of columns in the coarse grid (grid1 in the merge).
160+ lmap1: Numpy array with layer_mapping (input k -> output k) for grid1
161+ lmap2: Numpy array with layer_mapping (input k -> output k) for grid2
162+
158163
159164 Returns:
160165 A DataFrame with columns ``I1, J1, K1, I2, J2, K2, DIRECTION``.
@@ -163,6 +168,7 @@ def _compute_nnc_table(
163168
164169 rcol , rrow , rlay = refinement
165170 i0 , j0 , k0 = crop_origin
171+
166172 # In the merged grid, grid2 (refined) starts after a 1-column gap:
167173 i_offset = coarse_ncol + 1
168174
@@ -234,10 +240,10 @@ def _compute_nnc_table(
234240 {
235241 "I1" : mi + 1 ,
236242 "J1" : mj + 1 ,
237- "K1" : mk + 1 ,
243+ "K1" : lmap1 [ mk ] + 1 ,
238244 "I2" : ri + i_offset + 1 ,
239245 "J2" : rj + 1 ,
240- "K2" : rk + 1 ,
246+ "K2" : lmap2 [ rk ] + 1 ,
241247 "DIRECTION" : direction ,
242248 }
243249 )
@@ -271,6 +277,38 @@ def _set_actnum_by_region(
271277 grid .set_actnum (actnum )
272278
273279
280+ def _generate_layer_mappings (
281+ coarse_nlay : int ,
282+ refined_nlay : int ,
283+ refinement : tuple [int , int , int ],
284+ crop_origin : tuple [int , int , int ],
285+ ) -> tuple [np .ndarray , np .ndarray ]:
286+ """Generate mappings from old to new layer number.
287+ Args:
288+ coarse_nlay: Number of layers in the coarse grid (grid1 in the merge).
289+ refined_nlay: Number of layers in the refined grid (grid2 in the merge).
290+ crop_origin: 0-based ``(i0, j0, k0)`` origin of the crop box.
291+ refinement: ``(rcol, rrow, rlay)`` refinement factors.
292+
293+ Returns:
294+ lmap1: Numpy array with layer_mapping (input k -> output k) for grid1
295+ lmap2: Numpy array with layer_mapping (input k -> output k) for grid2
296+ """
297+
298+ _ , _ , rlay = refinement
299+ _ , _ , k0 = crop_origin
300+
301+ lmap1 = np .arange (coarse_nlay , dtype = np .int32 )
302+ lmap1 = lmap1 + np .where (
303+ lmap1 < k0 ,
304+ 0 ,
305+ (rlay - 1 ) * np .minimum (int (refined_nlay / rlay ), lmap1 - k0 ),
306+ )
307+ lmap2 = np .arange (refined_nlay , dtype = np .int32 ) + k0
308+
309+ return (lmap1 , lmap2 )
310+
311+
274312# ---------------------------------------------------------------------------
275313# Public API
276314# ---------------------------------------------------------------------------
@@ -281,17 +319,16 @@ def create_nested_hybrid_grid(
281319 region : xtgeo .GridProperty ,
282320 target_region_id : int ,
283321 refinement : tuple [int , int , int ],
284- ) -> tuple [xtgeo .Grid , pd .DataFrame ]:
322+ ) -> tuple [
323+ xtgeo .Grid ,
324+ pd .DataFrame ,
325+ ]:
285326 """Create a nested hybrid grid by refining one region and merging it back.
286327
287328 The cells belonging to *target_region_id* are replaced by a refined
288- (subdivided) version of the same region. A ``NEST_ID`` discrete property
289- is attached to the merged grid, encoding the nested hybrid structure:
290-
291- - ``NEST_ID == 1``: coarse (mother) grid cells.
292- - ``NEST_ID == 2``: refined grid cells.
329+ (subdivided) version of the same region.
293330
294- In addition, a **NNC mapping table** is returned that lists every
331+ A **NNC mapping table** is returned that lists every
295332 mother ↔ refined cell pair that should be connected by a Non-Neighbour
296333 Connection (NNC). The table is derived from the topological knowledge
297334 available at merge time (which original cell was refined and how its
@@ -316,9 +353,9 @@ def create_nested_hybrid_grid(
316353 refinement: ``(ncol, nrow, nlay)`` refinement factors.
317354
318355 Returns:
319- A tuple ``(merged_grid, nnc_table)`` where *merged_grid* is a new
320- :class:`xtgeo.Grid` with the refined region stitched back into the
321- coarse grid and *nnc_table* is a :class:`pandas.DataFrame` mapping
356+ A tuple ``(merged_grid, nnc_table)`` where *merged_grid*
357+ is a new :class:`xtgeo.Grid` with the refined region stitched back into
358+ the coarse grid and *nnc_table* is a :class:`pandas.DataFrame` mapping
322359 mother cells to their connected refined cells.
323360 """
324361 if any (r < 1 for r in refinement ):
@@ -343,10 +380,19 @@ def create_nested_hybrid_grid(
343380 # 2. Refine the cropped grid.
344381 refined = cropped .copy ()
345382 rcol , rrow , rlay = refinement
383+ _ , _ , olay = crop_origin
346384 refined .refine (refine_col = rcol , refine_row = rrow , refine_layer = rlay )
347385 _logger .info ("Refined cropped grid dimensions: %s" , refined .dimensions )
348386
349- # 3. Compute the NNC mapping table *before* deactivation mutates anything.
387+ # 3. Generate layer mappings
388+ lmap1 , lmap2 = _generate_layer_mappings (
389+ coarse_nlay = grid .nlay ,
390+ refined_nlay = refined .nlay ,
391+ crop_origin = crop_origin ,
392+ refinement = refinement ,
393+ )
394+
395+ # 4. Compute the NNC mapping table *before* deactivation mutates anything.
350396 # This uses the original region property to find boundary faces and
351397 # maps them through the crop → refine → merge index chain.
352398 nnc_table = _compute_nnc_table (
@@ -355,37 +401,20 @@ def create_nested_hybrid_grid(
355401 crop_origin = crop_origin ,
356402 refinement = refinement ,
357403 coarse_ncol = grid .ncol ,
404+ lmap1 = lmap1 ,
405+ lmap2 = lmap2 ,
358406 )
359407
360- # 4 . Deactivate the target region in the coarse grid (will be replaced).
408+ # 5 . Deactivate the target region in the coarse grid (will be replaced).
361409 coarse_region = grid .get_prop_by_name (region .name )
362410 _set_actnum_by_region (grid , coarse_region , target_region_id , invert = False )
363411
364- # 5 . In the refined grid keep only target-region cells active.
412+ # 6 . In the refined grid keep only target-region cells active.
365413 refined_region = refined .get_prop_by_name (region .name )
366414 _set_actnum_by_region (refined , refined_region , target_region_id , invert = True )
367415
368- # 6. Create NEST_ID properties before merging (1=mother, 2=refined).
369- nest_id_coarse = xtgeo .GridProperty (
370- grid ,
371- name = "NEST_ID" ,
372- discrete = True ,
373- values = np .where (grid .get_actnum ().values == 1 , 1 , 0 ).astype (np .int32 ),
374- codes = {0 : "inactive" , 1 : "mother" , 2 : "refined" },
375- )
376- grid .append_prop (nest_id_coarse )
377-
378- nest_id_refined = xtgeo .GridProperty (
379- refined ,
380- name = "NEST_ID" ,
381- discrete = True ,
382- values = np .where (refined .get_actnum ().values == 1 , 2 , 0 ).astype (np .int32 ),
383- codes = {0 : "inactive" , 1 : "mother" , 2 : "refined" },
384- )
385- refined .append_prop (nest_id_refined )
386-
387416 # 7. Merge the two grids.
388- merged = xtgeo .grid_merge (grid , refined )
417+ merged = xtgeo .grid_merge (grid , refined , lmap1 , lmap2 )
389418 _logger .info ("Merged grid dimensions: %s" , merged .dimensions )
390419
391420 return merged , nnc_table
0 commit comments