Skip to content

10. Unknown ends ​

march with a solver of unknown length, then a time loop left at steady state

Every bar so far knew its end: max_value steps, and the bar completed when current reached it. Two common loops do not fit that. An iterative solver runs until its residual is small enough, and nobody knows in advance how many iterations that takes. A time loop of 50 steps may reach a steady state at step 30 and leave early. march does both:

f90
call bar%initialize(template='solve {spinner:cyan} [{bar:20}] {count:yellow} iterations, {elapsed:blue}', &
                    indeterminate=.true., spinner_string='⠋', filled_char_string='#', empty_char_string='.')
residual = 1._R8P
iterations = 0
call bar%start
do while (residual > 1.e-6_R8P) ! how many iterations: unknown until done
   call advance
   residual = residual / 2._R8P
   iterations = iterations + 1
   call bar%update(current=real(iterations, R8P))
enddo
call bar%finish

While the solver runs:

$ march
solve ⠇ [........#####.......] 8 iterations, hh:mm:ss
  • indeterminate=.true. says that the total is unknown. current counts what is done, from min_value, and the bar never completes by itself: a block goes back and forth along the track, one cell per drawing, as the spinner turns.
  • {count} shows what is done (8), not 8/max; {speed}, if you ask for it, is what is done per second.
  • With nothing to measure against, an indeterminate bar has no percent, ETA or scale: asking for one stops the program in initialize, with a message.
  • bar%finish ends the bar: it draws the last update with the track full, ends the line and writes the summary, if asked.

Leaving a loop early ​

f90
steps = 50
call bar%initialize(prefix_string='march ', bracket_left_string='[', bracket_right_string=']', &
                    filled_char_string='#', empty_char_string='.', add_progress_percent=.true., &
                    progress_percent_color_fg='yellow', message_color_fg='green', width=30,     &
                    max_value=real(steps, R8P))
residual = 1._R8P
call bar%start
do step = 1, steps
   call advance
   residual = residual / 2._R8P
   call bar%update(current=real(step, R8P))
   if (residual < 1.e-9_R8P) exit ! steady state: the remaining steps are not needed
enddo
call bar%finish(message='steady state')

At the end:

$ march
solve ⠙ [####################] 20 iterations, hh:mm:ss
march [##################............]  60% steady state

Without finish, the exit would leave the bar running at 60%: on a terminal its line not ended and the cursor still hidden, the next output drawn over it. finish ends it where it is: the last update drawn, here with a message, the line ended, the cursor shown, the date and summary lines written if asked (the summary rate counts the 60% done). After finish, updates do nothing until the next start, as after 100%; on a bar not running, finish does nothing.

In a log, an indeterminate bar writes two lines, at the start and at the end: there is no 10% to wait for. A determinate bar finished early writes one last line, unless its last line already shows where it stopped.

For a livelier pulse, pulse_trail gives it a one-cell head and a fading trail, as a scanner: see Pulse trail.

What you learned

indeterminate bars, ended by finish; finish for a loop left before its end. Reference: finish.

Next: 11. A 1980s dashboard.

Released under the GPL v3, BSD 2-Clause, BSD 3-Clause or MIT License.