• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gyrokinetics / gs2 / 2078172070

03 Oct 2025 09:22AM UTC coverage: 10.835% (+0.03%) from 10.806%
2078172070

push

gitlab-ci

David Dickinson
Merged in experimental/user_controlled_output_base_name (pull request #1184)

5049 of 46599 relevant lines covered (10.83%)

119786.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

28.57
/src/command_line_handling.fpp
1
!> A collection of routines and types for dealing with the command line arguments
2
module command_line_handling
3
  implicit none
4
  private
5
  public :: command_line_settings_type, input_variable_set_type
6

7
  !> A type to represent our command line specification of input flags
8
  type input_variable_set_type
9
     character(len=:), allocatable :: raw_string
10
     character(len=:), allocatable :: namelist, variable, val
11
   contains
12
     procedure :: parse_raw_string
13
  end type input_variable_set_type
14

15
  !> Holds information reflecting command line options that have been set
16
  type command_line_settings_type
17
     logical :: initialised = .false.
18
     logical :: check_input = .false.
19
     logical :: parse_input = .false.
20
     character(len=:), allocatable :: input_file, output_base, exe_name, full_command
21
     type(input_variable_set_type), dimension(:), allocatable :: input_variable_set
22
   contains
23
     procedure :: parse_command_line
24
  end type command_line_settings_type
25

26
contains
27

28
  !> Parse command line arguments.
29
  !>
30
  !> This should be called before anything else, but especially before initialising MPI.
31
  subroutine parse_command_line(self)
4✔
32
    use git_version_mod, only: get_git_version
33
    use build_config, only : formatted_build_config
34
    class(command_line_settings_type), intent(in out) :: self
35
    integer :: arg_count, arg_n, arg_length
36
    logical :: skip_next
37
    character(len=:), allocatable :: argument
4✔
38
    character(len=*), parameter :: nl = new_line('a')
39
    character(len=*), parameter :: usage = &
40
         "gs2 [--version|-v] [--help|-h] [--build-config] [--check-input] [input file]" // nl // nl // &
41
         "GS2: A fast, flexible physicist's toolkit for gyrokinetics" // nl // &
42
         "For more help, see the documentation at https://gyrokinetics.gitlab.io/gs2/" // nl // &
43
         "or create an issue https://bitbucket.org/gyrokinetics/gs2/issues?status=open" // nl // &
44
         nl // &
45
         "  -h, --help           Print this message" // nl // &
46
         "  -v, --version        Print the GS2 version" // nl // &
47
         "  --build-config       Print the current build configuration" // nl // &
48
         "  --check-input FILE   Check input FILE for errors" // nl // &
49
         "  --parse-input FILE   Check we can parse the full input FILE" // nl // &
50
         "  --output-base <arg>  Sets the base name for output files" // nl // &
51
         "  --set <arg>          Override input variables. We expect <arg> to be " // nl // &
52
         "                       in the form: <namelist>:<variable>:<value>.     " // nl // &
53
         "  --restart            Run the provided case, forcing common flags for restarting"
54

55
    if (self%initialised) return
4✔
56

57
    arg_count = command_argument_count()
4✔
58

59
    ! Reset the instance
60
    select type(self)
61
    type is(command_line_settings_type)
62
       self = command_line_settings_type()
4✔
63
    end select
64

65
    ! Allocate arrays to zero length
66
    allocate(self%input_variable_set(0))
4✔
67

68
    ! Store the full command
69
    call get_command(length=arg_length)
4✔
70
    allocate(character(len=arg_length)::self%full_command)
4✔
71
    call get_command(self%full_command)
4✔
72

73
    ! Get the executable name
74
    arg_n = 0
4✔
75
    call get_command_argument(arg_n, length=arg_length)
4✔
76
    allocate(character(len=arg_length)::self%exe_name)
4✔
77
    call get_command_argument(arg_n, self%exe_name)
4✔
78

79
    skip_next = .false.
4✔
80

81
    do arg_n = 1, arg_count
4✔
82
       if (skip_next) then
4✔
83
          skip_next = .false.
×
84
          cycle
×
85
       end if
86

87
       call get_command_argument(arg_n, length=arg_length)
4✔
88
       if (allocated(argument)) deallocate(argument)
4✔
89
       allocate(character(len=arg_length)::argument)
4✔
90
       call get_command_argument(arg_n, argument)
4✔
91

92
       if ((argument == "--help") .or. (argument == "-h")) then
4✔
93
          write(*, '(a)') usage
×
94
          stop
×
95
       else if ((argument == "--version") .or. (argument == "-v")) then
4✔
96
          write(*, '("GS2 version ", a)') get_git_version()
2✔
97
          stop
2✔
98
       else if (argument == "--build-config") then
2✔
99
          write(*, '(a)') formatted_build_config()
2✔
100
          stop
2✔
101
       else if (argument == "--check-input") then
×
102
          self%check_input = .true.
×
103
       else if (argument == "--parse-input") then
×
104
          self%parse_input = .true.
×
105
       else if (argument == "--restart") then
×
106
          self%input_variable_set = [self%input_variable_set, &
×
107
               new_input_variable_set('init_g_knobs:ginit_option:many'), &
108
               new_input_variable_set('knobs:delt_option:check_restart'), &
109
               new_input_variable_set('gs2_diagnostics_knobs:append_old:T')]
×
110
       else if (argument == "--set") then
×
111
          call get_command_argument(arg_n + 1, length=arg_length)
×
112
          if (allocated(argument)) deallocate(argument)
×
113
          allocate(character(len=arg_length)::argument)
×
114
          call get_command_argument(arg_n + 1, argument)
×
115
          skip_next = .true.
×
116
          ! Note we parse immediately so may stop early if we encounter a malformed input
117
          ! This immediate parsing means that we _can't_ set the split_char on the command
118
          ! line without caring about the order of arguments.
119
          self%input_variable_set = [self%input_variable_set, &
×
120
               new_input_variable_set(argument)]
×
121
       else if (argument == "--output-base") then
×
122
          call get_command_argument(arg_n + 1, length=arg_length)
×
123
          if (allocated(argument)) deallocate(argument)
×
124
          allocate(character(len=arg_length)::self%output_base)
×
125
          call get_command_argument(arg_n + 1, self%output_base)
×
126
          skip_next = .true.
×
127
       else if (argument == "--") then
×
128
          exit
×
129
       else if (argument(1:1) == "-") then
×
130
          write(*,'(a)') "Error: Unrecognised argument '" // argument // "'. Usage is:" // nl // nl // usage
×
131
          error stop 2
×
132
       else !We assume that anything else must be the input file
133
          if (allocated(self%input_file)) then
×
134
             write(*,'(a)') "Error: We have detected multiple input files : " // nl &
135
                  // self%input_file // nl &
136
                  // argument // nl // "Aborting"
×
137
             error stop 2
×
138
          end if
139
          self%input_file = argument
×
140
       end if
141
    end do
142

143
    if (.not. allocated(self%input_file)) &
×
144
         error stop "Error: No input file detected in command line arguments."
×
145

146
    ! If output base not set default it to input file (minus the last three characters
147
    ! if they are '.in')
148
    if (.not. allocated(self%output_base)) then
×
149
       arg_length = len_trim (self%input_file)
×
150
       if (arg_length > 3)then
×
151
          if(self%input_file(arg_length - 2 : arg_length) == ".in") then
×
152
             self%output_base = self%input_file(1:arg_length-3)
×
153
          else
154
             self%output_base = self%input_file
×
155
          end if
156
       end if
157
    end if
158

159
    self%initialised = .true.
×
160
  end subroutine parse_command_line
×
161

162
  !> Create a new, fully initialised input_variable_set instance
163
  function new_input_variable_set(raw_string, split_char) result(self)
×
164
    type(input_variable_set_type) :: self
165
    character(len = *), intent(in) :: raw_string
166
    character, intent(in), optional :: split_char
167
    call self%parse_raw_string(raw_string, split_char)
×
168
  end function new_input_variable_set
×
169

170
  !> Tries to parse a passed string into input_variable_set_type
171
  !> Expect raw_string to be of the form
172
  !> `<namelist><split_char><variable><split_char><val>`
173
  subroutine parse_raw_string(self, raw_string, split_char)
×
174
    use optionals, only: get_option_with_default
×
175
    implicit none
176
    class(input_variable_set_type), intent(in out) :: self
177
    character(len = *), intent(in) :: raw_string
178
    character, intent(in), optional :: split_char
179
    character(len = :), allocatable :: working_string
×
180
    character :: split_c
181
    integer :: pos
182
    self%raw_string = raw_string
×
183
    split_c = get_option_with_default(split_char, ":")
×
184

185
    working_string = raw_string
×
186
    ! From f2023 we can use the split intrinsic function and variable error stop in 2018
187
    pos = index(working_string, split_c)
×
188
    if (pos == 0 .or. pos >= len_trim(working_string)) then
×
189
       write(*, '("Error parsing --set argument : ",A," (namelist)")') working_string
×
190
       error stop 2
×
191
    end if
192
    self%namelist = working_string(1 : pos-1)
×
193
    working_string = working_string(pos + 1:)
×
194

195
    pos = index(working_string, split_c)
×
196
    if (pos == 0 .or. pos >= len_trim(working_string)) then
×
197
       write(*, '("Error parsing --set argument : ",A," (variable)")') self%raw_string
×
198
       error stop 2
×
199
    end if
200
    self%variable = working_string(1 : pos-1)
×
201
    self%val = working_string(pos + 1:)
×
202
  end subroutine parse_raw_string
×
203

204
end module command_line_handling
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc