Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit fceddc8

Browse files
aulemahalraphaeldussin
andauthoredFeb 7, 2025··
Do not store grids, destroy them after usage (#402)
* Destroy grid one used, do not store it * upd changes * Release mesh memory too --------- Co-authored-by: raphael dussin <[email protected]>
1 parent 12fd94a commit fceddc8

File tree

3 files changed

+28
-37
lines changed

3 files changed

+28
-37
lines changed
 

‎CHANGES.rst

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
What's new
22
==========
33

4+
0.8.9 (unreleased)
5+
------------------
6+
* Destroy grids explicitly once weights are computed. Do not store them in `grid_in` and `grid_out` attributes. This fixes segmentation faults introduced by the memory fix of last version. By `Pascal Bourgault <https://github.com/aulemahal>`_.
7+
48
0.8.8 (2024-11-01)
59
------------------
610
* Fix ESMpy memory issues by explictly freeing the Grid memory upon garbage collection of ``Regridder`` objects. By `Pascal Bourgault <https://github.com/aulemahal>`_.

‎xesmf/backend.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -546,19 +546,15 @@ def esmf_regrid_finalize(regrid):
546546
regrid : ESMF.Regrid object
547547
548548
"""
549-
549+
# We do not destroy the Grids here, as they might be reused between multiple regrids
550550
regrid.destroy()
551551
regrid.srcfield.destroy()
552552
regrid.dstfield.destroy()
553-
# regrid.srcfield.grid.destroy()
554-
# regrid.dstfield.grid.destroy()
555553

556554
# double check
557555
assert regrid.finalized
558556
assert regrid.srcfield.finalized
559557
assert regrid.dstfield.finalized
560-
# assert regrid.srcfield.grid.finalized
561-
# assert regrid.dstfield.grid.finalized
562558

563559

564560
# Deprecated as of version 0.5.0

‎xesmf/frontend.py

+23-32
Original file line numberDiff line numberDiff line change
@@ -334,17 +334,15 @@ def __init__(
334334
baseregridder : xESMF BaseRegridder object
335335
336336
"""
337-
self.grid_in = grid_in
338-
self.grid_out = grid_out
339337
self.method = method
340338
self.reuse_weights = reuse_weights
341339
self.extrap_method = extrap_method
342340
self.extrap_dist_exponent = extrap_dist_exponent
343341
self.extrap_num_src_pnts = extrap_num_src_pnts
344342
self.ignore_degenerate = ignore_degenerate
345-
self.periodic = getattr(self.grid_in, 'periodic_dim', None) is not None
346-
self.sequence_in = isinstance(self.grid_in, (LocStream, Mesh))
347-
self.sequence_out = isinstance(self.grid_out, (LocStream, Mesh))
343+
self.periodic = getattr(grid_in, 'periodic_dim', None) is not None
344+
self.sequence_in = isinstance(grid_in, (LocStream, Mesh))
345+
self.sequence_out = isinstance(grid_out, (LocStream, Mesh))
348346

349347
if input_dims is not None and len(input_dims) != int(not self.sequence_in) + 1:
350348
raise ValueError(f'Wrong number of dimension names in `input_dims` ({len(input_dims)}.')
@@ -358,8 +356,8 @@ def __init__(
358356

359357
# record grid shape information
360358
# We need to invert Grid shapes to respect xESMF's convention (y, x).
361-
self.shape_in = self.grid_in.get_shape()[::-1]
362-
self.shape_out = self.grid_out.get_shape()[::-1]
359+
self.shape_in = grid_in.get_shape()[::-1]
360+
self.shape_out = grid_out.get_shape()[::-1]
363361
self.n_in = self.shape_in[0] * self.shape_in[1]
364362
self.n_out = self.shape_out[0] * self.shape_out[1]
365363

@@ -369,7 +367,7 @@ def __init__(
369367

370368
if not parallel:
371369
if not reuse_weights and weights is None:
372-
weights = self._compute_weights() # Dictionary of weights
370+
weights = self._compute_weights(grid_in, grid_out) # Dictionary of weights
373371
else:
374372
weights = filename if filename is not None else weights
375373

@@ -380,7 +378,7 @@ def __init__(
380378

381379
# replace zeros by NaN for weight matrix entries of unmapped target cells if specified or a mask is present
382380
if (
383-
(self.grid_out.mask is not None) and (self.grid_out.mask[0] is not None)
381+
(grid_out.mask is not None) and (grid_out.mask[0] is not None)
384382
) or unmapped_to_nan is True:
385383
self.weights = add_nans_to_weights(self.weights)
386384

@@ -435,10 +433,10 @@ def _get_default_filename(self):
435433

436434
return filename
437435

438-
def _compute_weights(self):
436+
def _compute_weights(self, grid_in, grid_out):
439437
regrid = esmf_regrid_build(
440-
self.grid_in,
441-
self.grid_out,
438+
grid_in,
439+
grid_out,
442440
self.method,
443441
extrap_method=self.extrap_method,
444442
extrap_dist_exponent=self.extrap_dist_exponent,
@@ -934,6 +932,9 @@ def __init__(
934932
parallel=parallel,
935933
**kwargs,
936934
)
935+
# Weights are computed, we do not need the grids anymore
936+
grid_in.destroy()
937+
grid_out.destroy()
937938

938939
# Record output grid and metadata
939940
lon_out, lat_out = _get_lon_lat(ds_out)
@@ -1109,13 +1110,6 @@ def _format_xroutput(self, out, new_dims=None):
11091110

11101111
return out
11111112

1112-
def __del__(self):
1113-
# Memory leak issue when regridding over a large number of datasets with xESMF
1114-
# https://github.com/JiaweiZhuang/xESMF/issues/53
1115-
if hasattr(self, 'grid_in'): # If the init has failed, grid_in isn't there
1116-
self.grid_in.destroy()
1117-
self.grid_out.destroy()
1118-
11191113

11201114
class SpatialAverager(BaseRegridder):
11211115
def __init__(
@@ -1249,6 +1243,9 @@ def __init__(
12491243
ignore_degenerate=ignore_degenerate,
12501244
unmapped_to_nan=False,
12511245
)
1246+
# Weights are computed, we do not need the grids anymore
1247+
grid_in.destroy()
1248+
locstream_out.destroy()
12521249

12531250
@staticmethod
12541251
def _check_polys_length(polys, threshold=1):
@@ -1267,12 +1264,12 @@ def _check_polys_length(polys, threshold=1):
12671264
stacklevel=2,
12681265
)
12691266

1270-
def _compute_weights_and_area(self, mesh_out):
1267+
def _compute_weights_and_area(self, grid_in, mesh_out):
12711268
"""Return the weights and the area of the destination mesh cells."""
12721269

12731270
# Build the regrid object
12741271
regrid = esmf_regrid_build(
1275-
self.grid_in,
1272+
grid_in,
12761273
mesh_out,
12771274
method='conservative',
12781275
ignore_degenerate=self.ignore_degenerate,
@@ -1286,10 +1283,9 @@ def _compute_weights_and_area(self, mesh_out):
12861283
regrid.dstfield.get_area()
12871284
dstarea = regrid.dstfield.data.copy()
12881285

1289-
esmf_regrid_finalize(regrid)
12901286
return w, dstarea
12911287

1292-
def _compute_weights(self):
1288+
def _compute_weights(self, grid_in, grid_out):
12931289
"""Return weight sparse matrix.
12941290
12951291
This function first explodes the geometries into a flat list of Polygon exterior objects:
@@ -1315,14 +1311,16 @@ def _compute_weights(self):
13151311
mesh_ext = Mesh.from_polygons(exteriors)
13161312

13171313
# Get weights for external polygons
1318-
w, area = self._compute_weights_and_area(mesh_ext)
1314+
w, area = self._compute_weights_and_area(grid_in, mesh_ext)
1315+
mesh_ext.destroy() # release mesh memory
13191316

13201317
# Get weights for interiors and append them to weights from exteriors as a negative contribution.
13211318
if len(interiors) > 0 and not self.ignore_holes:
13221319
mesh_int = Mesh.from_polygons(interiors)
13231320

13241321
# Get weights for interiors
1325-
w_int, area_int = self._compute_weights_and_area(mesh_int)
1322+
w_int, area_int = self._compute_weights_and_area(grid_in, mesh_int)
1323+
mesh_int.destroy() # release mesh memory
13261324

13271325
# Append weights from holes as negative weights
13281326
# In sparse >= 0.16, a fill_value of -0.0 is different from 0.0 and the concat would fail
@@ -1382,10 +1380,3 @@ def _format_xroutput(self, out, new_dims=None):
13821380
out.coords[self._lat_out_name] = xr.DataArray(self._lat_out, dims=(self.geom_dim_name,))
13831381
out.attrs['regrid_method'] = self.method
13841382
return out
1385-
1386-
def __del__(self):
1387-
# Memory leak issue when regridding over a large number of datasets with xESMF
1388-
# https://github.com/JiaweiZhuang/xESMF/issues/53
1389-
if hasattr(self, 'grid_in'): # If the init has failed, grid_in isn't there
1390-
self.grid_in.destroy()
1391-
self.grid_out.destroy()

0 commit comments

Comments
 (0)
Please sign in to comment.