Add FCI LSA SAF FRP csv and nc reader support - #3434
alexhalbig wants to merge 15 commits into
Conversation
… enhancement files.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3434 +/- ##
==========================================
+ Coverage 96.34% 96.36% +0.02%
==========================================
Files 465 469 +4
Lines 59151 59598 +447
==========================================
+ Hits 56990 57434 +444
- Misses 2161 2164 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
ameraner
left a comment
There was a problem hiding this comment.
Nice job, thank you for this! Just some minor comments/questions/edits, and a requried change for the pixel coordinates.
| data = data.assign_coords({ | ||
| "longitude": ("y", lons), | ||
| "latitude": ("y", lats), | ||
| }) |
There was a problem hiding this comment.
maybe I'm missing something, but what is the point of adding the lon-lat as coordinates on y here, if we then immediately after remap the data on the 2-d array?
There was a problem hiding this comment.
You are right. Dimensions are not needed here.
Removed assign_cords()
There was a problem hiding this comment.
could it be that the change was not pushed? The assign_coords is still there
|
The CI run still fails in the two TestParallaxCorrectionModifier.test_modifier_interface_cloud_moves_to_observer cases for foroyar. The failure is caused by an unevaluated Dask boolean array: This behavior surprises me, as the CI tests passed 2 days ago. The changes made, suggested by Andreas Review, should not impact the testing success in my opinion. My branch is not behind origin/main (HEAD...origin/main = 8 0). Could you please confirm whether this is a known Dask/Pyresample compatibility issue and whether these two tests should be adjusted or marked as expected failures? |
ameraner
left a comment
There was a problem hiding this comment.
Thank you for the updates! I noticed a couple of things, see below.
The failing tests are not due to your changes, they're probably due to some library update that broke the parallax tests.. they will need to be fixed separately (feel free to give it a go in another PR if you have time).
| nc_fci_frp: | ||
| file_reader: !!python/name:satpy.readers.fci_lsasaf_frp_l2_nc.FRPFileHandler | ||
| file_patterns: | ||
| - 'W_PT-{facility_or_tool},SATELLITE,LSA-509_{platform_name}_MTFRPPIXEL_MTG-{coverage}_{disposition_mode}_LPMG_{start_time:%Y%m%d%H%M%S}.nc' |
There was a problem hiding this comment.
until we really support nc, this should be removed
There was a problem hiding this comment.
I thought one step in advance, as I just need to finish the utests for the nc-reader. But I agree with you. Will be removed here, and added later.
There was a problem hiding this comment.
If NC support is finished then this thread can be resolved right? My next question though if that's true, should the NC files be listed first in the file_type:s below? I assume the netcdf files are better quality and better performance to read.
There was a problem hiding this comment.
Yes agree, they contain more reliant metadata information than parsing information from the filename. I admit, I didn't know the order of the file_types is important.
| data = data.assign_coords({ | ||
| "longitude": ("y", lons), | ||
| "latitude": ("y", lats), | ||
| }) |
There was a problem hiding this comment.
could it be that the change was not pushed? The assign_coords is still there
| grid = np.full(FRP_GRID_SHAPE, np.nan, dtype=np.float32) | ||
| grid[rows_int, cols_int] = values | ||
| xarr = xr.DataArray( | ||
| grid, | ||
| dims=("y", "x"), | ||
| attrs=attrs, | ||
| ) |
There was a problem hiding this comment.
The grid array here should be daskified to adhere to the normal format of reader outputs, and to optimise memory usage. You can take inspiration from an old version of the LI L2 reader to do that: https://github.com/pytroll/satpy/blame/618fc0e498c7f1e1bfce011f1ed1b041895ed8ce/satpy/readers/li_l2_nc.py#L141-L148
(in that reader, later on we needed an np.add.at functionality - so if you look at the current reader it is much more complex. However you don't need that here, since we have only one value per pixel, hence my link to an older blame version).
There was a problem hiding this comment.
Thanks this was helpful. The new version is daskified now.
|
I confirm that tests are failing in satpy main after |
|
The test failures are unrelated to this PR. Satpy main is also failing. See #3441 . |
Merge remote-tracking branch 'upstream/main' into feature-add-frp-mtg-reader
| values = data_array.data.compute() if hasattr(data_array.data, "compute") else data_array.values | ||
|
|
||
| # Create an empty 1-D nan array for the results | ||
| flattened_result = np.nan * da.zeros((FRP_GRID_SHAPE[0] * FRP_GRID_SHAPE[1]), dtype=data_array.dtype) |
There was a problem hiding this comment.
I’ve been thinking about whether this should be implemented with or without np.nan. What do you think?
- with np.nan: it becomes a masked array, which can still optionally be filled using fill_value=0
- without np.nan: it remains a regular raster, automatically filled with fill_value=0
As it's a fire detection variable, I prefer the first one.
There was a problem hiding this comment.
I think a NaN filled raster makes the most sense if I'm understanding you correctly.
| # | ||
| # satpy is free software: you can redistribute it and/or modify | ||
| # it under the terms of the GNU General Public License as published by | ||
| # the Free Software Foundation, either version 3 of the License, or | ||
| # (at your option) any later version. | ||
| # | ||
| # satpy is distributed in the hope that it will be useful, | ||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| # GNU General Public License for more details. | ||
| # | ||
| # You should have received a copy of the GNU General Public License | ||
| # along with satpy. If not, see <http://www.gnu.org/licenses/>. | ||
|
|
There was a problem hiding this comment.
| # | |
| # satpy is free software: you can redistribute it and/or modify | |
| # it under the terms of the GNU General Public License as published by | |
| # the Free Software Foundation, either version 3 of the License, or | |
| # (at your option) any later version. | |
| # | |
| # satpy is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| # GNU General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU General Public License | |
| # along with satpy. If not, see <http://www.gnu.org/licenses/>. |
We just recently relicensed to Apache v2 which does not require a license header on each file. Correct @mraspaud?
| @@ -0,0 +1,153 @@ | |||
| # Copyright (c) 2022 Satpy developers | |||
There was a problem hiding this comment.
| # Copyright (c) 2022 Satpy developers | |
| # Copyright (c) 2026 Satpy developers |
I'm not sure where we landed on copyright comments with the license change, but either way the year needs to be correct.
| """Get area definition for fci native 1 km grid.""" | ||
| return get_area_def("mtg_fci_fdss_1km") | ||
|
|
||
| def get_array_on_fci_grid(self, data_array): |
There was a problem hiding this comment.
I'm not familiar with this data. Could you tell me what abs_line and abs_samp contain? Are they a single value or a 1D array the same size as the variable being gridded?
Looking at the fancy work here I'm wondering/hoping if there is a way to put this into a map_blocks call or something. So the function that dask would call per chunk would generate the NaN filled chunk numpy array, fill in the data related to that chunk, and then return it. If you're already computing the 1D array (or pass it to map_blocks as a single chunk) then it should "just work". If necessary there is also dask's blockwise. My point being that I'm wondering if this could be a bit more lazy.
There was a problem hiding this comment.
abs_line represents the index of the active fire pixel, related to the same row in the native FCI projection grid.
Accordingly, abs_samp is the column number (zero based) value in the FCI projection.
There was a problem hiding this comment.
map_blocks and/or blockwise sounds worth a test. Maybe even dask.delayed. Anyway, the whole overall processing is already very fast, taking only about 10 seconds. I would consider this as a future improvement step :-)
There was a problem hiding this comment.
Sounds good. Semi-recently Dask has actually steered away from mixing Delayed objects and Dask arrays for performance reasons (it ends up duplicating some of the dask graph in some cases). Let's save it for later. that's fine by me.
There was a problem hiding this comment.
If these aren't two separate readers then I think it would make sense and follow common practice to put them in the same python module. Same for the tests.
There was a problem hiding this comment.
I agree that the tests and readers could be merged into one unified reader. That was my initial intention. However, I quickly noticed this would make the structure more complex to read and maintain.
The CSV files contain fewer variables than the NetCDF files and use a different filename structure. NetCDF provides detailed metadata; CSV provides none, except "encoded" in the filename.
I therefore prefer a simple KISS-style solution with minimal merging and no extensive if/else logic at numerous places. I therefore focused on making the user experience as convenient as possible. Users only need to specify "reader='lsasaf_mtgfrp_l2' " Satpy then determines from the filenames whether the NetCDF or CSV reader should be used.
There was a problem hiding this comment.
Unless I'm missing something there doesn't need to be any if/else logic. I'm not talking about merging them into one class. The two classes go in one module. One class gets called FRPCSVFileHandler and the other FRPNetCDFFileHandler and the YAML references those. The variable map global variables get a CSV or NC specific prefix to them. The chunk size and FRP shape variables don't even need to change. The CSV file handler is less than 100 lines of code and the NC reader is ~150. It is very common to have multiple file handlers in a single reader module and it is expected to be that way.
There was a problem hiding this comment.
Gotcha, thanks for clarifying. That sounds reasonable. I will change and let you know.
djhoese
left a comment
There was a problem hiding this comment.
I thought I had only a few questions so I made individual comments and then I just kept having more and more to say. Nice work on this. I just have a few questions/suggestions so far.
ameraner
left a comment
There was a problem hiding this comment.
Thank you Alex for adding the NetCDF reader too!
For better flexibility, I would propose to add the with_area_definition mechanism also to this reader, see the equivalent LI L2 implementation
satpy/satpy/readers/li_l2_nc.py
Lines 47 to 51 in 73ae7e4
True, same as for LIL2 satpy/satpy/readers/li_l2_nc.py
Line 81 in 73ae7e4
That way, by default (the current behaviour) you get a 2-d array that can be used as a mask etc. . But if requested through the reader_kwargs, a user can get just the 1-d lists of lat, lon and FRP values. If you still have time to work on this, otherwise it can be added later!
Summary
This PR adds tests for the FCI LSA SAF FRP L2 CSV and NC reader.
Including yaml-, utest-, composite-, and enhancement-files
Work finished, based on PWC Spring 2026 Issue - Closes #3350
Testing