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