When a DEM is supplied through externalDEMFile, SNAP samples it half a DEM pixel south-east of its georeferenced position. The auto-downloaded Copernicus DEM is sampled correctly, so the two paths disagree even when they read exactly the same data.
Cause
FileElevationModel.getElevation(GeoPos) gets pix from tileGeocoding.getPixelPos and then calls:
resampling.computeCornerBasedIndex(pix.x, pix.y, RASTER_WIDTH, RASTER_HEIGHT, newIndex);
computeCornerBasedIndex adds 0.5 to both coordinates before calling computeIndex. But
getPixelPos already returns SNAP’s convention, in which the first pixel center is at (0.5, 0.5), so the half pixel is added twice.
CopernicusElevationModel calls the same method, but passes indices computed directly from
longitude and latitude. Those are corner-based, so the extra half pixel is correct there.
Calling computeIndex directly in FileElevationModel could be the intended fix.
Reproduction
- Environment: SNAP 13.0.0
- Burst SLC: 015-030346-IW1, VV, 2022-08-01
- DEM: Copernicus GLO-30, tiles
N46_00_E007_00andN46_00_E008_00
I processed the SLC to RTC backscatter with Apply-Orbit-File → ThermalNoiseRemoval → Calibration → TOPSAR-Deburst → Multilook → Terrain-Flattening → Terrain-Correction, and set saveDEM=true so the product also contains the elevation
SNAP sampled.
I ran two experiments, which differ only in the DEM given to Terrain-Flattening and Terrain-Correction:
demName=Copernicus 30m Global DEMdemName=External DEM,externalDEMApplyEGM=true, with the two GLO-30 tiles mosaicked and still in orthometric heights
For the second run I used the tiles SNAP had downloaded itself, from
~/.snap/auxdata/dem/Copernicus 30m Global DEM/, so both runs start from the same inputs.
Leaving externalDEMApplyEGM=true makes SNAP add EGM96 to the external file too, exactly as it does internally, so the geoid cancels out and the sampling position is the only difference left (Applying EGM96 to a Copernicus DEM is wrong in the first place, but that is a separate issue).
Results
The two sampled DEMs, hillshaded:

Their difference:
Although both runs read the same tiles, the elevations differ by 7.4 m on average (9.4 m
standard deviation, −15.7 to +13.8 m for the 5th to 95th percentile).
Shifting the external run’s DEM back until it lines up with the built-in one takes 15.5 m
north-south and 10.5 m east-west, approximately half a GLO-30 pixel at 46.5 N, pointing to
the cause of the issue.
The RTC backscatter of the two runs:

Over the whole burst that is 2.14 dB standard deviation, and 21% of pixels differ by more than
1 dB.
Workaround
Shift the file’s geotransform origin half a pixel south-east, so that SNAP’s extra half pixel
lands back on the right place. GDAL reports the corners of the mosaic used here as 6.9998611 /
47.0001389 / 8.9998611 / 46.0001389, so the half-pixel-shift (1/7200º) is:
gdal_edit.py -a_ullr 7.0 47.0 9.0 46.0 dem.tif
Every other GIS then reads the file half a pixel wrong, so this file has to be used only by SNAP.

