FMS  2024.03
Flexible Modeling System
astronomy.F90
1 !***********************************************************************
2 !* GNU Lesser General Public License
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* FMS is free software: you can redistribute it and/or modify it under
7 !* the terms of the GNU Lesser General Public License as published by
8 !* the Free Software Foundation, either version 3 of the License, or (at
9 !* your option) any later version.
10 !*
11 !* FMS is distributed in the hope that it will be useful, but WITHOUT
12 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 !* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 !* for more details.
15 !*
16 !* You should have received a copy of the GNU Lesser General Public
17 !* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
18 !***********************************************************************
19 !> @defgroup astronomy_mod astronomy_mod
20 !> @ingroup astronomy
21 !> @brief Provides astronomical variables for use
22 !! by other modules within fms. The only currently used interface is
23 !! for determination of astronomical values needed by the shortwave
24 !! radiation packages.
25 !> @author Fei Liu
26 
27 !> @addtogroup astronomy_mod
28 !> @{
29 module astronomy_mod
30 
31 
32  use fms_mod, only: fms_init, &
33  mpp_pe, mpp_root_pe, stdlog, &
36  fatal, note, warning
37  use time_manager_mod, only: time_type, set_time, get_time, &
38  get_date_julian, set_date_julian, &
41  operator(-), operator(+), &
42  operator( // ), operator(<)
43  use fms_io_utils_mod, only: get_data_type_string
44  use constants_mod, only: constants_init, pi
45  use mpp_mod, only: input_nml_file
46  use platform_mod, only: r4_kind, r8_kind, i4_kind, i8_kind
47 
48  !--------------------------------------------------------------------
49 
50  implicit none
51  private
52 
53  !---------------------------------------------------------------------
54  !----------- version number for this module --------------------------
55 
56 ! Include variable "version" to be written to log file.
57 #include<file_version.h>
58 
59 
60  !---------------------------------------------------------------------
61  !------- interfaces --------
62 
63  public &
69 
71  module procedure set_orbital_parameters_r4, set_orbital_parameters_r8
72  end interface set_orbital_parameters
73 
75  module procedure get_orbital_parameters_r4, get_orbital_parameters_r8
76  end interface get_orbital_parameters
77 
78  !> @}
79 
80  !> @brief Calculates solar information for the given location(lat & lon) and time
81  !!
82  !> ~~~~~~~~~~{.f90}
83  !! call diurnal_solar (lat, lon, time, cosz, fracday, rrsun, dt_time)
84  !! call diurnal_solar (lat, lon, gmt, time_since_ae, cosz, fracday, rrsun, dt)
85  !! ~~~~~~~~~~
86  !!
87  !! The first option (used in conjunction with time_manager_mod)
88  !! generates the real variables gmt and time_since_ae from the
89  !! time_type input, and then calls diurnal_solar with these real inputs.
90  !!
91  !! The time of day is set by
92  !! ~~~~~~~~~~{.f90}
93  !! real, intent(in) :: gmt
94  !! ~~~~~~~~~~
95  !! The time of year is set by
96  !! ~~~~~~~~~~{.f90}
97  !! real, intent(in) :: time_since_ae
98  !! ~~~~~~~~~~
99  !! with time_type input, both of these are extracted from
100  !! ~~~~~~~~~~{.f90}
101  !! type(time_type), intent(in) :: time
102  !! ~~~~~~~~~~
103  !!
104  !! Separate routines exist within this interface for scalar,
105  !! 1D or 2D input and output fields:
106  !!
107  !! ~~~~~~~~~~{.f90}
108  !! real, intent(in), dimension(:,:) :: lat, lon
109  !! real, intent(in), dimension(:) :: lat, lon
110  !! real, intent(in) :: lat, lon
111  !!
112  !! real, intent(out), dimension(:,:) :: cosz, fracday
113  !! real, intent(out), dimension(:) :: cosz, fracday
114  !! real, intent(out) :: cosz, fracday
115  !! ~~~~~~~~~~
116  !!
117  !! One may also average the output fields over the time interval
118  !! between gmt and gmt + dt by including the optional argument dt (or
119  !! dt_time). dt is measured in radians and must be less than pi
120  !! (1/2 day). This average is computed analytically, and should be
121  !! exact except for the fact that changes in earth-sun distance over
122  !! the time interval dt are ignored. In the context of a diurnal GCM,
123  !! this option should always be employed to insure that the total flux
124  !! at the top of the atmosphere is not modified by time truncation error.
125  !!
126  !! ~~~~~~~~~~{.f90}
127  !! real, intent(in), optional :: dt
128  !! type(time_type), optional :: dt_time
129  !! ~~~~~~~~~~
130  !!
131  !! @param [in] <lat> Latitudes of model grid points [radians]
132  !! @param [in] <lon> Longitudes of model grid points [radians]
133  !! @param [in] <gmt> Time of day at longitude 0.0; midnight = 0.0, one day = 2 * pi [radians]
134  !! @param [in] <time_since_ae> Time of year; autumnal equinox = 0.0, one year = 2 * pi [radians]
135  !! @param [in] <time> Time at which astronomical values are desired (time_type variable) [seconds, days]
136  !! @param [out] <cosz> Cosine of solar zenith angle, set to zero when entire period is in darkness [dimensionless]
137  !! @param [out] <fracday> Daylight fraction of time interval [dimensionless]
138  !! @param [out] <rrsun> Earth-Sun distance (r) relative to semi-major axis of orbital ellipse
139  !! (a):(a/r)**2 [dimensionless]
140  !! @param [in] <dt> OPTIONAL: Time interval after gmt over which the astronomical variables are to be
141  !! averaged. this produces averaged output rather than instantaneous. [radians], (1 day = 2 * pi)
142  !! @param [in] <dt_time> OPTIONAL: Time interval after gmt over which the astronomical variables are to be
143  !! averaged. this produces averaged output rather than instantaneous. time_type,
144  !! [days, seconds]
145  !! @param [in] <allow_negative_cosz> Allow negative values for cosz?
146  !! @param [out] <half_day_out> half_day_out
147  !> @ingroup astronomy_mod
148  interface diurnal_solar
149  module procedure diurnal_solar_2d_r4, diurnal_solar_2d_r8
150  module procedure diurnal_solar_1d_r4, diurnal_solar_1d_r8
151  module procedure diurnal_solar_0d_r4, diurnal_solar_0d_r8
152  module procedure diurnal_solar_cal_2d_r4, diurnal_solar_cal_2d_r8
153  module procedure diurnal_solar_cal_1d_r4, diurnal_solar_cal_1d_r8
154  module procedure diurnal_solar_cal_0d_r4, diurnal_solar_cal_0d_r8
155  end interface diurnal_solar
156 
157  !> @brief Calculates the daily mean solar information for a given time and latitude.
158  !!
159  !> ~~~~~~~~~~{.f90}
160  !! call daily_mean_solar (lat, time, cosz, fracday, rrsun)
161  !! call daily_mean_solar (lat, time_since_ae, cosz, fracday, rrsun)
162  !! call daily_mean_solar (lat, time, cosz, solar)
163  !! call daily_mean_solar (lat, time_since_ae, cosz, solar)
164  !! ~~~~~~~~~~
165  !!
166  !! The first option (used in conjunction with time_manager_mod)
167  !! generates the real variable time_since_ae from the time_type
168  !! input time, and then calls daily_mean_solar with this real input
169  !! (option 2). The third and fourth options correspond to the first
170  !! and second and are used with then spectral 2-layer model, where
171  !! only cosz and solar are desired as output. These routines generate
172  !! dummy arguments and then call option 2, where the calculation is done.
173  !!
174  !! The time of year is set by
175  !! ~~~~~~~~~~{.f90}
176  !! real, intent(in) :: time_since_ae
177  !! ~~~~~~~~~~
178  !! With time_type input, the time of year is extracted from
179  !! ~~~~~~~~~~{.f90}
180  !! type(time_type), intent(in) :: time
181  !! ~~~~~~~~~~
182  !!
183  !! Separate routines exist within this interface for scalar,
184  !! 1D or 2D input and output fields:
185  !!
186  !! ~~~~~~~~~~{.f90}
187  !! real, intent(in), dimension(:,:) :: lat
188  !! real, intent(in), dimension(:) :: lat
189  !! real, intent(in) :: lat
190  !!
191  !! real, intent(out), dimension(:,:) :: cosz, fracday
192  !! real, intent(out), dimension(:) :: cosz, fracday
193  !! real, intent(out) :: cosz, fracday
194  !! ~~~~~~~~~~
195  !!
196  !! @param [in] <lat> Latitudes of model grid points [radians]
197  !! @param [in] <time_since_ae> Time of year; autumnal equinox = 0.0, one year = 2 * pi [radians]
198  !! @param [in] <time> Time at which astronomical values are desired (time_type variable) [seconds, days]
199  !! @param [out] <cosz> Cosine of solar zenith angle, set to zero when entire period is in darkness [dimensionless]
200  !! @param [out] <fracday> Daylight fraction of time interval [dimensionless]
201  !! @param [out] <rrsun> Earth-Sun distance (r) relative to semi-major axis of orbital ellipse
202  !! (a):(a/r)**2 [dimensionless]
203  !! @param [out] <solar> shortwave flux factor: cosine of zenith angle * daylight fraction /
204  !! (earth-sun distance squared) [dimensionless]
205  !> @ingroup astronomy_mod
207  module procedure daily_mean_solar_2d_r4, daily_mean_solar_2d_r8
208  module procedure daily_mean_solar_1d_r4, daily_mean_solar_1d_r8
209  module procedure daily_mean_solar_2level_r4, daily_mean_solar_2level_r8
210  module procedure daily_mean_solar_0d_r4, daily_mean_solar_0d_r8
211  module procedure daily_mean_solar_cal_2d_r4, daily_mean_solar_cal_2d_r8
212  module procedure daily_mean_solar_cal_1d_r4, daily_mean_solar_cal_1d_r8
213  module procedure daily_mean_solar_cal_2level_r4, daily_mean_solar_cal_2level_r8
214  module procedure daily_mean_solar_cal_0d_r4, daily_mean_solar_cal_0d_r8
215  end interface daily_mean_solar
216 
217  !> Calculates the annual mean of solar information for a given latitude and time.
218  !!
219  !> ~~~~~~~~~~{.f90}
220  !! call annual_mean_solar (js, je, lat, cosz, solar, fracday, rrsun)
221  !! call annual_mean_solar (lat, cosz, solar)
222  !! ~~~~~~~~~~
223  !!
224  !! The second interface above is used by the spectral 2-layer model,
225  !! which requires only cosz and solar as output arguments, and which
226  !! makes this call during the initialization phase of the model.
227  !! Separate routines exist within this interface for 1D or 2D input
228  !! and output fields:
229  !!
230  !! ~~~~~~~~~~{.f90}
231  !! real, intent(in), dimension(:,:) :: lat
232  !! real, intent(in), dimension(:) :: lat
233  !!
234  !! real, intent(out), dimension(:,:) :: cosz, solar, fracday
235  !! real, intent(out), dimension(:) :: cosz, solar, fracday
236  !! ~~~~~~~~~~
237  !!
238  !! @param [in] <jst> Starting subdomain j indices of data in the physics wiondow being integrated
239  !! @param [in] <jnd> Ending subdomain j indices of data in the physics wiondow being integrated
240  !! @param [in] <lat> Latitudes of model grid points [radians]
241  !! @param [out] <cosz> cosz is the average over the year of the cosine of an effective zenith angle
242  !! that would produce the correct daily solar flux if the sun were fixed at that
243  !! single position for the period of daylight on the given day. in this average,
244  !! the daily mean effective cosz is weighted by the daily mean solar flux. [dimensionless]
245  !! @param [out] <solar> Normalized solar flux, averaged over the year, equal to the product of
246  !! fracday*cosz*rrsun [dimensionless]
247  !! @param [out] <fracday> Daylight fraction calculated so as to make the average flux (solar) equal to the
248  !! product of the flux-weighted avg cosz * this fracday * assumed annual mean avg
249  !! Earth-Sun distance of 1.0. [dimensionless]
250  !! @param [out] <rrsun> Annual mean Earth-Sun distance (r) relative to semi-major axis of orbital ellipse
251  !! (a):(a/r)**2 [dimensionless]
252  !> @ingroup astronomy_mod
254  module procedure annual_mean_solar_2d_r4, annual_mean_solar_2d_r8
255  module procedure annual_mean_solar_1d_r4, annual_mean_solar_1d_r8
256  module procedure annual_mean_solar_2level_r4, annual_mean_solar_2level_r8
257  end interface annual_mean_solar
258 
259  !> Gets the length of year for current calendar
260  !!
261  !> ~~~~~~~~~~{.f90}
262  !! call get_period (period)
263  !! ~~~~~~~~~~
264  !!
265  !! Separate routines exist within this interface for integer
266  !! and time_type output:
267  !!
268  !! ~~~~~~~~~~{.f90}
269  !! integer, intent(out) :: period
270  !! type(time_type), intent(out) :: period
271  !! ~~~~~~~~~~
272  !!
273  !! @param [out] <period_out> Length of year for calendar in use
274  !> @ingroup astronomy_mod
275  interface get_period
276  module procedure get_period_time_type, get_period_integer
277  end interface
278 
279  !> Sets the length of a year for the calendar in use
280  !!
281  !> ~~~~~~~~~~{.f90}
282  !! call set_period (period_in)
283  !! ~~~~~~~~~~
284  !!
285  !! Separate routines exist within this interface for integer
286  !! and time_type output:
287  !!
288  !! ~~~~~~~~~~{.f90}
289  !! integer, intent(out) :: period_in
290  !! type(time_type), intent(out) :: period_in
291  !! ~~~~~~~~~~
292  !!
293  !! @param [in] <period_in> Length of year for calendar in use
294  !> @ingroup astronomy_mod
295  interface set_period
296  module procedure set_period_time_type, set_period_integer
297  end interface
298 
299 
300  private &
301  orbit, & ! Called from astronomy_init and set_orbital_parameters
302  r_inv_squared, & ! Called from diurnal_solar, daily_mean_solar and orbit
303  angle, declination, half_day ! called from diurnal_solar and daily_mean_solar
304  ! half_day, orbital_time, & ! called from diurnal_solar and daily_mean_solar
305  ! universal_time ! called from diurnal_solar:
306 
307  interface r_inv_squared
308  module procedure r_inv_squared_r4, r_inv_squared_r8
309  end interface r_inv_squared
310 
311  interface angle
312  module procedure angle_r4, angle_r8
313  end interface angle
314 
315  interface declination
316  module procedure declination_r4, declination_r8
317  end interface declination
318 
319  !> Private interface for internal use by dirunal_solar and daily_mean_solar.
320  !!
321  !> Example usage:
322  !! ~~~~~~~~~~{.f90}
323  !! half_day (latitude, dec) result (h)
324  !! ~~~~~~~~~~
325  !!
326  !! Separate routines exist within this interface for scalar,
327  !! or 2D input and output fields:
328  !!
329  !! ~~~~~~~~~~{.f90}
330  !! real, intent(in), dimension(:,:) :: latitude
331  !! real, intent(in) :: latitude
332  !!
333  !! real, dimension(size(latitude,1),size(latitude,2)) :: h
334  !! real :: h
335  !! ~~~~~~~~~~
336  !!
337  !! @param [in] <latitude> Latitudes of model grid points [radians]
338  !! @param [in] <dec> Solar declination [radians]
339  !! @param [out] <h> Half of the length of daylight at the given latitude and orbital position (dec); value
340  !! ranges between 0 (all darkness) and pi (all daylight) [dimensionless]
341  !> @ingroup astronomy_mod
342  interface half_day
343  module procedure half_day_2d_r4, half_day_2d_r8
344  module procedure half_day_0d_r4, half_day_0d_r8
345  end interface half_day
346 
347 
348 !> @addtogroup astronomy_mod
349 !> @{
350 
351 !---------------------------------------------------------------------
352 !-------- namelist ---------
353 
354 real(r8_kind) :: ecc = 0.01671_r8_kind !< Eccentricity of Earth's orbit [dimensionless]
355 real(r8_kind) :: obliq = 23.439_r8_kind !< Obliquity [degrees]
356 real(r8_kind) :: per = 102.932_r8_kind !< Longitude of perihelion with respect
357  !! to autumnal equinox in NH [degrees]
358 integer :: period = 0 !< Specified length of year [seconds];
359  !! must be specified to override default
360  !! value given by length_of_year in
361  !! time_manager_mod
362 integer :: day_ae = 23 !< Day of specified autumnal equinox
363 integer :: month_ae = 9 !< Month of specified autumnal equinox
364 integer :: year_ae = 1998 !< Year of specified autumnal equinox
365 integer :: hour_ae = 5 !< Hour of specified autumnal equinox
366 integer :: minute_ae = 37 !< Minute of specified autumnal equinox
367 integer :: second_ae = 0 !< Second of specified autumnal equinox
368 integer :: num_angles = 3600 !< Number of intervals into which the year
369  !! is divided to compute orbital positions
370 
371 
372 namelist /astronomy_nml/ ecc, obliq, per, period, &
373  year_ae, month_ae, day_ae, &
375  num_angles
376 
377 !--------------------------------------------------------------------
378 !------ public data ----------
379 
380 
381 !--------------------------------------------------------------------
382 !------ private data ----------
383 
384 type(time_type) :: autumnal_eq_ref !< time_type variable containing
385  !! specified time of reference
386  !! NH autumnal equinox
387 
388 type(time_type) :: period_time_type !< time_type variable containing
389  !! period of one orbit
390 
391 real(r8_kind), dimension(:), allocatable :: orb_angle !< table of orbital positions (0 to
392  !! 2*pi) as a function of time used
393  !! to find actual orbital position
394  !! via interpolation
395 
396 real(r8_kind) :: seconds_per_day = 86400.0_r8_kind !< seconds in a day
397 real(r8_kind) :: deg_to_rad !< conversion from degrees to radians
398 real(r8_kind) :: twopi !< 2 *PI
399 logical :: module_is_initialized=.false. !< has the module been initialized ?
400 
401 real(r8_kind), dimension(:,:), allocatable :: &
402  cosz_ann, & !< annual mean cos of zenith angle
403  solar_ann, & !< annual mean solar factor
404  fracday_ann !< annual mean daylight fraction
405 real(r8_kind) :: rrsun_ann !< annual mean earth-sun distance
406 logical :: annual_mean_calculated=.false. !< have the annual mean values been calculated?
407 integer :: num_pts = 0 !< count of grid_boxes for which
408  !! annual mean astronomy values have
409  !! been calculated
410 integer :: total_pts !< number of grid boxes owned by the processor
411 
412 !--------------------------------------------------------------------
413 
414 
415 
416 contains
417 
418 
419 
420 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421 !
422 ! PUBLIC SUBROUTINES
423 !
424 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
425 
426 !> @brief astronomy_init is the constructor for astronomy_mod.
427 !!
428 !! @throw FATAL, "astronomy_mod ecc must be between 0 and 0.99"
429 !! @throw FATAL, "astronomy_mod obliquity must be between -90 and 90 degrees"
430 !! @throw FATAL, "astronomy_mod perihelion must be between 0 and 360 degrees"
431 subroutine astronomy_init (latb, lonb)
432 
433 class(*), dimension(:,:), intent(in), optional :: latb !< 2d array of model latitudes at cell corners [radians]
434 class(*), dimension(:,:), intent(in), optional :: lonb !< 2d array of model longitudes at cell corners [radians]
435 logical :: is_valid
436 
437 !-------------------------------------------------------------------
438 ! local variables:
439 !-------------------------------------------------------------------
440 integer :: iunit, ierr, io, seconds, days, jd, id
441 character(len=17) :: err_str
442 
443 !-------------------------------------------------------------------
444 ! if module has already been initialized, exit.
445 !-------------------------------------------------------------------
446  if (module_is_initialized) return
447 
448 !-------------------------------------------------------------------
449 !> This routine will:
450 !> Verify that modules used by this module have been initialized.
451 !-------------------------------------------------------------------
452  call fms_init
453  call time_manager_init
454  call constants_init
455 
456 !-----------------------------------------------------------------------
457 !> Read namelist.
458 !-----------------------------------------------------------------------
459  read (input_nml_file, astronomy_nml, iostat=io)
460  ierr = check_nml_error(io,'astronomy_nml')
461 !---------------------------------------------------------------------
462 !> Write version number and namelist to logfile.
463 !---------------------------------------------------------------------
464  call write_version_number("ASTRONOMY_MOD", version)
465  if (mpp_pe() == mpp_root_pe() ) then
466  iunit = stdlog()
467  write (iunit, nml=astronomy_nml)
468  endif
469 !--------------------------------------------------------------------
470 !> Be sure input values are within valid ranges.
471 ! QUESTION : ARE THESE THE RIGHT LIMITS ???
472 !---------------------------------------------------------------------
473  if (ecc < 0.0_r8_kind .or. ecc > 0.99_r8_kind) &
474  call error_mesg ('astronomy_mod', &
475  'ecc must be between 0 and 0.99', fatal)
476  if (obliq < -90.0_r8_kind .or. obliq > 90.0_r8_kind) &
477  call error_mesg ('astronomy_mod', &
478  'obliquity must be between -90 and 90 degrees', fatal)
479  if (per < 0.0_r8_kind .or. per > 360.0_r8_kind) &
480  call error_mesg ('astronomy_mod', &
481  'perihelion must be between 0 and 360 degrees', fatal)
482 
483 !----------------------------------------------------------------------
484 !> Set up time-type variable defining specified time of autumnal equinox.
485 !----------------------------------------------------------------------
488 
489 !---------------------------------------------------------------------
490 !> Set up time-type variable defining length of year.
491 !----------------------------------------------------------------------
492  if (period == 0) then
494  call get_time (period_time_type, seconds, days)
495  period = int(seconds_per_day) * days + seconds
496  else
498  endif
499 
500 !---------------------------------------------------------------------
501 !> Define useful module variables.
502 !----------------------------------------------------------------------
503  twopi = 2.0_r8_kind * pi
504  deg_to_rad = twopi/360.0_r8_kind
505 
506 !---------------------------------------------------------------------
507 !> Call orbit to define table of orbital angles as function of
508 !! orbital time.
509 !----------------------------------------------------------------------
510 ! wfc moved here from orbit
511  allocate ( orb_angle(0:num_angles) )
512  call orbit
513 
514 !--------------------------------------------------------------------
515 !> If annual mean radiation is desired, then latb will be present.
516 !! allocate arrays to hold the needed astronomical factors. define
517 !! the total number of points that the processor is responsible for.
518 !--------------------------------------------------------------------
519  ! check that no invalid types (integers or characters) are given as optional arg
520 
521  is_valid = .false.
522  if (present(latb) .and. present(lonb)) then
523  select type (latb)
524  type is (real(r4_kind))
525  select type (lonb)
526  type is (real(r4_kind))
527  is_valid = .true.
528  class default
529  call get_data_type_string(lonb, err_str)
530  call error_mesg('astronomy_mod', 'kind mismatch, argument latb is real(r4_kind) but lonb has type: '// &
531  err_str, fatal)
532  end select
533  type is (real(r8_kind))
534  select type (lonb)
535  type is (real(r8_kind))
536  is_valid = .true.
537  class default
538  call get_data_type_string(lonb, err_str)
539  call error_mesg('astronomy_mod', 'kind mismatch, argument latb is real(r8_kind) but lonb has type: '//&
540  err_str, fatal)
541  end select
542  end select
543  if( is_valid ) then
544  jd = size(latb,2) - 1
545  id = size(lonb,1) - 1
546  allocate (cosz_ann(id, jd))
547  allocate (solar_ann(id, jd))
548  allocate (fracday_ann(id, jd))
549  total_pts = jd*id
550  else
551  call error_mesg('astronomy_mod', 'latb has unsupported kind size.' // &
552  'latb and lonb should both be real(r4_kind) or real(r8_kind)', fatal)
553  end if
554  elseif ( (present(latb) .and. .not. present(lonb)) .or. (present(lonb) .and. .not. present(latb)) ) then
555  call error_mesg ('astronomy_mod', 'lat and lon must both be present', fatal)
556  endif
557 
558 !---------------------------------------------------------------------
559 !> Mark the module as initialized.
560 !---------------------------------------------------------------------
561  module_is_initialized=.true.
562 
563 !---------------------------------------------------------------------
564 
565 end subroutine astronomy_init
566 
567 
568 !> @brief get_period_integer returns the length of the year as an
569 !! integer number of seconds.
570 !!
571 !! @throw FATAL, "astronomy_mod module has not been initialized"
572 subroutine get_period_integer (period_out)
573 
574 integer, intent(out) :: period_out !< Length of year [seconds]
575 
576 !--------------------------------------------------------------------
577 ! local variables:
578 
579 integer :: seconds, days
580 
581 !---------------------------------------------------------------------
582 ! exit if module has not been initialized.
583 !---------------------------------------------------------------------
584  if (.not. module_is_initialized) &
585  call error_mesg ( 'astronomy_mod', ' module has not been initialized', fatal)
586 
587 !--------------------------------------------------------------------
588 ! define length of year in seconds.
589 !--------------------------------------------------------------------
590  call get_time (period_time_type, seconds, days)
591  period_out = int(seconds_per_day) * days + seconds
592 
593 
594 end subroutine get_period_integer
595 
596 !> @brief get_period_time_type returns the length of the year as a time_type
597 !! variable.
598 !!
599 !! @throw FATAL, "astronomy_mod module has not been initialized"
600 subroutine get_period_time_type (period_out)
601 
602 type(time_type), intent(inout) :: period_out !< Length of year as time_type variable
603 
604 !---------------------------------------------------------------------
605 ! exit if module has not been initialized.
606 !---------------------------------------------------------------------
607  if (.not. module_is_initialized) &
608  call error_mesg ('astronomy_mod', 'module has not been initialized', fatal)
609 
610 !--------------------------------------------------------------------
611 ! define length of year as a time_type variable.
612 !--------------------------------------------------------------------
613  period_out = period_time_type
614 
615 end subroutine get_period_time_type
616 
617 !> @brief set_period_integer saves as the input length of the year (an
618 !! integer) in a time_type module variable.
619 !!
620 !! @throw FATAL, "astronomy_mod module has not been initialized"
621 subroutine set_period_integer (period_in)
622 
623 integer, intent(in) :: period_in !< Length of year as a time_type
624 
625 !---------------------------------------------------------------------
626 ! exit if module has not been initialized.
627 !---------------------------------------------------------------------
628  if (.not. module_is_initialized) &
629  call error_mesg ('astronomy_mod', 'module has not been initialized', fatal)
630 
631 !---------------------------------------------------------------------
632 ! define time_type variable defining the length of year from the
633 ! input value (integer seconds).
634 !---------------------------------------------------------------------
635  period_time_type = set_time(period_in, 0)
636 
637 end subroutine set_period_integer
638 
639 
640 !> @brief Set_period_time_type saves the length of the year (input as a
641 !! time_type variable) into a time_type module variable.
642 !!
643 !! @throw FATAL, "astronomy_mod module has not been initialized"
644 subroutine set_period_time_type(period_in)
645 
646 type(time_type), intent(in) :: period_in
647 
648 !---------------------------------------------------------------------
649 ! exit if module has not been initialized.
650 !---------------------------------------------------------------------
651  if (.not. module_is_initialized) &
652  call error_mesg ('astronomy_mod', 'module has not been initialized', fatal)
653 
654 !---------------------------------------------------------------------
655 ! define time_type variable defining the length of year from the
656 ! input value (time_type).
657 !---------------------------------------------------------------------
658  period_time_type = period_in
659 
660 
661 end subroutine set_period_time_type
662 
663 !> @brief set_ref_date_of_ae provides a means of specifying the reference
664 !! date of the NH autumnal equinox for a particular year.
665 !!
666 !! @details set_ref_date_of_ae provides a means of specifying the reference
667 !! date of the NH autumnal equinox for a particular year. It is only
668 !! used if calls are made to the calandar versions of the routines
669 !! diurnal_solar and daily_mean_solar. If the NOLEAP calendar is
670 !! used, then the date of autumnal equinox will be the same every
671 !! year. If JULIAN is used, then the date of autumnal equinox will
672 !! return to the same value every 4th year.
673 !!
674 !! @param [in] <day_in> Day of reference autumnal equinox
675 !! @param [in] <month_in> Month of reference autumnal equinox
676 !! @param [in] <year_in> Year of reference autumnal equinox
677 !! @param [out] <second_in> OPTIONAL: Second of reference autumnal equinox
678 !! @param [out] <minute_in> OPTIONAL: Minute of reference autumnal equinox
679 !! @param [out] <hour_in> OPTIONAL: Hour of reference autumnal equinox
680 !!
681 !! @throw FATAL, "astronomy_mod module has not been initialized"
682 
683 subroutine set_ref_date_of_ae (day_in,month_in,year_in, &
684  second_in,minute_in,hour_in)
685 
686 integer, intent(in) :: day_in, month_in, year_in
687 integer, intent(in), optional :: second_in, minute_in, hour_in
688 
689 !---------------------------------------------------------------------
690 ! exit if module has not been initialized.
691 !---------------------------------------------------------------------
692  if (.not. module_is_initialized) &
693  call error_mesg ('astronomy_mod', 'module has not been initialized', fatal)
694 
695 !--------------------------------------------------------------------
696 ! save the input time of ae specification into a time_type module
697 ! variable autumnal_eq_ref.
698 !--------------------------------------------------------------------
699  day_ae = day_in
700  month_ae = month_in
701  year_ae = year_in
702 
703  if (present(second_in)) then
704  second_ae = second_in
705  minute_ae = minute_in
706  hour_ae = hour_in
707  else
708  second_ae = 0
709  minute_ae = 0
710  hour_ae = 0
711  endif
712 
715 
716 !---------------------------------------------------------------------
717 
718 
719 end subroutine set_ref_date_of_ae
720 
721 
722 !> @brief get_ref_date_of_ae retrieves the reference date of the autumnal
723 !! equinox as integer variables.
724 !!
725 !! @throw FATAL, "astronomy_mod module has not been initialized"
726 !!
727 !! @param [out] <day_out> Day of reference autumnal equinox
728 !! @param [out] <month_out> Month of reference autumnal equinox
729 !! @param [out] <year_out> Year of reference autumnal equinox
730 !! @param [out] <second_out> Second of reference autumnal equinox
731 !! @param [out] <minute_out> Minute of reference autumnal equinox
732 !! @param [out] <hour_out> Hour of reference autumnal equinox
733 subroutine get_ref_date_of_ae (day_out,month_out,year_out,&
734  second_out,minute_out,hour_out)
735 
736 integer, intent(out) :: day_out, month_out, year_out, &
737  second_out, minute_out, hour_out
738 
739 !---------------------------------------------------------------------
740 ! exit if module has not been initialized.
741 !---------------------------------------------------------------------
742  if (.not. module_is_initialized) &
743  call error_mesg ('astronomy_mod', 'module has not been initialized', fatal)
744 
745 !---------------------------------------------------------------------
746 ! fill the output fields with the proper module data.
747 !---------------------------------------------------------------------
748  day_out = day_ae
749  month_out = month_ae
750  year_out = year_ae
751  second_out = second_ae
752  minute_out = minute_ae
753  hour_out = hour_ae
754 
755 
756 end subroutine get_ref_date_of_ae
757 
758 !> @brief astronomy_end is the destructor for astronomy_mod.
759 subroutine astronomy_end
760 
761  !----------------------------------------------------------------------
762  !> check if the module has been initialized.
763  !----------------------------------------------------------------------
764  if (.not. module_is_initialized) return
765  ! call error_mesg ( 'astronomy_mod', &
766  ! ' module has not been initialized', FATAL)
767 
768  !----------------------------------------------------------------------
769  !> deallocate module variables.
770  !----------------------------------------------------------------------
771  deallocate (orb_angle)
772  if (allocated(cosz_ann) ) then
773  deallocate (cosz_ann)
774  deallocate (fracday_ann)
775  deallocate (solar_ann)
776  endif
777 
778  !----------------------------------------------------------------------
779  !> mark the module as uninitialized.
780  !----------------------------------------------------------------------
781  module_is_initialized = .false.
782 
783 end subroutine astronomy_end
784 
785 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
786 !
787 ! PRIVATE SUBROUTINES
788 !
789 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
790 
791 !> @brief Orbit computes and stores a table of value of orbital angles as a
792 !! function of orbital time (both the angle and time are zero at
793 !! autumnal equinox in the NH, and range from 0 to 2*pi).
794 subroutine orbit
795 !---------------------------------------------------------------------
796 ! local variables
797 
798 integer :: n
799 real(kind=r8_kind) :: d1, d2, d3, d4, d5, dt, norm
800 
801 !--------------------------------------------------------------------
802 !> allocate the orbital angle array, sized by the namelist parameter
803 !! num_angles, defining the annual cycle resolution of the earth's
804 !! orbit. define some constants to be used.
805 !--------------------------------------------------------------------
806 ! wfc moving to astronomy_init
807 ! allocate ( orb_angle(0:num_angles) )
808 orb_angle(0) = 0.0_r8_kind
809 dt = twopi/real(num_angles, r8_kind)
810 norm = sqrt(1.0_r8_kind - ecc**2)
811 dt = dt*norm
812 
813 !---------------------------------------------------------------------
814 !> define the orbital angle at each of the num_angles locations in
815 !! the orbit.
816 !---------------------------------------------------------------------
817  do n = 1, num_angles
818  d1 = dt*r_inv_squared(orb_angle(n-1))
819  d2 = dt*r_inv_squared(orb_angle(n-1) + 0.5_r8_kind * d1)
820  d3 = dt*r_inv_squared(orb_angle(n-1) + 0.5_r8_kind * d2)
821  d4 = dt*r_inv_squared(orb_angle(n-1) + d3)
822  d5 = d1/6.0_r8_kind + d2/3.0_r8_kind + d3/3.0_r8_kind + d4/6.0_r8_kind
823  orb_angle(n) = orb_angle(n-1) + d5
824  end do
825 
826 end subroutine orbit
827 
828 
829 !> @brief Orbital time returns the time (1 year = 2*pi) since autumnal
830 !! equinox
831 !!
832 !! @details Orbital time returns the time (1 year = 2*pi) since autumnal
833 !! equinox; autumnal_eq_ref is a module variable of time_type and
834 !! will have been defined by default or by a call to
835 !! set_ref_date_of_ae; length_of_year is available through the time
836 !! manager and is set at the value approriate for the calandar being used
837 function orbital_time(time) result(t)
838 
839 type(time_type), intent(in) :: time !< time (1 year = 2*pi) since autumnal equinox
840 real(kind=r8_kind) :: t
841 
842  t = (time - autumnal_eq_ref)//period_time_type
843  t = twopi*(t - real(floor(t), r8_kind))
844  if (time < autumnal_eq_ref) t = twopi - t
845 
846 end function orbital_time
847 
848 
849 !> @brief universal_time returns the time of day at longitude = 0.0
850 !! (1 day = 2*pi)
851 function universal_time(time) result(t)
852 
853 type(time_type), intent(in) :: time !< Time (1 year = 2*pi) since autumnal equinox
854 real(kind=r8_kind) :: t
855 
856  !--------------------------------------------------------------------
857  ! local variables
858  !--------------------------------------------------------------------
859  integer :: seconds, days
860 
861  call get_time(time, seconds, days)
862  t = twopi * real(seconds, r8_kind)/real(seconds_per_day, r8_kind)
863 
864 end function universal_time
865 
866 #include "astronomy_r4.fh"
867 #include "astronomy_r8.fh"
868 
869 end module astronomy_mod
870 !> @}
871 ! close documentation grouping
real(r8_kind) ecc
Eccentricity of Earth's orbit [dimensionless].
Definition: astronomy.F90:354
integer second_ae
Second of specified autumnal equinox.
Definition: astronomy.F90:367
subroutine, public astronomy_end
astronomy_end is the destructor for astronomy_mod.
Definition: astronomy.F90:760
subroutine, public astronomy_init(latb, lonb)
astronomy_init is the constructor for astronomy_mod.
Definition: astronomy.F90:432
type(time_type) autumnal_eq_ref
time_type variable containing specified time of reference NH autumnal equinox
Definition: astronomy.F90:384
integer minute_ae
Minute of specified autumnal equinox.
Definition: astronomy.F90:366
real(kind=r8_kind) function, public orbital_time(time)
Orbital time returns the time (1 year = 2*pi) since autumnal equinox.
Definition: astronomy.F90:838
integer period
Specified length of year [seconds]; must be specified to override default value given by length_of_ye...
Definition: astronomy.F90:358
real(r8_kind), dimension(:), allocatable orb_angle
table of orbital positions (0 to 2*pi) as a function of time used to find actual orbital position via...
Definition: astronomy.F90:391
real(r8_kind), dimension(:,:), allocatable fracday_ann
annual mean daylight fraction
Definition: astronomy.F90:401
subroutine, public set_ref_date_of_ae(day_in, month_in, year_in, second_in, minute_in, hour_in)
set_ref_date_of_ae provides a means of specifying the reference date of the NH autumnal equinox for a...
Definition: astronomy.F90:685
real(kind=r8_kind) function, public universal_time(time)
universal_time returns the time of day at longitude = 0.0 (1 day = 2*pi)
Definition: astronomy.F90:852
integer total_pts
number of grid boxes owned by the processor
Definition: astronomy.F90:410
subroutine set_period_time_type(period_in)
Set_period_time_type saves the length of the year (input as a time_type variable) into a time_type mo...
Definition: astronomy.F90:645
integer num_angles
Number of intervals into which the year is divided to compute orbital positions.
Definition: astronomy.F90:368
real(r8_kind), dimension(:,:), allocatable cosz_ann
annual mean cos of zenith angle
Definition: astronomy.F90:401
integer num_pts
count of grid_boxes for which annual mean astronomy values have been calculated
Definition: astronomy.F90:407
integer year_ae
Year of specified autumnal equinox.
Definition: astronomy.F90:364
real(r8_kind) obliq
Obliquity [degrees].
Definition: astronomy.F90:355
subroutine, private orbit
Orbit computes and stores a table of value of orbital angles as a function of orbital time (both the ...
Definition: astronomy.F90:795
integer month_ae
Month of specified autumnal equinox.
Definition: astronomy.F90:363
real(r8_kind) seconds_per_day
seconds in a day
Definition: astronomy.F90:396
subroutine set_period_integer(period_in)
set_period_integer saves as the input length of the year (an integer) in a time_type module variable.
Definition: astronomy.F90:622
real(r8_kind) rrsun_ann
annual mean earth-sun distance
Definition: astronomy.F90:405
real(r8_kind), dimension(:,:), allocatable solar_ann
annual mean solar factor
Definition: astronomy.F90:401
integer day_ae
Day of specified autumnal equinox.
Definition: astronomy.F90:362
real(r8_kind) deg_to_rad
conversion from degrees to radians
Definition: astronomy.F90:397
real(r8_kind) per
Longitude of perihelion with respect to autumnal equinox in NH [degrees].
Definition: astronomy.F90:356
integer hour_ae
Hour of specified autumnal equinox.
Definition: astronomy.F90:365
logical module_is_initialized
has the module been initialized ?
Definition: astronomy.F90:399
subroutine, public get_ref_date_of_ae(day_out, month_out, year_out, second_out, minute_out, hour_out)
get_ref_date_of_ae retrieves the reference date of the autumnal equinox as integer variables.
Definition: astronomy.F90:735
subroutine get_period_time_type(period_out)
get_period_time_type returns the length of the year as a time_type variable.
Definition: astronomy.F90:601
subroutine get_period_integer(period_out)
get_period_integer returns the length of the year as an integer number of seconds.
Definition: astronomy.F90:573
real(r8_kind) twopi
2 *PI
Definition: astronomy.F90:398
type(time_type) period_time_type
time_type variable containing period of one orbit
Definition: astronomy.F90:388
logical annual_mean_calculated
have the annual mean values been calculated?
Definition: astronomy.F90:406
Calculates the annual mean of solar information for a given latitude and time.
Definition: astronomy.F90:253
Calculates the daily mean solar information for a given time and latitude.
Definition: astronomy.F90:206
Calculates solar information for the given location(lat & lon) and time.
Definition: astronomy.F90:148
Gets the length of year for current calendar.
Definition: astronomy.F90:275
Private interface for internal use by dirunal_solar and daily_mean_solar.
Definition: astronomy.F90:342
Sets the length of a year for the calendar in use.
Definition: astronomy.F90:295
integer function, public check_nml_error(IOSTAT, NML_NAME)
Checks the iostat argument that is returned after reading a namelist and determines if the error code...
Definition: fms.F90:580
subroutine, public write_version_number(version, tag, unit)
Prints to the log file (or a specified unit) the version id string and tag name.
Definition: fms.F90:758
subroutine, public fms_init(localcomm, alt_input_nml_path)
Initializes the FMS module and also calls the initialization routines for all modules in the MPP pack...
Definition: fms.F90:332
subroutine, public error_mesg(routine, message, level)
Print notes, warnings and error messages; terminates program for warning and error messages....
Definition: fms.F90:498
integer function stdlog()
This function returns the current standard fortran unit numbers for log messages. Log messages,...
Definition: mpp_util.inc:59
integer function mpp_pe()
Returns processor ID.
Definition: mpp_util.inc:407
type(time_type) function, public length_of_year()
Returns the mean length of the year in the default calendar setting.
subroutine, public get_time(Time, seconds, days, ticks, err_msg)
Returns days and seconds ( < 86400 ) corresponding to a time. err_msg should be checked for any error...
subroutine, public time_manager_init()
Initialization routine. Writes the version information to the log file.
Given an input date in year, month, days, etc., creates a time_type that represents this time interva...
Given some number of seconds and days, returns the corresponding time_type.
Type to represent amounts of time. Implemented as seconds and days to allow for larger intervals.