66Source
77 v2020:
88 https://www.bgc-jena.mpg.de/CarboScope/?ID=sEXTocNEET_v2020
9- v2024 :
10- https://www.bgc-jena.mpg.de/CarboScope/?file=nbetEXToc_v2024E .flux.nc
9+ v2025 :
10+ https://www.bgc-jena.mpg.de/CarboScope/?file=nbetEXToc_v2025 .flux.nc
1111
1212Last access
13- 20240909
13+ 20260716
1414
1515Download and processing instructions
1616 Download the file corresponding to the version you require:
1717 v2020: 'sEXTocNEET_v2020_daily.nc.gz'.
18- v2024: nbetEXToc_v2024E .flux.nc
18+ v2025: nbetEXToc_v2025 .flux.nc
1919
2020"""
2121
2828
2929import dask .array as da
3030import iris
31- from iris import NameConstraint
3231from cf_units import Unit
33-
3432from esmvalcore .preprocessor import monthly_statistics
33+ from iris import NameConstraint
3534
3635from esmvaltool .cmorizers .data import utilities as utils
3736
@@ -47,29 +46,31 @@ def _clean(filepath):
4746
4847def _calculate_flux (cube , filename , area_type ):
4948 """Calculate flux (dividing by land/sea area) and mask land/sea."""
50- if area_type == ' land' :
49+ if area_type == " land" :
5150 area_idx = 0
52- elif area_type == ' ocean' :
51+ elif area_type == " ocean" :
5352 area_idx = 1
5453 else :
5554 raise ValueError (
56- f"Expected 'land' or 'ocean' for 'area_type', got '{ area_type } '" )
55+ f"Expected 'land' or 'ocean' for 'area_type', got '{ area_type } '"
56+ )
5757
5858 # Get land/sea area fraction
5959 with warnings .catch_warnings ():
6060 warnings .filterwarnings (
61- ' ignore' ,
61+ " ignore" ,
6262 message = "Ignoring netCDF variable '.*?' invalid units '.*?'" ,
6363 category = UserWarning ,
64- module = 'iris' ,
64+ module = "iris" ,
65+ )
66+ area_cube = iris .load_cube (
67+ str (filename ), NameConstraint (var_name = "area" )
6568 )
66- area_cube = iris .load_cube (str (filename ),
67- NameConstraint (var_name = 'area' ))
6869 area = area_cube [area_idx ].core_data ()
6970 area = da .broadcast_to (area , cube .shape )
7071
7172 # Mask
72- mask = ( area == 0.0 )
73+ mask = area == 0.0
7374 area = da .ma .masked_array (area , mask = mask )
7475 cube .data = da .ma .masked_array (cube .core_data (), mask = mask )
7576
@@ -79,7 +80,7 @@ def _calculate_flux(cube, filename, area_type):
7980
8081 # Adapt metadata
8182 cube .units /= area_cube .units
82- cube .attributes [' positive' ] = ' down'
83+ cube .attributes [" positive" ] = " down"
8384
8485 return cube
8586
@@ -89,78 +90,85 @@ def _load_cube(filename, raw_name):
8990 logger .info ("Loading '%s' from file %s" , raw_name , filename )
9091 with warnings .catch_warnings ():
9192 warnings .filterwarnings (
92- ' ignore' ,
93+ " ignore" ,
9394 message = "Ignoring netCDF variable '.*?' invalid units '.*?'" ,
9495 category = UserWarning ,
95- module = ' iris' ,
96+ module = " iris" ,
9697 )
97- cube = iris .load_cube (str (filename ),
98- NameConstraint (var_name = raw_name ))
98+ cube = iris .load_cube (str (filename ), NameConstraint (var_name = raw_name ))
9999 return cube
100100
101101
102102def _time_operations (cube ):
103103 """Temporal operations on cube."""
104- valid_start_year = cube .attributes ['yrfi_valid' ]
105- valid_end_year = cube .attributes ['yrfe_valid' ]
106- logger .info ("Extracting valid years: %d-%d" , valid_start_year ,
107- valid_end_year )
108- cube = cube .extract (iris .Constraint (
109- time = lambda cell : valid_start_year <= cell .point .year <= valid_end_year
110- ))
104+ valid_start_year = cube .attributes ["yrfi_valid" ]
105+ valid_end_year = cube .attributes ["yrfe_valid" ]
106+ logger .info (
107+ "Extracting valid years: %d-%d" , valid_start_year , valid_end_year
108+ )
109+ cube = cube .extract (
110+ iris .Constraint (
111+ time = lambda cell : valid_start_year
112+ <= cell .point .year
113+ <= valid_end_year
114+ )
115+ )
111116 logger .info ("Calculating monthly means" )
112117 cube = monthly_statistics (cube )
113118 return cube
114119
115120
116121def _fix_metadata (cube , short_name , var , cfg ):
117122 """Fix metadata of cube."""
118- cmor_info = cfg [' cmor_table' ].get_variable (var [' mip' ], short_name )
123+ cmor_info = cfg [" cmor_table" ].get_variable (var [" mip" ], short_name )
119124
120125 # Fix cell measures
121- if 'fx' not in var [' mip' ]:
122- cell_measure = cube .cell_measure (' area per grid cell' )
126+ if "fx" not in var [" mip" ]:
127+ cell_measure = cube .cell_measure (" area per grid cell" )
123128 cell_measure .attributes = {}
124- cell_measure .standard_name = 'cell_area'
125- if short_name == 'nbp' :
126- cell_measure .var_name = 'areacella'
127- cell_measure .long_name = ('Grid-Cell Area for Atmospheric Grid '
128- 'Variables' )
129- elif short_name == 'fgco2' :
130- cell_measure .var_name = 'areacello'
131- cell_measure .long_name = 'Grid-Cell Area for Ocean Variables'
129+ cell_measure .standard_name = "cell_area"
130+ if short_name == "nbp" :
131+ cell_measure .var_name = "areacella"
132+ cell_measure .long_name = (
133+ "Grid-Cell Area for Atmospheric Grid Variables"
134+ )
135+ elif short_name == "fgco2" :
136+ cell_measure .var_name = "areacello"
137+ cell_measure .long_name = "Grid-Cell Area for Ocean Variables"
132138
133139 # Fix cell methods
134- if short_name == 'nbp' :
135- cube .add_cell_method (iris .coords .CellMethod ('mean where land' ,
136- coords = 'area' ))
137- elif short_name == 'fgco2' :
138- cube .add_cell_method (iris .coords .CellMethod ('mean where sea' ,
139- coords = 'area' ))
140- elif short_name in ('areacella' , 'areacello' ):
141- cube .add_cell_method (iris .coords .CellMethod ('sum' , coords = 'area' ))
142- elif short_name in ('sftlf' , 'sftof' ):
143- cube .add_cell_method (iris .coords .CellMethod ('mean' , coords = 'area' ))
140+ if short_name == "nbp" :
141+ cube .add_cell_method (
142+ iris .coords .CellMethod ("mean where land" , coords = "area" )
143+ )
144+ elif short_name == "fgco2" :
145+ cube .add_cell_method (
146+ iris .coords .CellMethod ("mean where sea" , coords = "area" )
147+ )
148+ elif short_name in ("areacella" , "areacello" ):
149+ cube .add_cell_method (iris .coords .CellMethod ("sum" , coords = "area" ))
150+ elif short_name in ("sftlf" , "sftof" ):
151+ cube .add_cell_method (iris .coords .CellMethod ("mean" , coords = "area" ))
144152
145153 # Fix coordinates
146- if 'fx' not in var [' mip' ]:
147- cube .remove_coord ( 'month_number' )
148- cube .remove_coord ( 'year' )
149- cube .coord (' time' ). var_name = 'time'
150- cube .coord (' time' ). long_name = 'time'
151- cube . coord ( 'time' ). points = da . around (
152- cube .coord (' time' ). core_points (), 3 )
153- cube .coord (' time' ). bounds = da . around (
154- cube . coord ( 'time' ). core_bounds (), 3 )
154+ if "fx" not in var [" mip" ]:
155+ cube .coord ( "time" ). var_name = "time"
156+ cube .coord ( "time" ). long_name = "time"
157+ cube .coord (" time" ). points = da . around (
158+ cube .coord (" time" ). core_points (), 3
159+ )
160+ cube .coord (" time" ). bounds = da . around (
161+ cube .coord (" time" ). core_bounds (), 3
162+ )
155163 utils .fix_coords (cube )
156- if ' depth0m' in cmor_info .dimensions :
164+ if " depth0m" in cmor_info .dimensions :
157165 depth_coord = iris .coords .AuxCoord (
158166 0.0 ,
159- var_name = ' depth' ,
160- standard_name = ' depth' ,
161- long_name = ' depth' ,
162- units = Unit ('m' ),
163- attributes = {' positive' : ' down' },
167+ var_name = " depth" ,
168+ standard_name = " depth" ,
169+ long_name = " depth" ,
170+ units = Unit ("m" ),
171+ attributes = {" positive" : " down" },
164172 )
165173 cube .add_aux_coord (depth_coord , ())
166174
@@ -169,82 +177,80 @@ def _fix_metadata(cube, short_name, var, cfg):
169177 utils .fix_var_metadata (cube , cmor_info )
170178
171179 # Fix global attributes
172- attrs = cfg [' attributes' ]
173- attrs [' mip' ] = var [' mip' ]
180+ attrs = cfg [" attributes" ]
181+ attrs [" mip" ] = var [" mip" ]
174182 utils .set_global_atts (cube , attrs )
175183
176184 return (cube , attrs )
177185
178186
179187def _extract_variable (short_name , var , cfg , filename , out_dir ):
180188 """Extract variable."""
181- raw_name = var .get (' raw_name' , short_name )
189+ raw_name = var .get (" raw_name" , short_name )
182190 cube = _load_cube (filename , raw_name )
183191
184192 # Fix metadata of raw input
185193 cube .cell_methods = ()
186- if ' raw_units' in var :
187- cube .units = var [' raw_units' ]
188- cube .attributes .pop (' invalid_units' , None )
194+ if " raw_units" in var :
195+ cube .units = var [" raw_units" ]
196+ cube .attributes .pop (" invalid_units" , None )
189197
190198 # Temporal operations
191- if 'fx' not in var [' mip' ]:
199+ if "fx" not in var [" mip" ]:
192200 cube = _time_operations (cube )
193201
194202 # Variable-specific calculations
195- if short_name == ' nbp' :
196- cube = _calculate_flux (cube , filename , ' land' )
197- elif short_name == ' fgco2' :
198- cube = _calculate_flux (cube , filename , ' ocean' )
199- elif short_name in (' areacella' , ' areacello' ):
203+ if short_name == " nbp" :
204+ cube = _calculate_flux (cube , filename , " land" )
205+ elif short_name == " fgco2" :
206+ cube = _calculate_flux (cube , filename , " ocean" )
207+ elif short_name in (" areacella" , " areacello" ):
200208 cube = cube [2 ]
201- cube .remove_coord ('rt' )
202- elif short_name in (' sftlf' , ' sftof' ):
209+ cube .remove_coord ("rt" )
210+ elif short_name in (" sftlf" , " sftof" ):
203211 total_area = cube [2 ]
204- area_idx = 0 if short_name == ' sftlf' else 1
212+ area_idx = 0 if short_name == " sftlf" else 1
205213 cube = cube [area_idx ]
206214 cube .data = cube .core_data () / total_area .core_data ()
207- cube .units = '1'
208- cube .remove_coord ('rt' )
215+ cube .units = "1"
216+ cube .remove_coord ("rt" )
209217
210218 # Fix metadata
211219 (cube , attrs ) = _fix_metadata (cube , short_name , var , cfg )
212220
213221 # Save variable
214- utils .save_variable (cube ,
215- short_name ,
216- out_dir ,
217- attrs ,
218- unlimited_dimensions = ['time' ])
222+ utils .save_variable (
223+ cube , short_name , out_dir , attrs , unlimited_dimensions = ["time" ]
224+ )
219225
220226
221227def _unzip (zip_path , out_dir ):
222228 """Unzip `*.gz` file."""
223229 logger .info ("Found input file '%s'" , zip_path )
224- unzip_path = Path (out_dir ) / zip_path .with_suffix ('' ).name
230+ unzip_path = Path (out_dir ) / zip_path .with_suffix ("" ).name
225231 logger .info ("Unzipping file" )
226- with gzip .open (zip_path , 'rb' ) as zip_file :
227- with open (unzip_path , 'wb' ) as unzip_file :
232+ with gzip .open (zip_path , "rb" ) as zip_file :
233+ with open (unzip_path , "wb" ) as unzip_file :
228234 shutil .copyfileobj (zip_file , unzip_file )
229235 logger .info ("Succefully extracted file to %s" , unzip_path )
230236 return unzip_path
231237
232238
233239def cmorization (in_dir , out_dir , cfg , cfg_user , start_date , end_date ):
234240 """Cmorization func call."""
235- in_path = Path (in_dir ) / cfg [' filename' ]
241+ in_path = Path (in_dir ) / cfg [" filename" ]
236242
237243 # Unzip file if necessary
238- if ' .gz' in cfg [' filename' ]:
244+ if " .gz" in cfg [" filename" ]:
239245 data_path = _unzip (in_path , out_dir )
240246 else :
241247 data_path = in_path
242248
243249 # Run the cmorization
244- for ( short_name , var ) in cfg [' variables' ].items ():
250+ for short_name , var in cfg [" variables" ].items ():
245251 logger .info ("CMORizing variable '%s'" , short_name )
246252 _extract_variable (short_name , var , cfg , data_path , out_dir )
247253
248254 # Remove unzipped file if necessary
249- if ' .gz' in cfg [' filename' ]:
255+ if " .gz" in cfg [" filename" ]:
250256 _clean (data_path )
0 commit comments