mirror of https://gitlab.com/QEF/q-e.git
929 lines
31 KiB
Fortran
929 lines
31 KiB
Fortran
! autopilot.f90
|
|
!********************************************************************************
|
|
! autopilot.f90 Copyright (c) 2005 Targacept, Inc.
|
|
!********************************************************************************
|
|
! The Autopilot Feature suite is a user level enhancement that enables the
|
|
! following features:
|
|
! automatic restart of a job;
|
|
! preconfiguration of job parameters;
|
|
! on-the-fly changes to job parameters;
|
|
! and pausing of a running job.
|
|
!
|
|
! For more information, see README.AUTOPILOT in document directory.
|
|
!
|
|
! This program is free software; you can redistribute it and/or modify it under
|
|
! the terms of the GNU General Public License as published by the Free Software
|
|
! Foundation; either version 2 of the License, or (at your option) any later version.
|
|
! This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
! WARRANTY; without even the implied warranty of MERCHANTABILITY FOR A PARTICULAR
|
|
! PURPOSE. See the GNU General Public License at www.gnu.or/copyleft/gpl.txt for
|
|
! more details.
|
|
!
|
|
! THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
|
! EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
|
! PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
|
! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
! FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND THE
|
|
! PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
|
|
! YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
!
|
|
! IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING,
|
|
! WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
|
|
! THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
|
! GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
|
|
! INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
|
|
! BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
|
! FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
|
|
! OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
|
!
|
|
! You should have received a copy of the GNU General Public License along with
|
|
! this program; if not, write to the
|
|
! Free Software Foundation, Inc.,
|
|
! 51 Franklin Street,
|
|
! Fifth Floor,
|
|
! Boston, MA 02110-1301, USA.
|
|
!
|
|
! Targacept's address is
|
|
! 200 East First Street, Suite 300
|
|
! Winston-Salem, North Carolina USA 27101-4165
|
|
! Attn: Molecular Design.
|
|
! Email: atp@targacept.com
|
|
!
|
|
! This work was supported by the Advanced Technology Program of the
|
|
! National Institute of Standards and Technology (NIST), Award No. 70NANB3H3065
|
|
!
|
|
!********************************************************************************
|
|
|
|
|
|
MODULE autopilot
|
|
!---------------------------------------------------------------------------
|
|
!! This module handles the Autopilot Feature Suite.
|
|
!
|
|
!! Written by Lee Atkinson, with help from the ATP team at Targacept, Inc.
|
|
!! Created June 2005.
|
|
!! Modified by Yonas Abraham, Sept 2006.
|
|
!
|
|
! The address for Targacept, Inc. is:
|
|
! 200 East First Street, Suite
|
|
! 300, Winston-Salem, North Carolina 27101;
|
|
! Attn: Molecular Design.
|
|
!
|
|
!! See README.AUTOPILOT in the Doc directory for more information.
|
|
!---------------------------------------------------------------------------
|
|
|
|
USE kinds
|
|
USE parser, ONLY : read_line
|
|
|
|
IMPLICIT NONE
|
|
SAVE
|
|
|
|
INTEGER, parameter :: MAX_INT = huge(1)
|
|
INTEGER, parameter :: max_event_step = 32 !right now there can be upto 32 Autopilot Events
|
|
INTEGER, parameter :: n_auto_vars = 10 !right now there are only 10 Autopilot Variables
|
|
|
|
INTEGER :: n_events
|
|
INTEGER :: event_index = 0
|
|
INTEGER :: max_rules = 320 !(max_event_step * n_auto_vars)
|
|
INTEGER :: n_rules
|
|
INTEGER :: event_step(max_event_step)
|
|
INTEGER :: current_nfi
|
|
LOGICAL :: pilot_p = .FALSE. ! pilot property
|
|
LOGICAL :: restart_p = .FALSE. ! restart property
|
|
LOGICAL :: pause_p = .FALSE. ! pause property
|
|
INTEGER :: pilot_unit = 42 ! perhaps move this to io_files
|
|
CHARACTER(LEN=256) :: pilot_type
|
|
|
|
! AUTOPILOT VARIABLES
|
|
! These are the variable tables which change the actual variable
|
|
! dynamically during the course of a simulation. There are many
|
|
! parameters which govern a simulation, yet only these are allowed
|
|
! to be changed using the event rule mechanism.
|
|
! Each of these tables are typed according to their variable
|
|
! and begin with event_
|
|
|
|
! &CONTROL
|
|
INTEGER :: rule_isave(max_event_step)
|
|
INTEGER :: rule_iprint(max_event_step)
|
|
LOGICAL :: rule_tprint(max_event_step)
|
|
REAL(DP) :: rule_dt(max_event_step)
|
|
! &SYSTEM
|
|
|
|
! &ELECTRONS
|
|
REAL(DP) :: rule_emass(max_event_step)
|
|
CHARACTER(LEN=80) :: rule_electron_dynamics(max_event_step)
|
|
REAL(DP) :: rule_electron_damping(max_event_step)
|
|
CHARACTER(LEN=80) :: rule_electron_orthogonalization(max_event_step)
|
|
|
|
|
|
! &IONS
|
|
CHARACTER(LEN=80) :: rule_ion_dynamics(max_event_step)
|
|
REAL(DP) :: rule_ion_damping(max_event_step)
|
|
CHARACTER(LEN=80) :: rule_ion_temperature(max_event_step)
|
|
REAL(DP) :: rule_tempw(max_event_step)
|
|
INTEGER :: rule_nhpcl(max_event_step)
|
|
REAL(DP) :: rule_fnosep(max_event_step)
|
|
! &CELL
|
|
|
|
! &PHONON
|
|
|
|
|
|
! Each rule also needs to be correlated (flagged) against the event time table
|
|
! This is used to flag the a given variable is to be changed on an
|
|
! event. Initially all set to false, a rule against an event makes it true
|
|
! Each of these flags are LOGICALs and begin with event_
|
|
! &CONTROL
|
|
LOGICAL :: event_isave(max_event_step)
|
|
LOGICAL :: event_iprint(max_event_step)
|
|
LOGICAL :: event_tprint(max_event_step)
|
|
LOGICAL :: event_dt(max_event_step)
|
|
! &SYSTEM
|
|
|
|
! &ELECTRONS
|
|
LOGICAL :: event_emass(max_event_step)
|
|
LOGICAL :: event_electron_dynamics(max_event_step)
|
|
LOGICAL :: event_electron_damping(max_event_step)
|
|
LOGICAL :: event_electron_orthogonalization(max_event_step)
|
|
|
|
! &IONS
|
|
LOGICAL :: event_ion_dynamics(max_event_step)
|
|
LOGICAL :: event_ion_damping(max_event_step)
|
|
LOGICAL :: event_ion_temperature(max_event_step)
|
|
LOGICAL :: event_tempw(max_event_step)
|
|
LOGICAL :: event_nhpcl(max_event_step)
|
|
LOGICAL :: event_fnosep(max_event_step)
|
|
! &CELL
|
|
|
|
! &PHONON
|
|
|
|
|
|
PRIVATE
|
|
PUBLIC :: auto_check, init_autopilot, card_autopilot, add_rule, &
|
|
& assign_rule, restart_p, max_event_step, event_index, event_step, rule_isave, &
|
|
& rule_iprint, &
|
|
& rule_tprint, &
|
|
& rule_dt, rule_emass, rule_electron_dynamics, rule_electron_damping, &
|
|
& rule_ion_dynamics, rule_ion_damping, rule_ion_temperature, rule_tempw, &
|
|
& rule_electron_orthogonalization, &
|
|
& event_isave, event_iprint, &
|
|
& event_tprint, &
|
|
& event_dt, event_emass, &
|
|
& event_electron_dynamics, event_electron_damping, event_ion_dynamics, &
|
|
& current_nfi, pilot_p, pilot_unit, pause_p,auto_error, parse_mailbox, &
|
|
& event_ion_damping, event_ion_temperature, event_tempw, &
|
|
& event_electron_orthogonalization, &
|
|
& event_nhpcl, event_fnosep, rule_nhpcl, rule_fnosep
|
|
|
|
|
|
CONTAINS
|
|
|
|
!----------------------------------------------------------------------------
|
|
SUBROUTINE auto_error( calling_routine, message)
|
|
!----------------------------------------------------------------------------
|
|
!! This routine calls errore based upon the pilot property flag.
|
|
!! If the flag is TRUE, the simulation will not stop,
|
|
!! but the pause property flag is set to TRUE,
|
|
!! causing pilot to force a state of sleep,
|
|
!! until the user can mail proper commands.
|
|
!! Otherwise, its assumed that dynamics have not started
|
|
!! and this call is an indirect result of read_cards.
|
|
!! Thus the simulation will stop.
|
|
!! Either way errore will always issues a warning message.
|
|
!
|
|
IMPLICIT NONE
|
|
!
|
|
CHARACTER(LEN=*), INTENT(IN) :: calling_routine
|
|
!! the name of the calling calling_routine
|
|
CHARACTER(LEN=*), INTENT(IN) :: message
|
|
!! the output message
|
|
!
|
|
INTEGER :: ierr
|
|
! the error flag
|
|
|
|
IF (pilot_p) THEN
|
|
! if ierr < 0 errore writes the message but does not stop
|
|
ierr = -1
|
|
pause_p = .TRUE.
|
|
!call mp_bcast(pause_p, ionode_id, world_comm)
|
|
ELSE
|
|
! if ierr > 0 it stops
|
|
ierr = 1
|
|
ENDIF
|
|
|
|
CALL errore( calling_routine, message, ierr )
|
|
|
|
END SUBROUTINE auto_error
|
|
|
|
|
|
!-----------------------------------------------------------------------
|
|
! AUTO (restart) MODE
|
|
!
|
|
! Checks if restart files are present, just like what readfile_cp does,
|
|
! which calls cp_readfile which create a path to restart.xml.
|
|
! This could be checked with inquire, as in check_restartfile.
|
|
! If restart_mode=auto, and restart.xml is present,
|
|
! then restart_mode="restart", else its "from_scratch".
|
|
! Set other associated vars, appropriately.
|
|
!
|
|
! Put this in setcontrol_flags on the select statement
|
|
!-----------------------------------------------------------------------
|
|
LOGICAL FUNCTION auto_check (ndr, outdir)
|
|
!---------------------------------------------------------------------
|
|
!! Checks if restart files are present.
|
|
!
|
|
USE io_global, ONLY: ionode, ionode_id
|
|
USE mp, ONLY : mp_bcast
|
|
USE mp_world, ONLY : world_comm
|
|
IMPLICIT NONE
|
|
INTEGER, INTENT(IN) :: ndr
|
|
!! I/O unit number
|
|
CHARACTER(LEN=*), INTENT(IN) :: outdir
|
|
!! output directory
|
|
!
|
|
! ... local variables
|
|
!
|
|
CHARACTER(LEN=256) :: dirname, filename
|
|
CHARACTER (LEN=6), EXTERNAL :: int_to_char
|
|
LOGICAL :: restart_p = .FALSE.
|
|
INTEGER :: strlen
|
|
! right now cp_readfile is called with outdir = ' '
|
|
! so, in keeping with the current design,
|
|
! the responsibility of setting
|
|
! ndr and outdir is the calling program
|
|
|
|
|
|
IF (ionode) THEN
|
|
dirname = 'RESTART' // int_to_char( ndr )
|
|
IF ( LEN( outdir ) > 1 ) THEN
|
|
strlen = index(outdir,' ') - 1
|
|
dirname = outdir(1:strlen) // '/' // dirname
|
|
END IF
|
|
|
|
filename = TRIM( dirname ) // '/' // 'restart.xml'
|
|
INQUIRE( FILE = TRIM( filename ), EXIST = restart_p )
|
|
|
|
auto_check = restart_p
|
|
END IF
|
|
CALL mp_bcast(auto_check, ionode_id, world_comm)
|
|
|
|
return
|
|
|
|
END FUNCTION auto_check
|
|
|
|
|
|
!-----------------------------------------------------------------------
|
|
SUBROUTINE init_autopilot
|
|
!---------------------------------------------------------------------
|
|
!! INITIALIZE AUTOPILOT: must be done, even if not in use.
|
|
!! Add this as a call in read_cards.f90 sub: card_default_values.
|
|
!
|
|
IMPLICIT NONE
|
|
!
|
|
integer :: event
|
|
|
|
pause_p = .FALSE.
|
|
|
|
! Initialize all events to an iteration that should never occur
|
|
DO event=1,max_event_step
|
|
event_step(event) = MAX_INT
|
|
ENDDO
|
|
|
|
n_events = 0
|
|
n_rules = 0
|
|
event_index = 1
|
|
|
|
! Initialize here
|
|
! &CONTROL
|
|
event_isave(:) = .false.
|
|
event_iprint(:) = .false.
|
|
event_tprint(:) = .false.
|
|
event_dt(:) = .false.
|
|
! &SYSTEM
|
|
! &ELECTRONS
|
|
event_emass(:) = .false.
|
|
event_electron_dynamics(:) = .false.
|
|
event_electron_damping(:) = .false.
|
|
event_electron_orthogonalization(:) = .false.
|
|
|
|
|
|
! &IONS
|
|
event_ion_dynamics(:) = .false.
|
|
event_ion_damping(:) = .false.
|
|
event_ion_temperature(:) = .false.
|
|
event_tempw(:) = .false.
|
|
! &CELL
|
|
! &PHONON
|
|
|
|
rule_isave(:) = 0
|
|
rule_iprint(:) = 0
|
|
rule_tprint(:) = .FALSE.
|
|
rule_dt(:) = 0.0_DP
|
|
rule_emass(:) = 0.0_DP
|
|
rule_electron_dynamics(:) = 'NONE'
|
|
rule_electron_damping(:) = 0.0_DP
|
|
rule_ion_dynamics(:) = 'NONE'
|
|
rule_ion_damping(:) = 0.0_DP
|
|
rule_ion_temperature(:) = 'NOT_CONTROLLED'
|
|
rule_tempw(:) = 0.01_DP
|
|
|
|
END SUBROUTINE init_autopilot
|
|
|
|
|
|
|
|
!-----------------------------------------------------------------------
|
|
SUBROUTINE card_autopilot( input_line )
|
|
!--------------------------------------------------------------------
|
|
!! Called in READ_CARDS and in PARSE_MAILBOX.
|
|
!
|
|
USE io_global, ONLY: ionode
|
|
!
|
|
IMPLICIT NONE
|
|
!
|
|
CHARACTER(LEN=256) :: input_line
|
|
!
|
|
! ... local variables
|
|
!
|
|
INTEGER :: i, j, linelen
|
|
LOGICAL :: process_this_line = .FALSE.
|
|
LOGICAL :: endrules = .FALSE.
|
|
LOGICAL :: tend = .FALSE.
|
|
LOGICAL, SAVE :: tread = .FALSE.
|
|
LOGICAL, EXTERNAL :: matches
|
|
CHARACTER(LEN=1), EXTERNAL :: capital
|
|
|
|
!ASU: copied this here since it seems not to be executed during each
|
|
! call of the routine. Needed for multiple rules in same block
|
|
process_this_line = .FALSE.
|
|
endrules = .FALSE.
|
|
tend = .FALSE.
|
|
tread = .FALSE.
|
|
|
|
! This is so we do not read a autopilot card twice from the input file
|
|
IF (( .NOT. pilot_p ) .and. tread ) THEN
|
|
CALL errore( ' card_autopilot ', ' two occurrences', 2 )
|
|
END IF
|
|
|
|
! If this routined has been called from parse_mailbox
|
|
! the pilot_type should be set
|
|
IF ( pilot_p ) THEN
|
|
! IF its MANUAL then process this line first!
|
|
! other skip this line and get the next
|
|
IF (TRIM(pilot_type) .eq. 'MANUAL') THEN
|
|
process_this_line = .TRUE.
|
|
ELSE IF (TRIM(pilot_type) .eq. 'PILOT') THEN
|
|
process_this_line = .FALSE.
|
|
ELSE IF (TRIM(pilot_type) .eq. 'AUTO') THEN
|
|
process_this_line = .FALSE.
|
|
ELSE
|
|
IF( ionode ) WRITE(*,*) 'AUTOPILOT: UNRECOGNIZED PILOT TYPE!', TRIM(pilot_type), '===='
|
|
GO TO 10
|
|
END IF
|
|
ELSE
|
|
! this routine is called from read_cards
|
|
pilot_type = 'AUTO'
|
|
process_this_line = .FALSE.
|
|
END IF
|
|
|
|
j=0
|
|
! must use a local (j) since n_rules may not get updated correctly
|
|
DO WHILE ((.NOT. endrules) .and. j<=max_rules)
|
|
j=j+1
|
|
|
|
IF (j > max_rules) THEN
|
|
CALL auto_error( ' AutoPilot ','Maximum Number of Dynamic Rules May Have Been Execced!')
|
|
go to 10
|
|
END IF
|
|
|
|
! Assume that pilot_p is an indicator and that
|
|
! this call to card_autopilot is from parse_mailbox
|
|
! and not from read_cards
|
|
IF(pilot_p) THEN
|
|
IF ( .NOT. process_this_line ) THEN
|
|
! get the next one
|
|
CALL read_line( input_line, end_of_file = tend)
|
|
END IF
|
|
ELSE
|
|
! from read_cards
|
|
CALL read_line( input_line, end_of_file = tend)
|
|
END IF
|
|
|
|
linelen = LEN_TRIM( input_line )
|
|
|
|
DO i = 1, linelen
|
|
input_line( i : i ) = capital( input_line( i : i ) )
|
|
END DO
|
|
|
|
! If ENDRULES isnt found, add_rule will fail
|
|
! and we run out of line anyway
|
|
|
|
IF ( tend .or. matches( 'ENDRULES', input_line ) ) GO TO 10
|
|
|
|
! Assume this is a rule
|
|
CALL ADD_RULE(input_line)
|
|
! now, do not process this line anymore
|
|
! make sure we get the next one
|
|
process_this_line = .FALSE.
|
|
|
|
END DO
|
|
|
|
IF( ionode ) WRITE(*,*) 'AUTOPILOT SET'
|
|
|
|
10 CONTINUE
|
|
|
|
END SUBROUTINE card_autopilot
|
|
|
|
|
|
|
|
|
|
!-----------------------------------------------------------------------
|
|
SUBROUTINE add_rule( input_line )
|
|
!---------------------------------------------------------------------
|
|
!! ADD RULE
|
|
!
|
|
USE io_global, ONLY: ionode
|
|
!
|
|
IMPLICIT NONE
|
|
!
|
|
CHARACTER(LEN=256) :: input_line
|
|
!
|
|
! ... local variables
|
|
!
|
|
integer :: i, linelen
|
|
integer :: eq1_pos, eq2_pos, plus_pos, colon_pos
|
|
CHARACTER(LEN=32) :: var_label
|
|
CHARACTER(LEN=32) :: value_str
|
|
INTEGER :: on_step, now_step, plus_step
|
|
integer :: ios
|
|
integer :: event
|
|
|
|
LOGICAL, EXTERNAL :: matches
|
|
LOGICAL :: new_event
|
|
|
|
|
|
! this is a temporary local variable
|
|
event = n_events
|
|
|
|
! important for parsing
|
|
i=0
|
|
eq1_pos = 0
|
|
eq2_pos = 0
|
|
plus_pos = 0
|
|
colon_pos = 0
|
|
|
|
linelen=LEN_TRIM( input_line )
|
|
|
|
|
|
! Attempt to get PLUS SYMBOL
|
|
i = 1
|
|
do while( (plus_pos .eq. 0) .and. (i <= linelen) )
|
|
i = i + 1
|
|
if(input_line( i : i ) .eq. '+') then
|
|
plus_pos = i
|
|
endif
|
|
end do
|
|
|
|
! Attempt to get a colon
|
|
i = 1
|
|
do while( (colon_pos .eq. 0) .and. (i <= linelen) )
|
|
i = i + 1
|
|
if(input_line( i : i ) .eq. ':') then
|
|
colon_pos = i
|
|
endif
|
|
end do
|
|
|
|
! Attempt to get first equals
|
|
i = 1
|
|
do while( (eq1_pos .eq. 0) .and. (i <= linelen) )
|
|
i = i + 1
|
|
if(input_line( i : i ) .eq. '=') then
|
|
eq1_pos = i
|
|
endif
|
|
end do
|
|
|
|
|
|
! Attempt to get second equals
|
|
if ((eq1_pos .ne. 0) .and. (eq1_pos < colon_pos)) then
|
|
i = colon_pos + 1
|
|
do while( (eq2_pos .eq. 0) .and. (i <= linelen) )
|
|
i = i + 1
|
|
if(input_line( i : i ) .eq. '=') then
|
|
eq2_pos = i
|
|
endif
|
|
end do
|
|
endif
|
|
|
|
! Complain if there is a bad parse
|
|
if (colon_pos .eq. 0) then
|
|
call auto_error( ' AutoPilot ','Missing colon separator')
|
|
go to 20
|
|
endif
|
|
|
|
if (eq1_pos .eq. 0) then
|
|
call auto_error( ' AutoPilot ','Missing equals sign')
|
|
go to 20
|
|
endif
|
|
|
|
if ((plus_pos > 0) .and. (eq1_pos < colon_pos)) then
|
|
call auto_error( ' AutoPilot ','equals and plus found prior to colon')
|
|
go to 20
|
|
endif
|
|
|
|
|
|
!================================================================================
|
|
! Detect events
|
|
IF ( (pilot_type .eq. 'MANUAL') .or. (pilot_type .eq. 'PILOT') ) THEN
|
|
!-------------------------------------------
|
|
!Do the equivalent of the following:
|
|
!READ(input_line, *) now_label, plus_label1, plus_step, colon_label, var_label, eq_label2, value_str
|
|
!Format:
|
|
! [NOW [+ plus_step] :] var_label = value_str
|
|
!-------------------------------------------
|
|
|
|
! if there is a NOW, get it and try to get plus and plus_step
|
|
|
|
IF ( matches( 'NOW', input_line ) ) THEN
|
|
! Attempt to get PLUS_STEP
|
|
plus_step = 0
|
|
! if all is non-trivial, read a PLUS_STEP
|
|
if ((plus_pos > 0) .and. (colon_pos > plus_pos)) then
|
|
! assume a number lies between
|
|
read(input_line(plus_pos+1:colon_pos-1),*,iostat=ios) plus_step
|
|
if(ios .ne. 0) then
|
|
CALL auto_error( ' AutoPilot ','Value Type Mismatch on NOW line!')
|
|
go to 20
|
|
end if
|
|
end if
|
|
! set NOW event
|
|
now_step = current_nfi + plus_step
|
|
ELSE
|
|
! set NOW event
|
|
now_step = current_nfi
|
|
END IF
|
|
|
|
|
|
!================================================================================
|
|
! set event
|
|
!
|
|
! Heres where it get interesting
|
|
! We may have a new event , or not! :)
|
|
|
|
IF ((event-1) .gt. 0) THEN
|
|
IF ( now_step .lt. event_step(event-1)) THEN
|
|
IF( ionode ) write(*,*) ' AutoPilot: current input_line', input_line
|
|
CALL auto_error( ' AutoPilot ','Dynamic Rule Event Out of Order!')
|
|
go to 20
|
|
ENDIF
|
|
ENDIF
|
|
|
|
IF (event .eq. 0) THEN
|
|
new_event = .true.
|
|
ELSEIF ( now_step .gt. event_step(event)) THEN
|
|
new_event = .true.
|
|
ELSE
|
|
new_event = .false.
|
|
ENDIF
|
|
|
|
IF ( new_event ) THEN
|
|
! new event
|
|
event = event + 1
|
|
|
|
IF (event > max_event_step) THEN
|
|
IF( ionode ) write(*,*) ' AutoPilot: current input_line', input_line
|
|
CALL auto_error( ' AutoPilot ','Maximum Number of Dynamic Rule Event Exceeded!')
|
|
go to 20
|
|
ENDIF
|
|
|
|
event_step(event) = now_step
|
|
n_events = event
|
|
ENDIF
|
|
|
|
|
|
ELSE IF ( matches( 'ON_STEP', input_line ) ) THEN
|
|
! Assuming pilot_type is AUTO
|
|
! if it isnt and ON_STEP these rules wont take anyway
|
|
|
|
!-------------------------------------------
|
|
!Do the equivalent of the following:
|
|
!READ(input_line, *) on_step_label, eq_label1, on_step, colon_label, var_label, eq_label2, value_str
|
|
!Format:
|
|
! ON_STEP = on_step : var_label = value_str
|
|
!-------------------------------------------
|
|
|
|
IF( ionode ) write(*,*) 'ADD_RULE: POWER STEERING'
|
|
|
|
! Attempt to get ON_STEP
|
|
on_step = MAX_INT
|
|
! if all is non-trivial, read a PLUS_STEP
|
|
if ((eq1_pos > 0) .and. (colon_pos > eq1_pos)) then
|
|
! assume a number lies between
|
|
read(input_line(eq1_pos+1:colon_pos-1),*,iostat=ios) on_step
|
|
if(ios .ne. 0) then
|
|
CALL auto_error( ' AutoPilot ','Value Type Mismatch on ON_STEP line!')
|
|
go to 20
|
|
end if
|
|
end if
|
|
|
|
|
|
|
|
!================================================================================
|
|
! set event
|
|
!
|
|
! Heres where it get interesting
|
|
! We may have a new event , or not! :)
|
|
|
|
|
|
IF ( ((event-1) .gt. 0)) THEN
|
|
IF ( on_step .lt. event_step(event-1)) THEN
|
|
IF( ionode ) write(*,*) ' AutoPilot: current input_line', input_line
|
|
CALL auto_error( ' AutoPilot ','Dynamic Rule Event Out of Order!')
|
|
go to 20
|
|
ENDIF
|
|
ENDIF
|
|
|
|
|
|
IF (event .eq. 0) THEN
|
|
new_event = .true.
|
|
ELSEIF (on_step .gt. event_step(event)) THEN
|
|
new_event = .true.
|
|
ELSE
|
|
new_event = .false.
|
|
ENDIF
|
|
|
|
IF (new_event) THEN
|
|
! new event
|
|
event = event + 1
|
|
IF (event > max_event_step) THEN
|
|
IF( ionode ) write(*,*) ' AutoPilot: current input_line', input_line
|
|
CALL auto_error( ' AutoPilot ','Maximum Number of Dynamic Rule Event Exceeded!')
|
|
go to 20
|
|
ENDIF
|
|
event_step(event) = on_step
|
|
n_events = event
|
|
ENDIF
|
|
|
|
END IF ! Event Detection Complete
|
|
|
|
|
|
!-------------------------------------
|
|
! Now look for a label and a value
|
|
!-------------------------------------
|
|
|
|
if (eq2_pos .eq. 0) then
|
|
var_label = input_line(colon_pos+1: eq1_pos-1)
|
|
read( input_line(eq1_pos+1:linelen), *, iostat=ios) value_str
|
|
if(ios .ne. 0) then
|
|
CALL auto_error( ' AutoPilot ','Value Type Mismatch on NOW_STEP line!')
|
|
go to 20
|
|
end if
|
|
else
|
|
var_label = input_line(colon_pos+1: eq2_pos-1)
|
|
read( input_line(eq2_pos+1:linelen), *, iostat=ios) value_str
|
|
if(ios .ne. 0) then
|
|
CALL auto_error( ' AutoPilot ','Value Type Mismatch on ON_STEP line!')
|
|
go to 20
|
|
end if
|
|
endif
|
|
|
|
! The Assignment must lie outside the new event scope since
|
|
! there can exists more than one rule per event
|
|
|
|
IF ( (n_rules+1) .gt. max_rules) THEN
|
|
IF( ionode ) write(*,*) ' AutoPilot: current n_rules', n_rules
|
|
CALL auto_error( ' AutoPilot ', ' invalid number of rules ')
|
|
go to 20
|
|
END IF
|
|
|
|
call assign_rule(event, var_label, value_str)
|
|
|
|
!IF( ionode ) write(*,*) ' Number of rules: ', n_rules
|
|
|
|
FLUSH(6)
|
|
|
|
20 CONTINUE
|
|
|
|
END SUBROUTINE add_rule
|
|
|
|
|
|
!-----------------------------------------------------------------------
|
|
SUBROUTINE assign_rule(event, var, value)
|
|
!---------------------------------------------------------------------
|
|
!! ASSIGN RULE
|
|
!
|
|
USE io_global, ONLY: ionode
|
|
!
|
|
IMPLICIT NONE
|
|
!
|
|
INTEGER :: event
|
|
CHARACTER(LEN=32) :: var
|
|
CHARACTER(LEN=32) :: value
|
|
!
|
|
! ... local variables
|
|
!
|
|
INTEGER :: i, varlen
|
|
INTEGER :: int_value
|
|
LOGICAL :: logical_value
|
|
REAL :: real_value
|
|
REAL(DP) :: realDP_value
|
|
LOGICAL :: assigned
|
|
LOGICAL, EXTERNAL :: matches
|
|
CHARACTER(LEN=1), EXTERNAL :: capital
|
|
|
|
|
|
var = TRIM(var)
|
|
varlen = LEN_TRIM(var)
|
|
|
|
DO i = 1, varlen
|
|
var( i : i ) = capital( var( i : i ) )
|
|
END DO
|
|
|
|
|
|
IF( ionode ) write(*,'(" Reading rule: ",A20,A20)' ) var, value
|
|
assigned = .TRUE.
|
|
|
|
IF ( matches( "ISAVE", var ) ) THEN
|
|
read(value, *) int_value
|
|
rule_isave(event) = int_value
|
|
event_isave(event) = .true.
|
|
ELSEIF ( matches( "IPRINT", var ) ) THEN
|
|
read(value, *) int_value
|
|
rule_iprint(event) = int_value
|
|
event_iprint(event) = .true.
|
|
ELSEIF ( matches( "TPRINT", var ) ) THEN
|
|
read(value, *) logical_value
|
|
rule_tprint(event) = logical_value
|
|
event_tprint(event) = .true.
|
|
ELSEIF ( matches( "DT", var ) ) THEN
|
|
read(value, *) real_value
|
|
rule_dt(event) = real_value
|
|
event_dt(event) = .true.
|
|
!IF( ionode ) write(*,*) 'RULE_DT', rule_dt(event), 'EVENT', event
|
|
ELSEIF ( matches( "EMASS", var ) ) THEN
|
|
read(value, *) realDP_value
|
|
rule_emass(event) = realDP_value
|
|
event_emass(event) = .true.
|
|
ELSEIF ( matches( "ELECTRON_DYNAMICS", var ) ) THEN
|
|
read(value, *) value
|
|
if ((value .ne. 'SD') .and. (value .ne. 'VERLET') .and. (value .ne. 'DAMP') &
|
|
.and. (value .ne. 'NONE') .and. (value .ne. 'CG') ) then
|
|
call auto_error(' autopilot ',' unknown electron_dynamics '//trim(value) )
|
|
assigned = .FALSE.
|
|
go to 40
|
|
endif
|
|
rule_electron_dynamics(event) = value
|
|
event_electron_dynamics(event) = .true.
|
|
ELSEIF ( matches( "ELECTRON_DAMPING", var ) ) THEN
|
|
read(value, *) realDP_value
|
|
rule_electron_damping(event) = realDP_value
|
|
event_electron_damping(event) = .true.
|
|
ELSEIF ( matches( "ION_DYNAMICS", var ) ) THEN
|
|
read(value, *) value
|
|
if ((value .ne. 'SD') .and. (value .ne. 'VERLET') .and. (value .ne. 'DAMP') .and. (value .ne. 'NONE')) then
|
|
call auto_error(' autopilot ',' unknown ion_dynamics '//trim(value) )
|
|
assigned = .FALSE.
|
|
go to 40
|
|
endif
|
|
rule_ion_dynamics(event) = value
|
|
event_ion_dynamics(event) = .true.
|
|
ELSEIF ( matches( "ORTHOGONALIZATION", var) ) THEN
|
|
read(value, *) value
|
|
if ( (value .ne. 'ORTHO') .and. (value .ne. 'GRAM-SCHMIDT') ) then
|
|
call auto_error(' autopilot ',' unknown orthogonalization '//trim(value) )
|
|
assigned = .false.
|
|
go to 40
|
|
endif
|
|
rule_electron_orthogonalization(event) = value
|
|
event_electron_orthogonalization(event) = .true.
|
|
ELSEIF ( matches( "ION_DAMPING", var ) ) THEN
|
|
read(value, *) realDP_value
|
|
rule_ion_damping(event) = realDP_value
|
|
event_ion_damping(event) = .true.
|
|
ELSEIF ( matches( "ION_TEMPERATURE", var ) ) THEN
|
|
read(value, *) value
|
|
if ((value .ne. 'NOSE') .and. (value .ne. 'NOT_CONTROLLED') .and. (value .ne. 'RESCALING')) then
|
|
call auto_error(' autopilot ',' unknown ion_temperature '//trim(value) )
|
|
assigned = .FALSE.
|
|
go to 40
|
|
endif
|
|
rule_ion_temperature(event) = value
|
|
event_ion_temperature(event) = .true.
|
|
ELSEIF ( matches( "TEMPW", var ) ) THEN
|
|
read(value, *) realDP_value
|
|
rule_tempw(event) = realDP_value
|
|
event_tempw(event) = .true.
|
|
ELSEIF ( matches( "NHPCL", var ) ) THEN
|
|
read(value, *) int_value
|
|
rule_nhpcl(event) = int_value
|
|
event_nhpcl(event) = .true.
|
|
ELSEIF ( matches( "FNOSEP", var ) ) THEN
|
|
read(value, *) realDP_value
|
|
rule_fnosep(event) = realDP_value
|
|
event_fnosep(event) = .true.
|
|
ELSE
|
|
CALL auto_error( 'autopilot', ' ASSIGN_RULE: FAILED '//trim(var)//' '//trim(value) )
|
|
END IF
|
|
|
|
40 if (assigned) then
|
|
n_rules = n_rules + 1
|
|
else
|
|
IF( ionode ) write(*,*) ' Autopilot: Rule Assignment Failure '
|
|
CALL auto_error( 'autopilot', ' ASSIGN_RULE: FAILED '//trim(var)//' '//trim(value) )
|
|
endif
|
|
|
|
END SUBROUTINE assign_rule
|
|
|
|
|
|
|
|
!-----------------------------------------------------------------------
|
|
SUBROUTINE parse_mailbox ()
|
|
!---------------------------------------------------------------------
|
|
!! Read the mailbox with a mailbox parser:
|
|
!
|
|
!! * if it starts with ON_STEP, then apply to event table etc;
|
|
!! * if not the try to establish that its a variable to set right now.
|
|
!
|
|
USE io_global, ONLY: ionode
|
|
!
|
|
IMPLICIT NONE
|
|
!
|
|
INTEGER :: i
|
|
CHARACTER(LEN=256) :: input_line
|
|
LOGICAL :: tend
|
|
|
|
CHARACTER(LEN=1), EXTERNAL :: capital
|
|
LOGICAL, EXTERNAL :: matches
|
|
|
|
|
|
! we can use this parser routine, since parse_unit=pilot_unit
|
|
CALL read_line( input_line, end_of_file=tend )
|
|
IF (tend) GO TO 50
|
|
|
|
DO i = 1, LEN_TRIM( input_line )
|
|
input_line( i : i ) = capital( input_line( i : i ) )
|
|
END DO
|
|
|
|
! This conditional implements the PAUSE feature calling init_auto_pilot,
|
|
! will reset this modules global PAUSE_P variable to FALSE
|
|
IF ( matches( "PAUSE", input_line ) .or. &
|
|
matches( "SLEEP", input_line ) .or. &
|
|
matches( "HOVER", input_line ) .or. &
|
|
matches( "WAIT", input_line ) .or. &
|
|
matches( "HOLD", input_line ) ) THEN
|
|
|
|
IF( ionode ) write(*,*) 'SLEEPING'
|
|
IF( ionode ) write(*,*) 'INPUT_LINE=', input_line
|
|
pause_p = .TRUE.
|
|
! now you can pass continue to resume
|
|
ELSE IF (matches( "CONTINUE", input_line ) .or. &
|
|
matches( "RESUME", input_line ) ) THEN
|
|
IF( ionode ) write(*,*) 'RUNNING'
|
|
IF( ionode ) write(*,*) 'INPUT_LINE=', input_line
|
|
pause_p = .FALSE.
|
|
|
|
! Now just quit this subroutine
|
|
ELSE
|
|
! Also, We didnt see a PAUSE cmd!
|
|
pause_p = .FALSE.
|
|
|
|
! now lets detect the mode for card_autopilot
|
|
! even though this line will be passed to it the first time
|
|
|
|
IF ( matches( "AUTOPILOT", TRIM(input_line) ) ) THEN
|
|
IF( ionode ) WRITE(*,*) ' New autopilot course detected'
|
|
pilot_type ='AUTO'
|
|
ELSE IF (matches( "PILOT", TRIM(input_line) ) ) THEN
|
|
IF( ionode ) WRITE(*,*) ' Relative pilot course correction detected'
|
|
pilot_type ='PILOT'
|
|
ELSE IF (matches( "NOW", TRIM(input_line) ) ) THEN
|
|
IF( ionode ) WRITE(*,*) ' Manual piloting detected'
|
|
pilot_type ='MANUAL'
|
|
ELSE
|
|
! Well lets just pause since this guys is throwing trash
|
|
IF( ionode ) WRITE(*,*) ' Mailbox contents not understood: pausing'
|
|
pause_p = .TRUE.
|
|
ENDIF
|
|
|
|
END IF
|
|
|
|
IF (pause_p) GO TO 50
|
|
|
|
|
|
! ok if one adds a rule during steering`
|
|
! event table must be cleared (from steer point) forward
|
|
!
|
|
! Every nodes gets this (and the call to card_autopilot
|
|
! which calls add_rule, which calls assign_rule, etc
|
|
! In this way we sync the event table
|
|
! Then we shouldn't have to sync employ_rules variable
|
|
! changes, or their subroutine side effects (like ions_nose_init)
|
|
|
|
CALL init_autopilot()
|
|
|
|
CALL card_autopilot( input_line )
|
|
|
|
50 CONTINUE
|
|
|
|
end subroutine parse_mailbox
|
|
|
|
|
|
END MODULE autopilot
|
|
|