5.13.2-dev0
mHM
The mesoscale Hydrological Model
Loading...
Searching...
No Matches
mo_nc_output.f90
Go to the documentation of this file.
1!> \file mo_nc_output.f90
2!> \brief \copybrief mo_nc_output
3!> \details \copydetails mo_nc_output
4
5!> \brief Creates NetCDF output for different fluxes and state variables of mHM.
6!> \details NetCDF is first initialized and later on variables are put to the NetCDF.
7!> \changelog
8!! - David Schaefer Aug 2015
9!! - major rewrite
10!! - Stephan Thober Oct 2015
11!! - adapted to mRM
12!! - O. Rakovec, R. Kumar Nov 2017
13!! - added project description for the netcdf outputs
14!! - S. Mueller, Dec 2022
15!! - unified module for mHM and mRM
16!> \authors Matthias Zink
17!> \date Apr 2013
18!> \copyright Copyright 2005-\today, the mHM Developers, Luis Samaniego, Sabine Attinger: All rights reserved.
19!! mHM is released under the LGPLv3+ license \license_note
20!> \ingroup f_common
22
23 use mo_kind, only : i4, dp, sp
24 use mo_common_types, only: grid
27 use mo_file, only : version
29 use mo_netcdf, only : ncdataset, ncdimension, ncvariable
31
32 implicit none
33
35
36 private
37
39 type(ncvariable) :: nc !< NcDataset which contains the variable
40 logical :: avg = .false. !< average data before writing
41 logical, pointer :: mask(:, :) !< mask to reconstruct data
42 real(dp), allocatable :: data(:) !< store the data between writes
43 integer(i4) :: counter = 0 !< count the number of updateVariable calls
44
45 contains
46 procedure, public :: updatevariable
47 procedure, public :: writevariabletimestep
48
49 end type outputvariable
50
51 ! constructor interface
52 interface outputvariable
53 procedure newoutputvariable
54 end interface outputvariable
55
57 integer(i4) :: idomain !< domain id
58 type(ncdataset) :: nc !< NcDataset to write
59 type(outputvariable), allocatable :: vars(:) !< store all created (dynamic) variables
60 integer(i4) :: counter !< count written time steps
61 integer(i4) :: previous_time !< previous time steps for bounds
62 integer(i4) :: time_unit_factor !< possible factor to convert hours to minutes when using center as time reference
63 integer(i4) :: outputs_frequence !< write out frequence (-3: yearly, -2: monthly, -1: daily, 0: end of run, >0: after n steps)
64 integer(i4) :: time_reference !< time stamp reference (0: begin, 1: center, 2: end of time interval)
65 logical :: double_precision !< output precision switch for nc files
66
67 contains
68 procedure, public :: writetimestep
69 procedure, public :: close
70
71 end type outputdataset
72
73 ! constructor interface
74 interface outputdataset
75 procedure newoutputdataset
76 end interface outputdataset
77
78contains
79
80 !> \brief Output variable dtype for single or double precision.
81 !> \return "f64" or "f32"
82 character(3) function data_dtype(double_precision)
83 implicit none
84 logical, intent(in) :: double_precision !< flag to use double precision
85 if ( double_precision ) then
86 data_dtype = "f64"
87 else
88 data_dtype = "f32"
89 end if
90 end function data_dtype
91
92 !> \brief Output variable dimension names.
93 !> \return (X, Y, T) names tuple
94 function data_dims()
95 implicit none
96 character(16), dimension(3) :: data_dims
97 if (iflag_cordinate_sys == 0) then
98 data_dims = (/"easting ", "northing", "time "/) ! X & Y coordinate system
99 else
100 data_dims = (/"lon ", "lat ", "time"/) ! lat & lon coordinate system
101 endif
102 end function data_dims
103
104 !> \brief Initialize OutputVariable
105 !> \details Modifications:
106 !! - David Schaefer Nov 2017 - added NcVariable initialization
107 !! - Robert Schweppe Jun 2018 - refactoring and reformatting
108 !> \return type(OutputVariable)
109 !> \authors David Schaefer
110 !> \date June 2015
111 function newoutputvariable(nc, name, dtype, dims, ncells, mask, deflate_level, avg) result(out)
112 implicit none
113
114 type(ncdataset), intent(in) :: nc !< NcDataset which contains the variable
115 character(*), intent(in) :: name !< name of the variable
116 character(*), intent(in) :: dtype !< data type of the variable
117 character(16), intent(in), dimension(3) :: dims !< dimensions of the variable (by name)
118 integer(i4), intent(in) :: ncells !< number of cells in domain
119 logical, intent(in), target, dimension(:, :) :: mask !< mask of the variable
120 integer(i4), intent(in) :: deflate_level !< deflate level for compression
121 logical, intent(in), optional :: avg !< flag to average the data before writing
122
123 type(outputvariable) :: out
124
125 allocate(out%data(ncells))
126 out%nc = nc%setVariable(name, dtype, dims, deflate_level = deflate_level, shuffle = .true.)
127 out%data = 0
128 out%mask => mask
129 if (present(avg)) out%avg = avg
130 end function newoutputvariable
131
132 !> \brief Update OutputVariable
133 !> \details Add the array given as actual argument to the derived type's component 'data'
134 !> \changelog
135 !! - Robert Schweppe Jun 2018
136 !! - refactoring and reformatting
137 !> \return type(OutputVariable)
138 !> \authors David Schaefer
139 !> \date June 2015
140 subroutine updatevariable(self, data)
141 implicit none
142
143 class(outputvariable), intent(inout) :: self
144 real(dp), intent(in), dimension(:) :: data !< data for current time step
145
146 self%data = self%data + data
147 self%counter = self%counter + 1
148
149 end subroutine updatevariable
150
151 !> \brief Write timestep to file
152 !> \details Write the content of the derived types's component 'data' to file, average if necessary
153 !> \changelog
154 !! - Robert Schweppe Jun 2018
155 !! - refactoring and reformatting
156 !> \authors David Schafer
157 !> \date June 2015
158 subroutine writevariabletimestep(self, current_time_step)
159 implicit none
160
161 class(outputvariable), intent(inout) :: self
162
163 !> index along the time dimension of the netcdf variable
164 integer(i4), intent(in) :: current_time_step
165
166 if (self%avg) then
167 self%data = self%data / real(self%counter, dp)
168 end if
169
170 call self%nc%setData(unpack(self%data, self%mask, nodata_dp), &
171 (/1, 1, current_time_step/))
172 self%data = 0
173 self%counter = 0
174
175 end subroutine writevariabletimestep
176
177 !> \brief Initialize OutputDataset
178 !> \details Create and initialize the output file. If new a new output
179 !! variable needs to be written, this is the first of two
180 !! procedures to change (second: updateDataset)
181 !> \changelog
182 !! - Robert Schweppe Jun 2018
183 !! - refactoring and reformatting
184 !! - Sebastian Mueller Jul 2020
185 !! - added output for river temperature
186 !> \return type(OutputDataset)
187 !> \authors Matthias Zink
188 !> \date Apr 2013
189 function newoutputdataset( iDomain, level, file_name, double_precision, outputs_frequence, time_reference ) result(out)
190 implicit none
191
192 integer(i4), intent(in) :: idomain !< domain id
193 type(grid), dimension(:), allocatable, target, intent(in) :: level !< level definitions for all domains
194 character(*), intent(in) :: file_name !< long name of the variable
195 logical, intent(in) :: double_precision !< mask on desired level
196 integer(i4), intent(in) :: outputs_frequence !< write out frequence (-3, -2, -1, 0, >0)
197 integer(i4), intent(in) :: time_reference !< time stamp reference (0: begin, 1: center, 2: end of time interval)
198
199 type(outputdataset) :: out
200
201 out%nc = createoutputfile(idomain, level, file_name, double_precision, outputs_frequence, time_reference)
202 out%iDomain = idomain
203
204 ! check if we need minutes instead of hours as time unit and set the time unit factor accordingly
205 if ( (outputs_frequence > 0) &
206 .and. (mod(timestep * outputs_frequence, 2) == 1) &
207 .and. (time_reference == 1) &
208 ) then
209 out%time_unit_factor = 60
210 else
211 out%time_unit_factor = 1
212 end if
213 out%outputs_frequence = outputs_frequence
214 out%time_reference = time_reference
215 out%double_precision = double_precision
216 out%previous_time = 0
217 out%counter = 0
218
219 end function newoutputdataset
220
221 !> \brief Write all accumulated data.
222 !> \details Write all accumulated and potentially averaged data to disk.
223 !> \changelog
224 !! - Robert Schweppe Jun 2018
225 !! - refactoring and reformatting
226 !> \authors David Schaefer
227 !> \date June 2015
228 subroutine writetimestep(self, current_time_step)
229 implicit none
230
231 class(outputdataset), intent(inout), target :: self
232
233 !> The model timestep to write
234 integer(i4), intent(in) :: current_time_step
235
236 integer(i4) :: ii
237
238 type(ncvariable) :: tvar
239
240 self%counter = self%counter + 1
241
242 ! add to time variable
243 tvar = self%nc%getVariable("time")
244 select case( self%time_reference )
245 case(0)
246 call tvar%setData(self%previous_time * self%time_unit_factor, (/self%counter/))
247 case(1)
248 call tvar%setData((self%previous_time + current_time_step) * self%time_unit_factor / 2, (/self%counter/))
249 case(2)
250 call tvar%setData(current_time_step * self%time_unit_factor, (/self%counter/))
251 end select
252 ! add bounds (with current time at end)
253 tvar = self%nc%getVariable("time_bnds")
254 call tvar%setData(self%previous_time * self%time_unit_factor, (/1, self%counter/))
255 call tvar%setData(current_time_step * self%time_unit_factor, (/2, self%counter/))
256 self%previous_time = current_time_step
257
258 do ii = 1, size(self%vars)
259 call self%vars(ii)%writeVariableTimestep(self%counter)
260 end do
261
262 end subroutine writetimestep
263
264 !> \brief Close the file
265 !> \details Close the file associated with variable of type(OutputDataset)
266 !> \changelog
267 !! - Stephan Thober Oct 2015
268 !! - adapted to mRM
269 !! - Robert Schweppe Jun 2018
270 !! - refactoring and reformatting
271 !> \authors Rohini Kumar & Stephan Thober
272 !> \date August 2013
273 subroutine close(self)
274
275 use mo_string_utils, only : num2str
276 use mo_message, only : message
277
278 implicit none
279
280 class(outputdataset) :: self
281
282 call self%nc%close()
283 call message(' OUTPUT: saved netCDF file for domain', trim(num2str(self%iDomain)))
284 call message(' to ', trim(self%nc%fname))
285
286 end subroutine close
287
288 !> \brief Create and initialize output file for X & Y coordinate system
289 !> \details Create output file, write all non-dynamic variables
290 !! and global attributes for the given domain for X & Y coordinate system
291 !> \changelog
292 !! - Stephan Thober Oct 2015
293 !! - adapted to mRM
294 !! - Robert Schweppe Jun 2018
295 !! - refactoring and reformatting
296 !! - Pallav Shrestha Mar 2020
297 !! - output file lat and lon are 1d or 2d based on coordinate system
298 !> \return type(NcDataset)
299 !> \authors David Schaefer
300 !> \date June 2015
301 function createoutputfile(iDomain, level, file_name, double_precision, outputs_frequence, time_reference) result(nc)
302
305 use mo_julian, only : dec2date
306
307 implicit none
308
309 ! -> domain id
310 integer(i4), intent(in) :: idomain !< selected domain
311 type(grid), dimension(:), allocatable, intent(in) :: level !< level definitions for all domains
312 character(*), intent(in) :: file_name !< long name of the variable
313 logical, intent(in) :: double_precision !< mask on desired level
314 integer(i4), intent(in) :: outputs_frequence !< write out frequence (-3, -2, -1, 0, >0)
315 integer(i4), intent(in) :: time_reference !< time stamp reference (0: begin, 1: center, 2: end of time interval)
316
317 type(ncdataset) :: nc
318 type(ncdimension), dimension(4) :: dimids1
319 type(ncvariable) :: var
320 integer(i4) :: day, month, year
321 character(1028) :: fname
322 character(128) :: unit, date, time, datetime
323 real(dp), allocatable, dimension(:) :: easting, northing
324 real(dp), allocatable, dimension(:, :) :: x_bnds, y_bnds
325 real(dp) :: half_step
326 real(dp), allocatable, dimension(:) :: lat1d, lon1d ! 1D lat lon vectors. Used if coordinate system is lat & lon
327 real(dp), allocatable, dimension(:, :) :: lat2d, lon2d ! temporary storage of mHM's 2D latlon array.
328 ! Used as 2d lat lon arrays if coordinate system is X & Y
329 character(3) :: dtype
330
331 dtype = data_dtype(double_precision)
332
333 ! half cell step to calculate cell bounds from center
334 half_step = level(idomain)%cellsize / 2.0_dp
335
336 fname = trim(dirout(idomain)) // trim(file_name)
337 call geocoordinates(level(idomain), lat2d, lon2d)
338
339 nc = ncdataset(trim(fname), "w")
340
341 ! set the horizonal dimensions
342 if (iflag_cordinate_sys == 0) then
343
344 ! X & Y coordinate system; 2D lat lon!
345 !============================================================
346 call mapcoordinates(level(idomain), northing, easting)
347 allocate(x_bnds(2, size(easting)))
348 allocate(y_bnds(2, size(northing)))
349 x_bnds(1, :) = easting - half_step
350 x_bnds(2, :) = easting + half_step
351 y_bnds(1, :) = northing - half_step
352 y_bnds(2, :) = northing + half_step
353
354 dimids1 = (/&
355 nc%setDimension("easting", size(easting)), &
356 nc%setDimension("northing", size(northing)), &
357 nc%setDimension("time", 0), &
358 nc%setDimension("bnds", 2) &
359 /)
360 ! easting
361 var = nc%setVariable("easting", dtype, (/ dimids1(1) /))
362 call set_attributes(var, "x-coordinate in the given coordinate system", &
363 unit="m", standard_name="projection_x_coordinate", add_coords=.false., axis="X", bounds="easting_bnds")
364 call var%setData(easting)
365 var = nc%setVariable("easting_bnds", dtype, (/ dimids1(4), dimids1(1) /))
366 call var%setData(x_bnds)
367 ! northing
368 var = nc%setVariable("northing", dtype, (/ dimids1(2) /))
369 call set_attributes(var, "y-coordinate in the given coordinate system", &
370 unit="m", standard_name="projection_y_coordinate", add_coords=.false., axis="Y", bounds="northing_bnds")
371 call var%setData(northing)
372 var = nc%setVariable("northing_bnds", dtype, (/ dimids1(4), dimids1(2) /))
373 call var%setData(y_bnds)
374 ! lon
375 var = nc%setVariable("lon", dtype, dimids1(1 : 2))
376 call set_attributes(var, "longitude", unit="degrees_east", &
377 double_precision=double_precision, add_coords=.false., standard_name="longitude")
378 call var%setData(lon2d)
379 ! lat
380 var = nc%setVariable("lat", dtype, dimids1(1 : 2))
381 call set_attributes(var, "latitude", unit="degrees_north", &
382 double_precision=double_precision, add_coords=.false., standard_name="latitude")
383 call var%setData(lat2d)
384
385 else
386
387 ! lat & lon coordinate system; 1D lat lon!
388 !============================================================
389 lon1d = lon2d(:, 1) ! first column info is sufficient
390 lat1d = lat2d(1, :) ! first row info is sufficient
391 allocate(x_bnds(2, size(lon1d)))
392 allocate(y_bnds(2, size(lat1d)))
393 ! cellsize is given in degree in case of lat-lon coordinates
394 x_bnds(1, :) = lon1d - half_step
395 x_bnds(2, :) = lon1d + half_step
396 y_bnds(1, :) = lat1d - half_step
397 y_bnds(2, :) = lat1d + half_step
398
399 dimids1 = (/&
400 nc%setDimension("lon", size(lon1d)), &
401 nc%setDimension("lat", size(lat1d)), &
402 nc%setDimension("time", 0), &
403 nc%setDimension("bnds", 2) &
404 /)
405 ! lon
406 var = nc%setVariable("lon", dtype, (/ dimids1(1) /)) ! sufficient to store lon as vector
407 call set_attributes(var, "longitude", unit="degrees_east", &
408 add_coords=.false., standard_name="longitude", axis="X", bounds="lon_bnds")
409 call var%setData(lon1d)
410 var = nc%setVariable("lon_bnds", dtype, (/ dimids1(4), dimids1(1) /))
411 call var%setData(x_bnds)
412 ! lat
413 var = nc%setVariable("lat", dtype, (/ dimids1(2) /)) ! sufficient to store lat as vector
414 call set_attributes(var, "latitude", unit="degrees_north", &
415 add_coords=.false., standard_name="latitude", axis="Y", bounds="lat_bnds")
416 call var%setData(lat1d)
417 var = nc%setVariable("lat_bnds", dtype, (/ dimids1(4), dimids1(2) /))
418 call var%setData(y_bnds)
419
420 endif
421
422 ! set record dimension
423 ! time units
424 call dec2date(real(evalper(idomain)%julStart, dp), dd = day, mm = month, yy = year)
425
426 ! check if we need minutes instead of hours as time unit
427 if ( (outputs_frequence > 0) & ! only for output after n steps
428 .and. (mod(timestep * outputs_frequence, 2) == 1) & ! only for uneven hours
429 .and. (time_reference == 1) & ! only when center of time span is used for time stamp
430 ) then
431 write(unit, "('minutes since ', i4, '-' ,i2.2, '-', i2.2, 1x, '00:00:00')") year, month, day
432 else
433 write(unit, "('hours since ', i4, '-' ,i2.2, '-', i2.2, 1x, '00:00:00')") year, month, day
434 end if
435
436 ! time
437 var = nc%setVariable("time", "i32", (/ dimids1(3) /))
438 call set_attributes(var, "time", unit=unit, add_coords=.false., standard_name="time", axis="T", bounds="time_bnds")
439 var = nc%setVariable("time_bnds", "i32", (/ dimids1(4), dimids1(3) /))
440
441 ! global attributes
442 call date_and_time(date = date, time = time)
443 write(datetime, "(a4,'-',a2,'-',a2,1x,a2,':',a2,':',a2)") date(1 : 4), &
444 date(5 : 6), date(7 : 8), time(1 : 2), time(3 : 4), time(5 : 6)
445
446 call nc%setAttribute("project", project_details)
447 call nc%setAttribute("setup_description", setup_description)
448 call nc%setAttribute("simulation_type", simulation_type)
449 call nc%setAttribute("Conventions", conventions)
450 call nc%setAttribute("contact", contact)
451 call nc%setAttribute("mHM_details", trim(mhm_details) // ", release mHMv" // trim(version))
452 call nc%setAttribute("history", trim(datetime) // ", " // history)
453 call nc%setAttribute("title", "mHMv"//trim(version)//" "//trim(simulation_type)//" outputs")
454 call nc%setAttribute("creation_date", datetime)
455
456 end function createoutputfile
457
458 !> \brief Write output variable attributes
459 !> \details Modifications:
460 !! - Robert Schweppe Jun 2018 - refactoring and reformatting
461 !> \authors David Schaefer
462 !> \date June 2015
463 subroutine set_attributes(var, long_name, unit, double_precision, add_coords, standard_name, axis, bounds)
464 implicit none
465
466 type(ncvariable), intent(inout) :: var !< NetCDF variable
467 character(*), intent(in), optional :: long_name !< long name of the variable
468 character(*), intent(in), optional :: unit !< unit of the variable
469 logical, intent(in), optional :: double_precision !< precision flag, if missing, no fill-value and missing-value is written
470 logical, intent(in), optional :: add_coords !< whether to add the "coordiantes" attribute "lat lon", .true. by defult
471 character(*), intent(in), optional :: standard_name !< standard name of the variable
472 character(*), intent(in), optional :: axis !< axis attribute
473 character(*), intent(in), optional :: bounds !< bounds attribute
474
475 logical :: add_coords_
476
477 add_coords_ = .true.
478 if (present(add_coords)) add_coords_ = add_coords
479
480 if (present(double_precision)) then
481 if (double_precision) then
482 call var%setFillValue(nodata_dp)
483 call var%setAttribute("missing_value", nodata_dp)
484 ! call var%setAttribute("scale_factor", 1.0_dp) ! not needed if just 1
485 else
486 call var%setFillValue(real(nodata_dp, kind=sp))
487 call var%setAttribute("missing_value", real(nodata_dp, kind=sp))
488 ! call var%setAttribute("scale_factor", 1.0_sp) ! not needed if just 1
489 end if
490 end if
491 if (present(long_name)) call var%setAttribute("long_name", long_name)
492 if (present(unit)) call var%setAttribute("units", unit)
493 if (add_coords_) call var%setAttribute("coordinates", "lat lon")
494 if (present(standard_name)) call var%setAttribute("standard_name", standard_name)
495 if (present(axis)) call var%setAttribute("axis", axis)
496 if (present(bounds)) call var%setAttribute("bounds", bounds)
497
498 end subroutine set_attributes
499
500end module mo_nc_output
Provides constants commonly used by mHM, mRM and MPR.
real(dp), parameter, public nodata_dp
Provides structures needed by mHM, mRM and/or mpr.
type(period), dimension(:), allocatable, public evalper
Provides common types needed by mHM, mRM and/or mpr.
Provides structures needed by mHM, mRM and/or mpr.
character(1024), public history
character(1024), public setup_description
character(1024), public project_details
character(1024), public contact
character(256), public conventions
character(1024), public simulation_type
character(256), dimension(:), allocatable, public dirout
integer(i4), public iflag_cordinate_sys
character(1024), public mhm_details
Provides file names and units for mHM.
Definition mo_file.F90:29
character(len=*), parameter version
Current mHM model version (will be set to )
Definition mo_file.F90:33
gridding tools
Definition mo_grid.f90:12
subroutine, public mapcoordinates(level, y, x)
Generate map coordinates.
Definition mo_grid.f90:371
subroutine, public geocoordinates(level, lat, lon)
Generate geographic coordinates.
Definition mo_grid.f90:434
Creates NetCDF output for different fluxes and state variables of mHM.
character(16) function, dimension(3), public data_dims()
Output variable dimension names.
character(3) function, public data_dtype(double_precision)
Output variable dtype for single or double precision.
subroutine updatevariable(self, data)
Update OutputVariable.
subroutine, public set_attributes(var, long_name, unit, double_precision, add_coords, standard_name, axis, bounds)
Write output variable attributes.
type(outputdataset) function newoutputdataset(idomain, level, file_name, double_precision, outputs_frequence, time_reference)
Initialize OutputDataset.
subroutine close(self)
Close the file.
type(ncdataset) function createoutputfile(idomain, level, file_name, double_precision, outputs_frequence, time_reference)
Create and initialize output file for X & Y coordinate system.
subroutine writetimestep(self, current_time_step)
Write all accumulated data.
subroutine writevariabletimestep(self, current_time_step)
Write timestep to file.
type(outputvariable) function newoutputvariable(nc, name, dtype, dims, ncells, mask, deflate_level, avg)
Initialize OutputVariable.