CCSM/ESMF Stage-1 meeting
Time in Driver Run-Loop
2:45 pm MST, Damon Room
High level run driver overview – some alturnatives, future flexibility...
Agenda:
- Questions on last meeting:
- Is one subroutine for initialization what we want? – Yes, move all of initialization into that one subroutine.
- Is passing time-info down to components ok? (Note if pass up – must have an additional initialization phase) – For Stage-1 we'll do this at Stage-2 we need to re-evaluate keeping in mind that we'll have to make changes to component models.
- Note that must change timing for when couplers are called.
- Here's the link to last meeting Feb 24th
- Questions brought up on Component Rules email:
- Current CCSM components time-stamp data differently than own clock – maybe a time-step off or so...
- Calling ESMF Components is more expensive than a normal subroutine call – how about we add integer date, and time-of-day as an attribute to ESMF State data?
- Currently clocks sent to components in ESMF are read-only. Only way to pass info up – is via clock in component derived type. Or you could add data to the export state with meta-data to describe it – see above suggestion.
- Subtle time problem – export states not valid till alarm rings again..
- Concurrent case – states are prepared for different times..
- Look at top level run driver
- Examine some alturnatives..
- Next meeting in 2-weeks – look at pseudo-code for driver with mrg components doing time-averaging.
Slightly modified from last time
Here we move the coupler calls that act on component export states to before the component runs rather than after. The reason for that is that the export state after the component runs – represents the state at the next time the component will run – not the current time.
This has the advantage that the time-averaging complexity inherient in one model (only CAM, not other data atm models) is hidden inside of CAM and not elsewhere – this makes plug-and-play easier.! ! Time loop ! do while( .not. ESMF_ClockIsStopTime( ESyncClock ) ) ! ! Averaging rules components are responsible for any averaging done on within ! their coupling interval. Merger will average atmosphere fields for surface components ! and put the average on the given surface component import state. Merger will also average ! surface data to the atmosphere model coupling interval and put it on the atm import ! state ! if ( shr_inputinfo_RunModel( initinfo, atm ) .and. ESMF_AlarmIsRinging( atm_alarm ) )then ! Run coupler on atmosphere export state before running... call ESMF_CplCompRun ( cc_map_a2l, import=a2x_a, export=a2x_l, rc=rc ) call ESMF_CplCompRun ( cc_map_a2i, import=a2x_a, export=a2x_i, rc=rc ) call ESMF_CplCompRun ( cc_map_a2o, import=a2x_a, export=a2x_o, rc=rc ) ! Now run an atmosphere time-step -- don't use export state until reach next time to run call ESMF_GridCompRun( gc_atm, import=x2a_a, export=a2x_a, ESyncClock, phase=1 ) end if if ( shr_inputinfo_RunModel( initinfo, lnd ) .and. ESMF_AlarmIsRinging( lnd_alarm ) )then call ESMF_CplCompRun ( cc_map_l2a, import=l2x_l, export=l2x_a, rc=rc ) ! Note: cs_l is the composite state pointing to: a2x_l call ESMF_GridCompRun ( gc_mrg_x2l, import=cs_l, export=x2l_l, rc=rc ) call ESMF_GridCompRun( gc_lnd, import=x2l_l, export=l2x_l, ESyncClock, phase=1 ) call ESMF_GridCompRun( gc_lnd, phase=2 ) call ESMF_AlarmRingerOff( lnd_alarm ) end if if ( shr_inputinfo_RunModel( initinfo, ice ) .and. ESMF_AlarmIsRinging( ice_alarm ) )then call ESMF_CplCompRun ( cc_map_i2a, import=i2x_i, export=i2x_a, rc=rc ) call ESMF_CplCompRun ( cc_map_i2o, import=i2o_i, export=i2o_o, rc=rc ) ! Note: cs_i is the composite state pointing to: a2x_i, o2i_i call ESMF_GridCompRun ( gc_mrg_x2i, import=cs_i, export=x2i_i, rc=rc ) call ESMF_GridCompRun( gc_ice, import=x2i_i, export=i2x_i, ESyncClock, phase=1 ) call ESMF_GridCompRun( gc_ice, phase=2 ) call ESMF_AlarmRingerOff( ice_alarm ) end if if ( shr_inputinfo_RunModel( initinfo, ocn ) .and. ESMF_AlarmIsRinging( ocn_alarm ) )then call ESMF_CplCompRun( cc_map_o2a, import=o2x_o, export=o2x_a, rc=rc) call ESMF_CplCompRun ( cc_map_o2i, import=o2x_o, export=o2x_i, rc=rc ) ! Note: cs_o is the composite state pointing to: i2o_o and a2x_o call ESMF_GridCompRun ( gc_mrg_x2o, import=cs_o, export=x2o_o, rc=rc ) call ESMF_GridCompRun( gc_ocn, import=x2o_o, export=o2x_o, ESyncClock, phase=1 ) call ESMF_AlarmRingerOff( ocn_alarm ) end if if ( shr_inputinfo_RunModel( initinfo, atm ) .and. ESMF_AlarmIsRinging( atm_alarm ) )then ! Note: cs_a is the composite state pointing to: l2x_a, i2x_a, and o2x_a call ESMF_GridCompRun( gc_mrg_x2a, import=cs_a, export=x2a_a, rc=rc ) call ESMF_GridCompRun( gc_atm, import=x2a_a, phase=2 ) call ESMF_AlarmRingerOff( atm_alarm ) end if ! Advance the clock call ESMF_AdvanceClock( ESyncClock ) ! Write restart information out if ( ESMF_AlarmIsRinging( restart ) )then call shr_inputinfo_WriteRPointer( ymd, tod, mpicom, masterproc, initinfo, rest_file ) call shr_inputinfo_WriteRestart( rest_file, mpicom, masterproc, initinfo ) call shr_timemgr_WriteRestart( rest_file, mpicom, masterproc, SyncClock ) call shr_inputinfo_ArchiveRestart( initinfo, masterproc ) call ESMF_AlarmRingerOff( restart ) end if end do
What atm_comp_esmf would look like for cam...
atm_comp_run1:
if ( sync_clock%curr_time /= my_clock%curr_time ) abort if ( sync_clock%atm_alarm /= ringing ) abort call ESMF_AlarmGet( sync_clock%atm_alarm, next_ring_time=next_time ) next_time = next_time - my_clock%TimeStep ! Zero out time-average call eshr_EState_TimeAvgZero( a2x_a, n ) ! Convert ESMF import state to CAM internal input data-type call atm_Import_ESMF( x2a_a, cam_in ) do while ( (.not. ESMF_ClockIsStopTime(my_clock)) .and. (my_clock%curr_time .lt. next_time) ) call cam_run1( cam_in, cam_out ) if ( my_clock%curr_time .lt. next_time )then call cam_run2( cam_out, cam_in ) call cam_run3( cam_out ) call cam_run4( cam_out, cam_in, rstwr, nlend ) call advance_clock( my_clock ) end if ! Convert CAM internal output data-type to ESMF export state call atm_Export_ESMF( cam_out, atm_export ) ! Add to Time-average call eshr_EState_TimeAvgSum( atm_export, a2x_a, n ) end do ! Calculate time-average call eshr_EState_TimeAvg( a2x_a, n )
atm_comp_run2:
if ( sync_clock%curr_time /= my_clock%curr_time+my_clock%TimeStep ) abort if ( sync_clock%atm_alarm /= ringing ) abort call ESMF_AlarmGet( sync_clock%atm_alarm, next_ring_time=next_time ) ! Convert ESMF import state to CAM internal input data-type call atm_Import_ESMF( x2a_a, cam_in ) ! Zero out time-average call eshr_EState_TimeAvgZero( a2x_a, n ) do while ( (.not. ESMF_ClockIsStopTime(my_clock)) .and. (my_clock%curr_time .lt. next_time) ) call cam_run2( cam_out, cam_in ) call cam_run3( cam_out ) call cam_run4( cam_out, cam_in, rstwr, nlend ) call advance_clock( my_clock ) if ( my_clock%curr_time .lt. next_time )then call cam_run1( cam_in, cam_out ) end do ! Convert CAM internal output data-type to ESMF export state call atm_Export_ESMF( cam_out, atm_export ) ! Add to Time-average call eshr_EState_TimeAvgSum( atm_export, a2x_a, n ) end do ! Calculate time-average call eshr_EState_TimeAvg( a2x_a, n )
Alturnative – run driver at atm/lnd internal time-step
In this case, we use the straightforward CAM component that just runs over a single time step and doesn't do time-averaging. At initialization, the time-step from atm and lnd would have to be sent up from the components, and used for the driver-time-step. Below I also assume that ice and ocean do not do any time-averaging.
Assume: - Driver time-step – 20 min
- Ice time-step – 1 hour
- atm/land coupling interval – 1 hour
- Ocean time-step and coupling interval – 1 day
PROBLEM: The eshr_ calls below need to be inside a gridded component – so they run on the right VM!
! ! Time loop ! do while( .not. ESMF_ClockIsStopTime( ESyncClock ) ) if ( shr_inputinfo_RunModel( initinfo, atm ) .and. ESMF_AlarmIsRinging( atm_alarm ) )then call eshr_EState_TimeAvg( a2x_a, natm ) ! Run coupler on atmosphere export state before running... call ESMF_CplCompRun ( cc_map_a2l, import=a2x_a, export=a2x_l, rc=rc ) call ESMF_CplCompRun ( cc_map_a2i, import=a2x_a, export=a2x_i, rc=rc ) call ESMF_CplCompRun ( cc_map_a2o, import=a2x_a, export=a2x_o, rc=rc ) call eshr_EState_TimeAvgZero( a2x_a, natm ) end if if ( shr_inputinfo_RunModel( initinfo, atm ) )then call ESMF_GridCompRun( gc_atm, import=x2a_a, export=atm_export, ESyncClock, phase=1 ) call eshr_EState_TimeAvgSum( a2x_a, atm_export, natm ) end if if ( shr_inputinfo_RunModel( initinfo, lnd ) .and. ESMF_AlarmIsRinging( lnd_alarm ) )then call eshr_EState_TimeAvg( l2x_l, nlnd ) call ESMF_CplCompRun ( cc_map_l2a, import=l2x_l, export=l2x_a, rc=rc ) call ESMF_AlarmRingerOff( lnd_alarm ) call eshr_EState_TimeAvgZero( l2x_l, nlnd ) end if if ( shr_inputinfo_RunModel( initinfo, lnd ) )then ! Note: cs_l is the composite state pointing to: a2x_l call ESMF_GridCompRun ( gc_mrg_x2l, import=cs_l, export=x2l_l, rc=rc ) call ESMF_GridCompRun( gc_lnd, import=x2l_l, export=l2x_l, ESyncClock, phase=1 ) call ESMF_GridCompRun( gc_lnd, phase=2 ) call eshr_EState_TimeAvgSum( l2x_l, nlnd ) end if if ( shr_inputinfo_RunModel( initinfo, ice ) .and. ESMF_AlarmIsRinging( ice_alarm ) )then call ESMF_CplCompRun ( cc_map_i2a, import=i2x_i, export=i2x_a, rc=rc ) call ESMF_CplCompRun ( cc_map_i2o, import=i2o_i, export=i2o_o, rc=rc ) ! Note: cs_i is the composite state pointing to: a2x_i, o2i_i call ESMF_GridCompRun ( gc_mrg_x2i, import=cs_i, export=x2i_i, rc=rc ) call ESMF_GridCompRun( gc_ice, import=x2i_i, export=i2x_i, ESyncClock, phase=1 ) call ESMF_GridCompRun( gc_ice, phase=2 ) call ESMF_AlarmRingerOff( ice_alarm ) end if if ( shr_inputinfo_RunModel( initinfo, ocn ) .and. ESMF_AlarmIsRinging( ocn_alarm ) )then call ESMF_CplCompRun( cc_map_o2a, import=o2x_o, export=o2x_a, rc=rc) call ESMF_CplCompRun ( cc_map_o2i, import=o2x_o, export=o2x_i, rc=rc ) ! Note: cs_o is the composite state pointing to: i2o_o and a2x_o call ESMF_GridCompRun ( gc_mrg_x2o, import=cs_o, export=x2o_o, rc=rc ) call ESMF_GridCompRun( gc_ocn, import=x2o_o, export=o2x_o, ESyncClock, phase=1 ) call ESMF_AlarmRingerOff( ocn_alarm ) end if if ( shr_inputinfo_RunModel( initinfo, atm ) .and. ESMF_AlarmIsRinging( atm_alarm ) )then ! Note: cs_a is the composite state pointing to: l2x_a, i2x_a, and o2x_a call ESMF_GridCompRun( gc_mrg_x2a, import=cs_a, export=x2a_a, rc=rc ) call ESMF_GridCompRun( gc_atm, import=x2a_a, phase=2 ) call ESMF_AlarmRingerOff( atm_alarm ) end if ! Advance the clock call ESMF_AdvanceClock( ESyncClock ) ! Write restart information out if ( ESMF_AlarmIsRinging( restart ) )then call shr_inputinfo_WriteRPointer( ymd, tod, mpicom, masterproc, initinfo, rest_file ) call shr_inputinfo_WriteRestart( rest_file, mpicom, masterproc, initinfo ) call shr_timemgr_WriteRestart( rest_file, mpicom, masterproc, SyncClock ) call ESMF_AlarmRingerOff( restart ) end if end do
Behavior of CAM when running concurrently
- Send every radiation time-step (hourly) – mark as previous time-step
- Receive on time-step after the send is done
- Send and receive if nstep == 0 (mark as current time-step)
- Do NOT send or receive if nstep == 1
- Code NOT tested when not averaging over radiation time-steps
- Sends and receives are done inbetween cam_run1 and cam_run2
Do other models mark data with a different time-stamp than model time-step?
Is this important? For cases we are likely to go to (the hybrid case) we may not need this performance optimization?
Send time maps directly to the atm_alarm. But, receive time implys need another alarm offset by one atmosphere time-step. This makes behavior awkward for the above behavior dependent on alarms.Put time-stamps on E-States (this could just be integer date and time-of day attributes on States)
! ! Time loop ! do while( .not. ESMF_ClockIsStopTime( ESyncClock ) ) ! ! Averaging rules components are responsible for any averaging done on within ! their coupling interval. Merger will average atmosphere fields for surface components ! and put the average on the given surface component import state. Merger will also average ! surface data to the atmosphere model coupling interval and put it on the atm import ! state ! if ( shr_inputinfo_RunModel( initinfo, atm ) .and. ESMF_AlarmIsRinging( atm_alarm ) )then ! Now run an atmosphere time-step -- don't use export state until reach next time to run call ESMF_GridCompRun( gc_atm, import=x2a_a, export=a2x_a, ESyncClock, phase=1 ) end if if ( eshr_EState_ReachedTimeStamp( a2x_a, ESyncClock ) )then call ESMF_CplCompRun ( cc_map_a2l, import=a2x_a, export=a2x_l, rc=rc ) call ESMF_CplCompRun ( cc_map_a2i, import=a2x_a, export=a2x_i, rc=rc ) call ESMF_CplCompRun ( cc_map_a2o, import=a2x_a, export=a2x_o, rc=rc ) end if if ( shr_inputinfo_RunModel( initinfo, lnd ) .and. ESMF_AlarmIsRinging( lnd_alarm ) )then ! Note: cs_l is the composite state pointing to: a2x_l call ESMF_GridCompRun ( gc_mrg_x2l, import=cs_l, export=x2l_l, rc=rc ) call ESMF_GridCompRun( gc_lnd, import=x2l_l, export=l2x_l, ESyncClock, phase=1 ) call ESMF_GridCompRun( gc_lnd, phase=2 ) call ESMF_AlarmRingerOff( lnd_alarm ) end if if ( eshr_EState_ReachedTimeStamp( l2x_l, ESyncClock ) )then call ESMF_CplCompRun ( cc_map_l2a, import=l2x_l, export=l2x_a, rc=rc ) end if if ( shr_inputinfo_RunModel( initinfo, ice ) .and. ESMF_AlarmIsRinging( ice_alarm ) )then ! Note: cs_i is the composite state pointing to: a2x_i, o2i_i call ESMF_GridCompRun ( gc_mrg_x2i, import=cs_i, export=x2i_i, rc=rc ) call ESMF_GridCompRun( gc_ice, import=x2i_i, export=i2x_i, ESyncClock, phase=1 ) call ESMF_GridCompRun( gc_ice, phase=2 ) call ESMF_AlarmRingerOff( ice_alarm ) end if if ( eshr_EState_ReachedTimeStamp( i2x_i, ESyncClock ) )then call ESMF_CplCompRun ( cc_map_i2a, import=i2x_i, export=i2x_a, rc=rc ) call ESMF_CplCompRun ( cc_map_i2o, import=i2o_i, export=i2o_o, rc=rc ) end if if ( shr_inputinfo_RunModel( initinfo, ocn ) .and. ESMF_AlarmIsRinging( ocn_alarm ) )then ! Note: cs_o is the composite state pointing to: i2o_o and a2x_o call ESMF_GridCompRun ( gc_mrg_x2o, import=cs_o, export=x2o_o, rc=rc ) call ESMF_GridCompRun( gc_ocn, import=x2o_o, export=o2x_o, ESyncClock, phase=1 ) call ESMF_AlarmRingerOff( ocn_alarm ) end if if ( eshr_EState_ReachedTimeStamp( o2x_o, ESyncClock ) )then call ESMF_CplCompRun( cc_map_o2a, import=o2x_o, export=o2x_a, rc=rc) call ESMF_CplCompRun ( cc_map_o2i, import=o2x_o, export=o2x_i, rc=rc ) end if if ( shr_inputinfo_RunModel( initinfo, atm ) .and. ESMF_AlarmIsRinging( atm_alarm ) )then ! Note: cs_a is the composite state pointing to: l2x_a, i2x_a, and o2x_a call ESMF_GridCompRun( gc_mrg_x2a, import=cs_a, export=x2a_a, rc=rc ) call ESMF_GridCompRun( gc_atm, import=x2a_a, phase=2 ) call ESMF_AlarmRingerOff( atm_alarm ) end if ! Advance the clock call ESMF_AdvanceClock( ESyncClock ) ! Write restart information out if ( ESMF_AlarmIsRinging( restart ) )then call shr_inputinfo_WriteRPointer( ymd, tod, mpicom, masterproc, initinfo, rest_file ) call shr_inputinfo_WriteRestart( rest_file, mpicom, masterproc, initinfo ) call shr_timemgr_WriteRestart( rest_file, mpicom, masterproc, SyncClock ) call ESMF_AlarmRingerOff( restart ) end if end do
Use Ready/Not-Ready flags on E-States
We go back to the case where the driver runs at the atm/lnd internal
time-step. Since, the atmosphere is called every time-step – it can then flag it's import/export states as ready or not.
In this case, we use the straightforward CAM component that just runs over a single time step and doesn't do time-averaging. At initialization, the time-step from atm and lnd would have to be sent up from the components, and used for the driver-time-step. Below I also assume that ice and ocean do not do any time-averaging.
Assume: - Driver time-step – 20 min
- Ice time-step – 1 hour
- atm/land coupling interval – 1 hour
- Ocean time-step and coupling interval – 1 day
PROBLEM: The eshr_ calls below need to be inside a gridded component – so they run on the right VM!
! ! Time loop ! do while( .not. ESMF_ClockIsStopTime( ESyncClock ) ) if ( shr_inputinfo_RunModel( initinfo, atm ) .and. eshr_EState_IsReady( a2x_a ) )then call eshr_EState_TimeAvg( a2x_a, natm ) ! Run coupler on atmosphere export state before running... call ESMF_CplCompRun ( cc_map_a2l, import=a2x_a, export=a2x_l, rc=rc ) call ESMF_CplCompRun ( cc_map_a2i, import=a2x_a, export=a2x_i, rc=rc ) call ESMF_CplCompRun ( cc_map_a2o, import=a2x_a, export=a2x_o, rc=rc ) call eshr_EState_TimeAvgZero( a2x_a, natm ) end if if ( shr_inputinfo_RunModel( initinfo, atm ) )then call ESMF_GridCompRun( gc_atm, import=x2a_a, export=atm_export, ESyncClock, phase=1 ) call eshr_EState_TimeAvgSum( a2x_a, atm_export, natm ) end if if ( shr_inputinfo_RunModel( initinfo, lnd ) .and. eshr_EState_IsReady( l2x_l ) )then call eshr_EState_TimeAvg( l2x_l, nlnd ) call ESMF_CplCompRun ( cc_map_l2a, import=l2x_l, export=l2x_a, rc=rc ) call ESMF_AlarmRingerOff( lnd_alarm ) call eshr_EState_TimeAvgZero( l2x_l, nlnd ) end if if ( shr_inputinfo_RunModel( initinfo, lnd ) )then ! Note: cs_l is the composite state pointing to: a2x_l call ESMF_GridCompRun ( gc_mrg_x2l, import=cs_l, export=x2l_l, rc=rc ) call ESMF_GridCompRun( gc_lnd, import=x2l_l, export=l2x_l, ESyncClock, phase=1 ) call ESMF_GridCompRun( gc_lnd, phase=2 ) call eshr_EState_TimeAvgSum( l2x_l, nlnd ) end if if ( shr_inputinfo_RunModel( initinfo, ice ) .and. eshr_EState_IsReady( i2x_i ) )then call ESMF_CplCompRun ( cc_map_i2a, import=i2x_i, export=i2x_a, rc=rc ) call ESMF_CplCompRun ( cc_map_i2o, import=i2o_i, export=i2o_o, rc=rc ) ! Note: cs_i is the composite state pointing to: a2x_i, o2i_i call ESMF_GridCompRun ( gc_mrg_x2i, import=cs_i, export=x2i_i, rc=rc ) call ESMF_GridCompRun( gc_ice, import=x2i_i, export=i2x_i, ESyncClock, phase=1 ) call ESMF_GridCompRun( gc_ice, phase=2 ) call ESMF_AlarmRingerOff( ice_alarm ) end if if ( shr_inputinfo_RunModel( initinfo, ocn ) .and. eshr_EState_IsReady( o2x_o ) )then call ESMF_CplCompRun( cc_map_o2a, import=o2x_o, export=o2x_a, rc=rc) call ESMF_CplCompRun ( cc_map_o2i, import=o2x_o, export=o2x_i, rc=rc ) ! Note: cs_o is the composite state pointing to: i2o_o and a2x_o call ESMF_GridCompRun ( gc_mrg_x2o, import=cs_o, export=x2o_o, rc=rc ) call ESMF_GridCompRun( gc_ocn, import=x2o_o, export=o2x_o, ESyncClock, phase=1 ) call ESMF_AlarmRingerOff( ocn_alarm ) end if if ( shr_inputinfo_RunModel( initinfo, atm ) )then ! Note: cs_a is the composite state pointing to: l2x_a, i2x_a, and o2x_a call ESMF_GridCompRun( gc_mrg_x2a, import=cs_a, export=x2a_a, rc=rc ) call ESMF_GridCompRun( gc_atm, import=x2a_a, phase=2 ) end if ! Advance the clock call ESMF_AdvanceClock( ESyncClock ) ! Write restart information out if ( ESMF_AlarmIsRinging( restart ) )then call shr_inputinfo_WriteRPointer( ymd, tod, mpicom, masterproc, initinfo, rest_file ) call shr_inputinfo_WriteRestart( rest_file, mpicom, masterproc, initinfo ) call shr_timemgr_WriteRestart( rest_file, mpicom, masterproc, SyncClock ) call ESMF_AlarmRingerOff( restart ) end if end do