portedportable, not yet portedexecuted, not portableexecutable, not hit by this run
| 1 | ! This file is part of MOM6, the Modular Ocean Model version 6. | |
| 2 | ! See the LICENSE file for licensing information. | |
| 3 | ! SPDX-License-Identifier: Apache-2.0 | |
| 4 | ||
| 5 | 2 | program MOM6 |
| 6 | ||
| 7 | !********+*********+*********+*********+*********+*********+*********+** | |
| 8 | !* * | |
| 9 | !* The Modular Ocean Model, version 6 * | |
| 10 | !* MOM6 * | |
| 11 | !* * | |
| 12 | !* By Alistair Adcroft, Stephen Griffies and Robert Hallberg * | |
| 13 | !* * | |
| 14 | !* This file is the ocean-only driver for Version 6 of the Modular * | |
| 15 | !* Ocean Model (MOM). A separate ocean interface for use with * | |
| 16 | !* coupled models is provided in ocean_model_MOM.F90. These two * | |
| 17 | !* drivers are kept in separate directories for convenience of code * | |
| 18 | !* selection during compiling. This file orchestrates the calls to * | |
| 19 | !* the MOM initialization routines, to the subroutine that steps * | |
| 20 | !* the model, and coordinates the output and saving restarts. A * | |
| 21 | !* description of all of the files that constitute MOM is found in * | |
| 22 | !* the comments at the beginning of MOM.F90. The arguments of each * | |
| 23 | !* subroutine are described where the subroutine is defined. * | |
| 24 | !* * | |
| 25 | !* Macros written all in capital letters are defined in MOM_memory.h. * | |
| 26 | !* * | |
| 27 | !********+*********+*********+*********+*********+*********+*********+** | |
| 28 | ||
| 29 | 1 | use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end |
| 30 | use MOM_cpu_clock, only : CLOCK_COMPONENT | |
| 31 | use MOM_data_override, only : data_override_init | |
| 32 | use MOM_diag_mediator, only : diag_mediator_end, diag_ctrl, diag_mediator_close_registration | |
| 33 | use MOM_diag_manager_infra, only : diag_manager_set_time_end_infra | |
| 34 | use MOM, only : initialize_MOM, step_MOM, MOM_control_struct, MOM_end | |
| 35 | use MOM, only : extract_surface_state, finish_MOM_initialization | |
| 36 | use MOM, only : get_MOM_state_elements, MOM_state_is_synchronized | |
| 37 | use MOM, only : step_offline | |
| 38 | use MOM, only : save_MOM_restart | |
| 39 | use MOM_coms, only : Set_PElist | |
| 40 | use MOM_domains, only : MOM_infra_init, MOM_infra_end, set_MOM_thread_affinity | |
| 41 | use MOM_ensemble_manager, only : ensemble_manager_init, get_ensemble_size | |
| 42 | use MOM_ensemble_manager, only : ensemble_pelist_setup | |
| 43 | use MOM_error_handler, only : MOM_error, MOM_mesg, WARNING, FATAL, is_root_pe | |
| 44 | use MOM_error_handler, only : callTree_enter, callTree_leave, callTree_waypoint | |
| 45 | use MOM_file_parser, only : read_param, get_param, log_param, log_version, param_file_type | |
| 46 | use MOM_file_parser, only : close_param_file | |
| 47 | use MOM_forcing_type, only : forcing, mech_forcing, forcing_diagnostics | |
| 48 | use MOM_forcing_type, only : mech_forcing_diags, MOM_forcing_chksum, MOM_mech_forcing_chksum | |
| 49 | use MOM_get_input, only : get_MOM_input, directories | |
| 50 | use MOM_grid, only : ocean_grid_type | |
| 51 | use MOM_ice_shelf, only : initialize_ice_shelf, ice_shelf_end, ice_shelf_CS | |
| 52 | use MOM_ice_shelf, only : shelf_calc_flux, add_shelf_forces, ice_shelf_save_restart | |
| 53 | use MOM_ice_shelf, only : initialize_ice_shelf_fluxes, initialize_ice_shelf_forces | |
| 54 | use MOM_ice_shelf, only : ice_shelf_query, adjust_ice_sheet_frazil | |
| 55 | use MOM_ice_shelf_initialize, only : initialize_ice_SMB | |
| 56 | use MOM_interpolate, only : time_interp_external_init | |
| 57 | use MOM_io, only : file_exists, open_ASCII_file, close_file | |
| 58 | use MOM_io, only : check_nml_error, io_infra_init, io_infra_end | |
| 59 | use MOM_io, only : APPEND_FILE, READONLY_FILE | |
| 60 | use MOM_string_functions,only : uppercase | |
| 61 | use MOM_surface_forcing, only : set_forcing, forcing_save_restart | |
| 62 | use MOM_surface_forcing, only : surface_forcing_init, surface_forcing_CS | |
| 63 | use MOM_time_manager, only : time_type, set_date, get_date, real_to_time, time_to_real | |
| 64 | use MOM_time_manager, only : operator(+), operator(-), operator(*), operator(/) | |
| 65 | use MOM_time_manager, only : operator(>), operator(<), operator(>=) | |
| 66 | use MOM_time_manager, only : increment_date, set_calendar_type, month_name | |
| 67 | use MOM_time_manager, only : JULIAN, GREGORIAN, NOLEAP, THIRTY_DAY_MONTHS, NO_CALENDAR | |
| 68 | use MOM_tracer_flow_control, only : tracer_flow_control_CS | |
| 69 | use MOM_unit_scaling, only : unit_scale_type | |
| 70 | use MOM_variables, only : surface | |
| 71 | use MOM_verticalGrid, only : verticalGrid_type | |
| 72 | use MOM_wave_interface, only : wave_parameters_CS, MOM_wave_interface_init | |
| 73 | use MOM_wave_interface, only : Update_Surface_Waves | |
| 74 | use MOM_write_cputime, only : write_cputime, MOM_write_cputime_init | |
| 75 | use MOM_write_cputime, only : write_cputime_start_clock, write_cputime_CS | |
| 76 | ||
| 77 | implicit none | |
| 78 | ||
| 79 | #include <MOM_memory.h> | |
| 80 | ||
| 81 | ! A structure with the driving mechanical surface forces | |
| 82 | type(mech_forcing) :: forces | |
| 83 | ! A structure containing pointers to the thermodynamic forcing fields | |
| 84 | ! at the ocean surface. | |
| 85 | type(forcing) :: fluxes | |
| 86 | ! A structure containing pointers to the ocean surface state fields. | |
| 87 | 1 | type(surface) :: sfc_state |
| 88 | ||
| 89 | ! A pointer to a structure containing metrics and related information. | |
| 90 | type(ocean_grid_type), pointer :: grid => NULL() | |
| 91 | type(verticalGrid_type), pointer :: GV => NULL() | |
| 92 | ! A pointer to a structure containing dimensional unit scaling factors. | |
| 93 | type(unit_scale_type), pointer :: US => NULL() | |
| 94 | ||
| 95 | ! If .true., use the ice shelf model for part of the domain. | |
| 96 | logical :: use_ice_shelf = .false. | |
| 97 | ||
| 98 | ! If .true., use surface wave coupling | |
| 99 | logical :: use_waves = .false. | |
| 100 | ||
| 101 | ! This is .true. if incremental restart files may be saved. | |
| 102 | logical :: permit_incr_restart = .true. | |
| 103 | ||
| 104 | ! nmax is the number of iterations after which to stop so that the | |
| 105 | ! simulation does not exceed its CPU time limit. nmax is determined by | |
| 106 | ! evaluating the CPU time used between successive calls to write_cputime. | |
| 107 | ! Initially it is set to be very large. | |
| 108 | integer :: nmax=2000000000 | |
| 109 | ||
| 110 | ! A structure containing several relevant directory paths. | |
| 111 | type(directories) :: dirs | |
| 112 | ||
| 113 | ! A suite of time types for use by MOM | |
| 114 | type(time_type), target :: Time ! A copy of the ocean model's time. | |
| 115 | ! Other modules can set pointers to this and | |
| 116 | ! change it to manage diagnostics. | |
| 117 | type(time_type) :: Master_Time ! The ocean model's master clock. No other | |
| 118 | ! modules are ever given access to this. | |
| 119 | type(time_type) :: Time1 ! The value of the ocean model's time at the | |
| 120 | ! start of a call to step_MOM. | |
| 121 | type(time_type) :: Start_time ! The start time of the simulation. | |
| 122 | type(time_type) :: segment_start_time ! The start time of this run segment. | |
| 123 | type(time_type) :: Time_end ! End time for the segment or experiment. | |
| 124 | type(time_type) :: restart_time ! The next time to write restart files. | |
| 125 | type(time_type) :: Time_step_ocean ! A time_type version of dt_forcing. | |
| 126 | logical :: segment_start_time_set ! True if segment_start_time has been set to a valid value. | |
| 127 | ||
| 128 | real :: elapsed_time = 0.0 ! Elapsed time in this run [T ~> s]. | |
| 129 | logical :: elapsed_time_master ! If true, elapsed time is used to set the model's master | |
| 130 | ! clock (Time). This is needed if Time_step_ocean is not | |
| 131 | ! an exact representation of dt_forcing. | |
| 132 | real :: dt_forcing ! The coupling time step [T ~> s]. | |
| 133 | real :: dt ! The nominal baroclinic dynamics time step [T ~> s]. | |
| 134 | integer :: ntstep ! The number of baroclinic dynamics time steps within dt_forcing. | |
| 135 | real :: dt_therm ! The thermodynamic timestep [T ~> s] | |
| 136 | real :: dt_dyn ! The actual dynamic timestep used [T ~> s]. The value of dt_dyn | |
| 137 | ! is chosen so that dt_forcing is an integer multiple of dt_dyn. | |
| 138 | real :: dtdia ! The diabatic timestep [T ~> s] | |
| 139 | real :: t_elapsed_seg ! The elapsed time in this run segment [T ~> s] | |
| 140 | integer :: n, ns, n_max, nts, n_last_thermo | |
| 141 | logical :: diabatic_first, single_step_call, initialize_smb | |
| 142 | type(time_type) :: Time2, time_chg ! Temporary time variables | |
| 143 | ||
| 144 | integer :: Restart_control ! An integer that is bit-tested to determine whether | |
| 145 | ! incremental restart files are saved and whether they | |
| 146 | ! have a time stamped name. +1 (bit 0) for generic | |
| 147 | ! files and +2 (bit 1) for time-stamped files. A | |
| 148 | ! restart file is saved at the end of a run segment | |
| 149 | ! unless Restart_control is negative. | |
| 150 | ||
| 151 | real :: Time_unit ! The time unit for the following input fields [s]. | |
| 152 | type(time_type) :: restint ! The time between saves of the restart file. | |
| 153 | type(time_type) :: daymax ! The final day of the simulation. | |
| 154 | ||
| 155 | integer :: CPU_steps ! The number of steps between writing CPU time. | |
| 156 | integer :: date(6) ! Possibly the start date of this run segment. | |
| 157 | 6 | type(param_file_type) :: param_file ! The structure indicating the file(s) |
| 158 | ! containing all run-time parameters. | |
| 159 | ||
| 160 | integer :: calendar_type=-1 ! A coded integer indicating the calendar type. | |
| 161 | ||
| 162 | integer :: unit, io_status, ierr | |
| 163 | integer :: initClock, mainClock, termClock | |
| 164 | ||
| 165 | logical :: debug ! If true, write verbose checksums for debugging purposes. | |
| 166 | logical :: offline_tracer_mode ! If false, use the model in prognostic mode where | |
| 167 | ! the barotropic and baroclinic dynamics, thermodynamics, | |
| 168 | ! etc. are stepped forward integrated in time. | |
| 169 | ! If true, then all of the above are bypassed with all | |
| 170 | ! fields necessary to integrate only the tracer advection | |
| 171 | ! and diffusion equation are read in from files stored from | |
| 172 | ! a previous integration of the prognostic model | |
| 173 | ||
| 174 | 179 | type(MOM_control_struct) :: MOM_CSp !< The control structure with all the MOM6 internal types, |
| 175 | !! parameters and variables | |
| 176 | type(tracer_flow_control_CS), pointer :: & | |
| 177 | tracer_flow_CSp => NULL() !< A pointer to the tracer flow control structure | |
| 178 | type(surface_forcing_CS), pointer :: surface_forcing_CSp => NULL() | |
| 179 | type(write_cputime_CS), pointer :: write_CPU_CSp => NULL() | |
| 180 | type(ice_shelf_CS), pointer :: ice_shelf_CSp => NULL() | |
| 181 | logical :: override_shelf_fluxes !< If true, and shelf dynamics are active, | |
| 182 | !! the data_override feature is enabled (only for MOSAIC grid types) | |
| 183 | type(wave_parameters_cs), pointer :: waves_CSp => NULL() | |
| 184 | type(diag_ctrl), pointer :: & | |
| 185 | diag => NULL() !< A pointer to the diagnostic regulatory structure | |
| 186 | !----------------------------------------------------------------------- | |
| 187 | ||
| 188 | character(len=4), parameter :: vers_num = 'v2.0' | |
| 189 | ! This include declares and sets the variable "version". | |
| 190 | # include "version_variable.h" | |
| 191 | character(len=40) :: mod_name = "MOM_main (MOM_driver)" ! This module's name. | |
| 192 | ||
| 193 | ! These are the variables that might be read via the namelist capability. | |
| 194 | integer :: date_init(6)=0 ! The start date of the whole simulation. | |
| 195 | character(len=16) :: calendar = 'julian' ! The name of the calendar type. | |
| 196 | integer :: years=0, months=0, days=0 ! These may determine the segment run | |
| 197 | integer :: hours=0, minutes=0, seconds=0 ! length, if read from a namelist. | |
| 198 | integer :: ocean_nthreads = 1 | |
| 199 | logical :: use_hyper_thread = .false. | |
| 200 | namelist /ocean_solo_nml/ date_init, calendar, months, days, hours, minutes, seconds, & | |
| 201 | ocean_nthreads, use_hyper_thread | |
| 202 | ||
| 203 | !===================================================================== | |
| 204 | ||
| 205 | 1 | call write_cputime_start_clock(write_CPU_CSp) |
| 206 | ||
| 207 | 1 | call MOM_infra_init() ; call io_infra_init() |
| 208 | ||
| 209 | !allocate(forces,fluxes,sfc_state) | |
| 210 | ||
| 211 | ! Initialize the ensemble manager based on settings in input.nml(ensemble.nml). | |
| 212 | 1 | call initialize_ocean_only_ensembles() |
| 213 | ||
| 214 | ! These clocks are on the global pelist. | |
| 215 | 1 | initClock = cpu_clock_id( 'Initialization' ) |
| 216 | 1 | mainClock = cpu_clock_id( 'Main loop' ) |
| 217 | 1 | termClock = cpu_clock_id( 'Termination' ) |
| 218 | 1 | call cpu_clock_begin(initClock) |
| 219 | ||
| 220 | 1 | call MOM_mesg('======== Model being driven by MOM_driver ========', 2) |
| 221 | 1 | call callTree_waypoint("Program MOM_main, MOM_driver.F90") |
| 222 | ||
| 223 | 1 | if (file_exists('input.nml')) then |
| 224 | ! Provide for namelist specification of the run length and calendar data. | |
| 225 | 1 | call open_ASCII_file(unit, 'input.nml', action=READONLY_FILE) |
| 226 | 1 | read(unit, ocean_solo_nml, iostat=io_status) |
| 227 | 1 | call close_file(unit) |
| 228 | 1 | ierr = check_nml_error(io_status,'ocean_solo_nml') |
| 229 | 1 | if (is_root_pe() .and. (years+months+days+hours+minutes+seconds > 0)) write(*,ocean_solo_nml) |
| 230 | endif | |
| 231 | ||
| 232 | ! This call sets the number and affinity of threads with openMP. | |
| 233 | !$ call set_MOM_thread_affinity(ocean_nthreads, use_hyper_thread) | |
| 234 | ||
| 235 | ! This call is required to initiate dirs%restart_input_dir for ocean_solo.res | |
| 236 | ! The contents of dirs will be reread in initialize_MOM. | |
| 237 | 1 | call get_MOM_input(dirs=dirs) |
| 238 | ||
| 239 | 1 | segment_start_time_set = .false. |
| 240 | ! Read ocean_solo restart, which can override settings from the namelist. | |
| 241 | 1 | if (file_exists(trim(dirs%restart_input_dir)//'ocean_solo.res')) then |
| 242 | 0 | date(:) = -1 |
| 243 | 0 | call open_ASCII_file(unit, trim(dirs%restart_input_dir)//'ocean_solo.res', action=READONLY_FILE) |
| 244 | 0 | read(unit,*) calendar_type |
| 245 | 0 | read(unit,*) date_init |
| 246 | 0 | read(unit,*) date |
| 247 | 0 | call close_file(unit) |
| 248 | ||
| 249 | 0 | call set_calendar_type(calendar_type) |
| 250 | 0 | if (sum(date) >= 0) then |
| 251 | ! In this case, the segment starts at a time fixed by ocean_solo.res | |
| 252 | 0 | segment_start_time = set_date(date(1), date(2), date(3), date(4), date(5), date(6)) |
| 253 | 0 | segment_start_time_set = .true. |
| 254 | endif | |
| 255 | else | |
| 256 | 1 | calendar = uppercase(calendar) |
| 257 | 1 | if (calendar(1:6) == 'JULIAN') then ; calendar_type = JULIAN |
| 258 | 0 | elseif (calendar(1:9) == 'GREGORIAN') then ; calendar_type = GREGORIAN |
| 259 | 0 | elseif (calendar(1:6) == 'NOLEAP') then ; calendar_type = NOLEAP |
| 260 | 0 | elseif (calendar(1:10)=='THIRTY_DAY') then ; calendar_type = THIRTY_DAY_MONTHS |
| 261 | 0 | elseif (calendar(1:11)=='NO_CALENDAR') then ; calendar_type = NO_CALENDAR |
| 262 | 0 | elseif (calendar(1:1) /= ' ') then |
| 263 | 0 | call MOM_error(FATAL,'MOM_driver: Invalid namelist value '//trim(calendar)//' for calendar') |
| 264 | else | |
| 265 | 0 | call MOM_error(FATAL,'MOM_driver: No namelist value for calendar') |
| 266 | endif | |
| 267 | 1 | call set_calendar_type(calendar_type) |
| 268 | endif | |
| 269 | ||
| 270 | ||
| 271 | 7 | if (sum(date_init) > 0) then |
| 272 | Start_time = set_date(date_init(1), date_init(2), date_init(3), & | |
| 273 | 0 | date_init(4), date_init(5), date_init(6)) |
| 274 | else | |
| 275 | 1 | Start_time = real_to_time(0.0) |
| 276 | endif | |
| 277 | ||
| 278 | 1 | call time_interp_external_init() |
| 279 | ||
| 280 | ! Call initialize MOM with an optional Ice Shelf CS which, if present triggers | |
| 281 | ! initialization of ice shelf parameters and arrays. | |
| 282 | !$omp target enter data map(alloc: MOM_CSp) | |
| 283 | 1 | if (segment_start_time_set) then |
| 284 | ! In this case, the segment starts at a time fixed by ocean_solo.res | |
| 285 | 0 | Time = segment_start_time |
| 286 | call initialize_MOM(Time, Start_time, param_file, dirs, MOM_CSp, & | |
| 287 | segment_start_time, offline_tracer_mode=offline_tracer_mode, & | |
| 288 | diag_ptr=diag, tracer_flow_CSp=tracer_flow_CSp, ice_shelf_CSp=ice_shelf_CSp, & | |
| 289 | 0 | waves_CSp=Waves_CSp) |
| 290 | else | |
| 291 | ! In this case, the segment starts at a time read from the MOM restart file | |
| 292 | ! or is left at Start_time by MOM_initialize. | |
| 293 | 1 | Time = Start_time |
| 294 | call initialize_MOM(Time, Start_time, param_file, dirs, MOM_CSp, & | |
| 295 | offline_tracer_mode=offline_tracer_mode, diag_ptr=diag, & | |
| 296 | 1 | tracer_flow_CSp=tracer_flow_CSp, ice_shelf_CSp=ice_shelf_CSp, waves_CSp=Waves_CSp) |
| 297 | endif | |
| 298 | ||
| 299 | 1 | call get_MOM_state_elements(MOM_CSp, G=grid, GV=GV, US=US, C_p_scaled=fluxes%C_p) |
| 300 | 1 | Master_Time = Time |
| 301 | 1 | use_ice_shelf = associated(ice_shelf_CSp) |
| 302 | ||
| 303 | 1 | if (use_ice_shelf) then |
| 304 | ! These arrays are not initialized in most solo cases, but are needed | |
| 305 | ! when using an ice shelf | |
| 306 | 0 | call initialize_ice_shelf_fluxes(ice_shelf_CSp, grid, US, fluxes) |
| 307 | 0 | call initialize_ice_shelf_forces(ice_shelf_CSp, grid, US, forces) |
| 308 | 0 | call ice_shelf_query(ice_shelf_CSp, grid, data_override_shelf_fluxes=override_shelf_fluxes) |
| 309 | 0 | if (override_shelf_fluxes) call data_override_init(Ocean_Domain_in=grid%domain%mpp_domain) |
| 310 | call get_param(param_file, mod_name, "INITIALIZE_ICE_SHEET_SMB", & | |
| 311 | 0 | initialize_smb, "Read in a constant SMB for the ice sheet", default=.false.) |
| 312 | 0 | if (initialize_smb) call initialize_ice_SMB(fluxes%shelf_sfc_mass_flux, grid, US, param_file) |
| 313 | endif | |
| 314 | ||
| 315 | ||
| 316 | 1 | call callTree_waypoint("done initialize_MOM") |
| 317 | ||
| 318 | 1 | call extract_surface_state(MOM_CSp, sfc_state) |
| 319 | ||
| 320 | 1 | if (use_ice_shelf .and. allocated(sfc_state%frazil)) & |
| 321 | 0 | call adjust_ice_sheet_frazil(sfc_state, fluxes, Ice_shelf_CSp) |
| 322 | ||
| 323 | call surface_forcing_init(Time, grid, US, param_file, diag, & | |
| 324 | 1 | surface_forcing_CSp, tracer_flow_CSp) |
| 325 | 1 | call callTree_waypoint("done surface_forcing_init") |
| 326 | ||
| 327 | ||
| 328 | call get_param(param_file, mod_name, "USE_WAVES", Use_Waves, & | |
| 329 | 1 | "If true, enables surface wave modules.",default=.false.) |
| 330 | ! MOM_wave_interface_init is called regardless of the value of USE_WAVES because | |
| 331 | ! it also initializes statistical waves. | |
| 332 | 1 | call MOM_wave_interface_init(Time, grid, GV, US, param_file, Waves_CSp, diag) |
| 333 | ||
| 334 | 1 | segment_start_time = Time |
| 335 | 1 | elapsed_time = 0.0 |
| 336 | ||
| 337 | ! Read all relevant parameters and write them to the model log. | |
| 338 | 1 | call log_version(param_file, mod_name, version, "") |
| 339 | call get_param(param_file, mod_name, "DT", dt, & | |
| 340 | 1 | units="s", scale=US%s_to_T, fail_if_missing=.true.) |
| 341 | call get_param(param_file, mod_name, "DT_FORCING", dt_forcing, & | |
| 342 | "The time step for changing forcing, coupling with other "//& | |
| 343 | "components, or potentially writing certain diagnostics. "//& | |
| 344 | "The default value is given by DT.", & | |
| 345 | 1 | units="s", default=US%T_to_s*dt, scale=US%s_to_T) |
| 346 | 1 | if (offline_tracer_mode) then |
| 347 | call get_param(param_file, mod_name, "DT_OFFLINE", dt_forcing, & | |
| 348 | "Length of time between reading in of input fields", & | |
| 349 | 0 | units="s", scale=US%s_to_T, fail_if_missing=.true.) |
| 350 | 0 | dt = dt_forcing |
| 351 | endif | |
| 352 | 1 | ntstep = MAX(1,ceiling(dt_forcing/dt - 0.001)) |
| 353 | ||
| 354 | 1 | Time_step_ocean = real_to_time(dt_forcing, unscale=US%T_to_s) |
| 355 | 1 | elapsed_time_master = (abs(dt_forcing - time_to_real(Time_step_ocean, scale=US%s_to_T)) > 1.0e-12*dt_forcing) |
| 356 | 1 | if (elapsed_time_master) & |
| 357 | 0 | call MOM_mesg("Using real elapsed time for the master clock.", 2) |
| 358 | ||
| 359 | ! Determine the segment end time, either from the namelist file or parsed input file. | |
| 360 | ! Note that Time_unit always is in [s]. | |
| 361 | call get_param(param_file, mod_name, "TIMEUNIT", Time_unit, & | |
| 362 | "The time unit for DAYMAX, ENERGYSAVEDAYS, and RESTINT.", & | |
| 363 | 1 | units="s", default=86400.0) |
| 364 | 1 | if (years+months+days+hours+minutes+seconds > 0) then |
| 365 | 0 | Time_end = increment_date(Time, years, months, days, hours, minutes, seconds) |
| 366 | 0 | call MOM_mesg('Segment run length determined from ocean_solo_nml.', 2) |
| 367 | call get_param(param_file, mod_name, "DAYMAX", daymax, timeunit=Time_unit, & | |
| 368 | 0 | default=Time_end, do_not_log=.true.) |
| 369 | call log_param(param_file, mod_name, "DAYMAX", daymax, & | |
| 370 | "The final time of the whole simulation, in units of "//& | |
| 371 | "TIMEUNIT seconds. This also sets the potential end "//& | |
| 372 | "time of the present run segment if the end time is "//& | |
| 373 | "not set via ocean_solo_nml in input.nml.", & | |
| 374 | 0 | timeunit=Time_unit) |
| 375 | else | |
| 376 | call get_param(param_file, mod_name, "DAYMAX", daymax, & | |
| 377 | "The final time of the whole simulation, in units of "//& | |
| 378 | "TIMEUNIT seconds. This also sets the potential end "//& | |
| 379 | "time of the present run segment if the end time is "//& | |
| 380 | "not set via ocean_solo_nml in input.nml.", & | |
| 381 | 1 | timeunit=Time_unit, fail_if_missing=.true.) |
| 382 | 1 | Time_end = daymax |
| 383 | endif | |
| 384 | ||
| 385 | 1 | call diag_manager_set_time_end_infra(Time_end) |
| 386 | ||
| 387 | call get_param(param_file, mod_name, "SINGLE_STEPPING_CALL", single_step_call, & | |
| 388 | "If true, advance the state of MOM with a single step "//& | |
| 389 | "including both dynamics and thermodynamics. If false "//& | |
| 390 | 1 | "the two phases are advanced with separate calls.", default=.true.) |
| 391 | call get_param(param_file, mod_name, "DT_THERM", dt_therm, & | |
| 392 | "The thermodynamic and tracer advection time step. "//& | |
| 393 | "Ideally DT_THERM should be an integer multiple of DT "//& | |
| 394 | "and less than the forcing or coupling time-step, unless "//& | |
| 395 | "THERMO_SPANS_COUPLING is true, in which case DT_THERM "//& | |
| 396 | "can be an integer multiple of the coupling timestep. By "//& | |
| 397 | "default DT_THERM is set to DT.", & | |
| 398 | 1 | units="s", default=US%T_to_s*dt, scale=US%s_to_T) |
| 399 | call get_param(param_file, mod_name, "DIABATIC_FIRST", diabatic_first, & | |
| 400 | "If true, apply diabatic and thermodynamic processes, "//& | |
| 401 | "including buoyancy forcing and mass gain or loss, "//& | |
| 402 | 1 | "before stepping the dynamics forward.", default=.false.) |
| 403 | ||
| 404 | ||
| 405 | 1 | if (Time >= Time_end) call MOM_error(FATAL, & |
| 406 | 0 | "MOM_driver: The run has been started at or after the end time of the run.") |
| 407 | ||
| 408 | call get_param(param_file, mod_name, "RESTART_CONTROL", Restart_control, & | |
| 409 | "An integer whose bits encode which restart files are "//& | |
| 410 | "written. Add 2 (bit 1) for a time-stamped file, and odd "//& | |
| 411 | "(bit 0) for a non-time-stamped file. A non-time-stamped "//& | |
| 412 | "restart file is saved at the end of the run segment "//& | |
| 413 | 1 | "for any non-negative value.", default=1) |
| 414 | call get_param(param_file, mod_name, "RESTINT", restint, & | |
| 415 | "The interval between saves of the restart file in units "//& | |
| 416 | "of TIMEUNIT. Use 0 (the default) to not save "//& | |
| 417 | "incremental restart files at all.", default=real_to_time(0.0), & | |
| 418 | 1 | timeunit=Time_unit) |
| 419 | call get_param(param_file, mod_name, "WRITE_CPU_STEPS", cpu_steps, & | |
| 420 | "The number of coupled timesteps between writing the cpu "//& | |
| 421 | "time. If this is not positive, do not check cpu time, and "//& | |
| 422 | "the segment run-length can not be set via an elapsed CPU time.", & | |
| 423 | 1 | default=1000) |
| 424 | call get_param(param_file, "MOM", "DEBUG", debug, & | |
| 425 | "If true, write out verbose debugging data.", & | |
| 426 | 1 | default=.false., debuggingParam=.true.) |
| 427 | ||
| 428 | 1 | call log_param(param_file, mod_name, "ELAPSED TIME AS MASTER", elapsed_time_master) |
| 429 | ||
| 430 | 1 | if (cpu_steps > 0) & |
| 431 | call MOM_write_cputime_init(param_file, dirs%output_directory, Start_time, & | |
| 432 | 1 | write_CPU_CSp) |
| 433 | ||
| 434 | ! Close the param_file. No further parsing of input is possible after this. | |
| 435 | 1 | call close_param_file(param_file) |
| 436 | 1 | call diag_mediator_close_registration(diag) |
| 437 | ||
| 438 | ! Write out a time stamp file. | |
| 439 | 1 | if (is_root_pe() .and. (calendar_type /= NO_CALENDAR)) call write_time_stamp_file(Time) |
| 440 | ||
| 441 | 1 | if (cpu_steps > 0) call write_cputime(Time, 0, write_CPU_CSp) |
| 442 | ||
| 443 | if (((.not.BTEST(Restart_control,1)) .and. (.not.BTEST(Restart_control,0))) & | |
| 444 | 1 | .or. (Restart_control < 0)) permit_incr_restart = .false. |
| 445 | ||
| 446 | 1 | if (restint > real_to_time(0.0)) then |
| 447 | ! restart_time is the next integral multiple of restint. | |
| 448 | restart_time = Start_time + restint * & | |
| 449 | 0 | (1 + ((Time + Time_step_ocean) - Start_time) / restint) |
| 450 | else | |
| 451 | ! Set the time so late that there is no intermediate restart. | |
| 452 | 1 | restart_time = Time_end + Time_step_ocean |
| 453 | 1 | permit_incr_restart = .false. |
| 454 | endif | |
| 455 | ||
| 456 | 1 | call cpu_clock_end(initClock) !end initialization |
| 457 | ||
| 458 | 1 | call cpu_clock_begin(mainClock) !begin main loop |
| 459 | ||
| 460 | 1 | ns = 1 |
| 461 | 13 | do while ((ns < nmax) .and. (Time < Time_end)) |
| 462 | 12 | call callTree_enter("Main loop, MOM_driver.F90",ns) |
| 463 | ||
| 464 | ! Set the forcing for the next steps. | |
| 465 | 12 | if (.not. offline_tracer_mode) then |
| 466 | call set_forcing(sfc_state, forces, fluxes, Time, Time_step_ocean, grid, US, & | |
| 467 | 12 | surface_forcing_CSp) |
| 468 | endif | |
| 469 | 12 | if (debug) then |
| 470 | 0 | call MOM_mech_forcing_chksum("After set forcing", forces, grid, US, haloshift=0) |
| 471 | 0 | call MOM_forcing_chksum("After set forcing", fluxes, grid, US, haloshift=0) |
| 472 | endif | |
| 473 | ||
| 474 | 12 | if (use_ice_shelf) then |
| 475 | 0 | call shelf_calc_flux(sfc_state, fluxes, Time, dt_forcing, ice_shelf_CSp) |
| 476 | 0 | call add_shelf_forces(grid, US, Ice_shelf_CSp, forces, external_call=.true.) |
| 477 | endif | |
| 478 | 12 | fluxes%fluxes_used = .false. |
| 479 | 12 | fluxes%dt_buoy_accum = dt_forcing |
| 480 | ||
| 481 | 12 | if (use_waves) then |
| 482 | 0 | call Update_Surface_Waves(grid, GV, US, time, time_step_ocean, waves_csp) |
| 483 | endif | |
| 484 | ||
| 485 | 12 | if (ns==1) then |
| 486 | 1 | call finish_MOM_initialization(Time, dirs, MOM_CSp) |
| 487 | endif | |
| 488 | ||
| 489 | ! This call steps the model over a time dt_forcing. | |
| 490 | 12 | Time1 = Master_Time ; Time = Master_Time |
| 491 | 12 | if (offline_tracer_mode) then |
| 492 | 0 | call step_offline(forces, fluxes, sfc_state, Time1, dt_forcing, MOM_CSp) |
| 493 | 12 | elseif (single_step_call) then |
| 494 | 12 | call step_MOM(forces, fluxes, sfc_state, Time1, dt_forcing, MOM_CSp, Waves=Waves_CSP) |
| 495 | else | |
| 496 | 0 | n_max = 1 ; if (dt_forcing > dt) n_max = ceiling(dt_forcing/dt - 0.001) |
| 497 | 0 | dt_dyn = dt_forcing / real(n_max) |
| 498 | ||
| 499 | 0 | nts = MAX(1,MIN(n_max,floor(dt_therm/dt_dyn + 0.001))) |
| 500 | 0 | n_last_thermo = 0 |
| 501 | ||
| 502 | 0 | Time2 = Time1 ; t_elapsed_seg = 0.0 |
| 503 | 0 | do n=1,n_max |
| 504 | 0 | if (diabatic_first) then |
| 505 | 0 | if (modulo(n-1,nts)==0) then |
| 506 | 0 | dtdia = dt_dyn*min(ntstep,n_max-(n-1)) |
| 507 | call step_MOM(forces, fluxes, sfc_state, Time2, dtdia, MOM_CSp, & | |
| 508 | do_dynamics=.false., do_thermodynamics=.true., & | |
| 509 | 0 | start_cycle=(n==1), end_cycle=.false., cycle_length=dt_forcing) |
| 510 | endif | |
| 511 | ||
| 512 | call step_MOM(forces, fluxes, sfc_state, Time2, dt_dyn, MOM_CSp, & | |
| 513 | do_dynamics=.true., do_thermodynamics=.false., & | |
| 514 | 0 | start_cycle=.false., end_cycle=(n==n_max), cycle_length=dt_forcing) |
| 515 | else | |
| 516 | call step_MOM(forces, fluxes, sfc_state, Time2, dt_dyn, MOM_CSp, & | |
| 517 | do_dynamics=.true., do_thermodynamics=.false., & | |
| 518 | 0 | start_cycle=(n==1), end_cycle=.false., cycle_length=dt_forcing) |
| 519 | ||
| 520 | 0 | if ((modulo(n,nts)==0) .or. (n==n_max)) then |
| 521 | 0 | dtdia = dt_dyn*(n - n_last_thermo) |
| 522 | ! Back up Time2 to the start of the thermodynamic segment. | |
| 523 | 0 | if (n > n_last_thermo+1) & |
| 524 | 0 | Time2 = Time2 - real_to_time((dtdia - dt_dyn), unscale=US%T_to_s) |
| 525 | call step_MOM(forces, fluxes, sfc_state, Time2, dtdia, MOM_CSp, & | |
| 526 | do_dynamics=.false., do_thermodynamics=.true., & | |
| 527 | 0 | start_cycle=.false., end_cycle=(n==n_max), cycle_length=dt_forcing) |
| 528 | 0 | n_last_thermo = n |
| 529 | endif | |
| 530 | endif | |
| 531 | ||
| 532 | 0 | t_elapsed_seg = t_elapsed_seg + dt_dyn |
| 533 | 0 | Time2 = Time1 + real_to_time(t_elapsed_seg, unscale=US%T_to_s) |
| 534 | enddo | |
| 535 | endif | |
| 536 | ||
| 537 | ! Time = Time + Time_step_ocean | |
| 538 | ! This is here to enable fractional-second time steps. | |
| 539 | 12 | elapsed_time = elapsed_time + dt_forcing |
| 540 | 12 | if (elapsed_time > 2.0e9*US%s_to_T) then |
| 541 | ! This is here to ensure that the conversion from a real to an integer can be accurately | |
| 542 | ! represented in long runs (longer than ~63 years). It will also ensure that elapsed time | |
| 543 | ! does not lose resolution of order the timetype's resolution, provided that the timestep and | |
| 544 | ! tick are larger than 10-5 seconds. If a clock with a finer resolution is used, a smaller | |
| 545 | ! value would be required. | |
| 546 | 0 | time_chg = real_to_time(elapsed_time, unscale=US%T_to_s) |
| 547 | 0 | segment_start_time = segment_start_time + time_chg |
| 548 | 0 | elapsed_time = elapsed_time - time_to_real(time_chg, scale=US%s_to_T) |
| 549 | endif | |
| 550 | 12 | if (elapsed_time_master) then |
| 551 | 0 | Master_Time = segment_start_time + real_to_time(elapsed_time, unscale=US%T_to_s) |
| 552 | else | |
| 553 | 12 | Master_Time = Master_Time + Time_step_ocean |
| 554 | endif | |
| 555 | 12 | Time = Master_Time |
| 556 | ||
| 557 | 12 | if (cpu_steps > 0) then ; if (MOD(ns, cpu_steps) == 0) then |
| 558 | 0 | call write_cputime(Time, ns+ntstep-1, write_CPU_CSp, nmax) |
| 559 | endif ; endif | |
| 560 | ||
| 561 | 12 | call mech_forcing_diags(forces, dt_forcing, grid, Time, diag, surface_forcing_CSp%handles) |
| 562 | ||
| 563 | 12 | if (.not. offline_tracer_mode) then |
| 564 | 12 | if (fluxes%fluxes_used) then |
| 565 | call forcing_diagnostics(fluxes, sfc_state, grid, US, Time, & | |
| 566 | 12 | diag, surface_forcing_CSp%handles) |
| 567 | else | |
| 568 | call MOM_error(FATAL, "The solo MOM_driver is not yet set up to handle "//& | |
| 569 | 0 | "thermodynamic time steps that are longer than the coupling timestep.") |
| 570 | endif | |
| 571 | endif | |
| 572 | ||
| 573 | ! See if it is time to write out a restart file - timestamped or not. | |
| 574 | 12 | if ((permit_incr_restart) .and. (fluxes%fluxes_used) .and. & |
| 575 | (Time + (Time_step_ocean/2) > restart_time)) then | |
| 576 | 0 | if (BTEST(Restart_control,1)) then |
| 577 | call save_MOM_restart(MOM_CSp, dirs%restart_output_dir, Time, grid, & | |
| 578 | 0 | time_stamped=.true., GV=GV) |
| 579 | call forcing_save_restart(surface_forcing_CSp, grid, Time, & | |
| 580 | 0 | dirs%restart_output_dir, .true.) |
| 581 | 0 | if (use_ice_shelf) call ice_shelf_save_restart(ice_shelf_CSp, Time, & |
| 582 | 0 | dirs%restart_output_dir, .true.) |
| 583 | endif | |
| 584 | 0 | if (BTEST(Restart_control,0)) then |
| 585 | 0 | call save_MOM_restart(MOM_CSp, dirs%restart_output_dir, Time, grid, GV=GV) |
| 586 | call forcing_save_restart(surface_forcing_CSp, grid, Time, & | |
| 587 | 0 | dirs%restart_output_dir) |
| 588 | 0 | if (use_ice_shelf) call ice_shelf_save_restart(ice_shelf_CSp, Time, & |
| 589 | 0 | dirs%restart_output_dir) |
| 590 | endif | |
| 591 | 0 | restart_time = restart_time + restint |
| 592 | endif | |
| 593 | ||
| 594 | 12 | ns = ns + ntstep |
| 595 | 12 | call callTree_leave("Main loop") |
| 596 | enddo | |
| 597 | ||
| 598 | 1 | call cpu_clock_end(mainClock) |
| 599 | 1 | call cpu_clock_begin(termClock) |
| 600 | 1 | if (Restart_control>=0) then |
| 601 | 1 | if (.not.MOM_state_is_synchronized(MOM_CSp)) & |
| 602 | call MOM_error(WARNING, "End of MOM_main reached with inconsistent "//& | |
| 603 | "dynamics and advective times. Additional restart fields "//& | |
| 604 | 0 | "that have not been coded yet would be required for reproducibility.") |
| 605 | 1 | if (.not.fluxes%fluxes_used .and. .not.offline_tracer_mode) call MOM_error(FATAL, & |
| 606 | "End of MOM_main reached with unused buoyancy fluxes. "//& | |
| 607 | "For conservation, the ocean restart files can only be "//& | |
| 608 | 0 | "created after the buoyancy forcing is applied.") |
| 609 | ||
| 610 | 1 | call save_MOM_restart(MOM_CSp, dirs%restart_output_dir, Time, grid, GV=GV) |
| 611 | 1 | if (use_ice_shelf) call ice_shelf_save_restart(ice_shelf_CSp, Time, & |
| 612 | 0 | dirs%restart_output_dir) |
| 613 | ||
| 614 | ! Write the ocean solo restart file. | |
| 615 | call write_ocean_solo_res(Time, Start_time, calendar_type, & | |
| 616 | 1 | trim(dirs%restart_output_dir)//'ocean_solo.res') |
| 617 | endif | |
| 618 | ||
| 619 | 1 | if (is_root_pe()) then |
| 620 | 1 | call open_ASCII_file(unit, "exitcode") |
| 621 | 1 | if (Time < daymax) then |
| 622 | 0 | write(unit,*) 9 |
| 623 | else | |
| 624 | 1 | write(unit,*) 0 |
| 625 | endif | |
| 626 | 1 | call close_file(unit) |
| 627 | endif | |
| 628 | ||
| 629 | 1 | call callTree_waypoint("End MOM_main") |
| 630 | 1 | if (use_ice_shelf) call ice_shelf_end(ice_shelf_CSp) |
| 631 | 1 | call diag_mediator_end(Time, diag, end_diag_manager=.true.) |
| 632 | 1 | if (cpu_steps > 0) call write_cputime(Time, ns-1, write_CPU_CSp, call_end=.true.) |
| 633 | 1 | call cpu_clock_end(termClock) |
| 634 | ||
| 635 | 1 | call MOM_end(MOM_CSp) |
| 636 | !$omp target exit data map(delete: MOM_CSp) | |
| 637 | ||
| 638 | ! This closes out the infrastructure, including clocks, I/O and message passing communicators. | |
| 639 | 1 | call io_infra_end() ; call MOM_infra_end() |
| 640 | ||
| 641 | contains | |
| 642 | ||
| 643 | !> Write out the ocean solo restart file to the indicated path. | |
| 644 | 3 | subroutine write_ocean_solo_res(Time, Start_time, calendar, file_path) |
| 645 | type(time_type), intent(in) :: Time !< The current model time. | |
| 646 | type(time_type), intent(in) :: Start_Time !< The start time of the simulation. | |
| 647 | integer, intent(in) :: calendar !< A coded integer indicating the calendar type. | |
| 648 | character(len=*), intent(in) :: file_path !< The full path and name of the restart file | |
| 649 | ||
| 650 | ! Local variables | |
| 651 | integer :: unit | |
| 652 | integer :: yr, mon, day, hr, mins, sec ! Temp variables for writing the date. | |
| 653 | ||
| 654 | 1 | if (.not.is_root_pe()) return |
| 655 | ||
| 656 | 1 | call open_ASCII_file(unit, trim(file_path)) |
| 657 | 1 | write(unit, '(i6,8x,a)') calendar, & |
| 658 | 2 | '(Calendar: no_calendar=0, thirty_day_months=1, julian=2, gregorian=3, noleap=4)' |
| 659 | ||
| 660 | 1 | call get_date(Start_time, yr, mon, day, hr, mins, sec) |
| 661 | 1 | write(unit, '(6i6,8x,a)') yr, mon, day, hr, mins, sec, & |
| 662 | 2 | 'Model start time: year, month, day, hour, minute, second' |
| 663 | 1 | call get_date(Time, yr, mon, day, hr, mins, sec) |
| 664 | 1 | write(unit, '(6i6,8x,a)') yr, mon, day, hr, mins, sec, & |
| 665 | 2 | 'Current model time: year, month, day, hour, minute, second' |
| 666 | 1 | call close_file(unit) |
| 667 | 1 | end subroutine write_ocean_solo_res |
| 668 | ||
| 669 | ||
| 670 | !> Write out an ascii time stamp file with the model time, following FMS conventions. | |
| 671 | 3 | subroutine write_time_stamp_file(Time) |
| 672 | type(time_type), intent(in) :: Time !< The current model time. | |
| 673 | ! Local variables | |
| 674 | integer :: unit | |
| 675 | integer :: yr, mon, day, hr, mins, sec ! Temp variables for writing the date. | |
| 676 | character(len=9) :: month ! The name of the month | |
| 677 | ||
| 678 | 1 | if (.not.is_root_PE()) return |
| 679 | ||
| 680 | 1 | call open_ASCII_file(unit, 'time_stamp.out', action=APPEND_FILE) |
| 681 | 1 | call get_date(Time, yr, mon, day, hr, mins, sec) |
| 682 | 1 | month = month_name(mon) |
| 683 | 1 | write(unit,'(6i4,2x,a3)') yr, mon, day, hr, mins, sec, month(1:3) |
| 684 | 1 | call get_date(Time_end, yr, mon, day, hr, mins, sec) |
| 685 | 1 | month = month_name(mon) |
| 686 | 1 | write(unit,'(6i4,2x,a3)') yr, mon, day, hr, mins, sec, month(1:3) |
| 687 | 1 | call close_file(unit) |
| 688 | end subroutine write_time_stamp_file | |
| 689 | ||
| 690 | !> Initialize the ensemble manager. If there are no settings for ensemble_size | |
| 691 | !! in input.nml(ensemble.nml), these should not do anything. In coupled | |
| 692 | !! configurations, this all occurs in the external driver. | |
| 693 | 1 | subroutine initialize_ocean_only_ensembles() |
| 694 | 1 | integer, dimension(:), allocatable :: ocean_PElist |
| 695 | integer, dimension(0) :: atm_PElist, land_PElist, ice_PElist | |
| 696 | integer :: ensemble_size, nPEs_per, ensemble_info(6) | |
| 697 | ||
| 698 | 1 | call ensemble_manager_init() ; ensemble_info(:) = get_ensemble_size() |
| 699 | 1 | ensemble_size = ensemble_info(1) ; nPEs_per = ensemble_info(2) |
| 700 | 1 | if (ensemble_size > 1) then ! There are multiple ensemble members. |
| 701 | 0 | allocate(ocean_pelist(nPEs_per)) |
| 702 | call ensemble_pelist_setup(.true., 0, nPEs_per, 0, 0, atm_pelist, ocean_pelist, & | |
| 703 | 0 | land_pelist, ice_pelist) |
| 704 | 0 | call Set_PElist(ocean_pelist) |
| 705 | 0 | deallocate(ocean_pelist) |
| 706 | endif | |
| 707 | 1 | end subroutine initialize_ocean_only_ensembles |
| 708 | ||
| 709 | end program MOM6 |