32 implicit none ;
private 46 integer,
parameter ::
mlen = 1240
49 integer :: unitall = -1
50 integer :: unitshort = -1
51 integer :: unitlayout = -1
52 logical :: filesareopen = .false.
53 character(len=mLen) :: docfilebase =
'' 55 logical :: complete = .true.
56 logical :: minimal = .true.
57 logical :: layout = .true.
58 logical :: definesyntax = .false.
59 logical :: warnonconflicts = .false.
60 integer :: commentcolumn = 32
62 character(len=240) :: blockprefix =
'' 67 character(len=80) :: name
68 character(len=620) :: msg
80 character(len=*),
intent(in) :: varname, desc, units
83 character(len=mLen) :: mesg
85 if (.not. (
is_root_pe() .and.
associated(doc)))
return 88 if (doc%filesAreOpen)
then 89 numspc = max(1,doc%commentColumn-8-len_trim(varname))
90 mesg =
"#define "//trim(varname)//repeat(
" ",numspc)//
"!" 91 if (len_trim(units) > 0) mesg = trim(mesg)//
" ["//trim(units)//
"]" 98 subroutine doc_param_logical(doc, varname, desc, units, val, default, layoutParam)
100 character(len=*),
intent(in) :: varname, desc, units
101 logical,
intent(in) :: val
102 logical,
optional,
intent(in) :: default
103 logical,
optional,
intent(in) :: layoutParam
105 character(len=mLen) :: mesg
106 logical :: equalsDefault
108 if (.not. (
is_root_pe() .and.
associated(doc)))
return 111 if (doc%filesAreOpen)
then 118 equalsdefault = .false.
119 if (
present(default))
then 120 if (val .eqv. default) equalsdefault = .true.
135 character(len=*),
intent(in) :: varname, desc, units
136 logical,
intent(in) :: vals(:)
137 logical,
optional,
intent(in) :: default
138 logical,
optional,
intent(in) :: layoutParam
141 character(len=mLen) :: mesg
142 character(len=mLen) :: valstring
143 logical :: equalsDefault
145 if (.not. (
is_root_pe() .and.
associated(doc)))
return 148 if (doc%filesAreOpen)
then 150 do i=2,min(
size(vals),128)
160 equalsdefault = .false.
161 if (
present(default))
then 162 equalsdefault = .true.
163 do i=1,
size(vals) ;
if (vals(i) .neqv. default) equalsdefault = .false. ;
enddo 176 subroutine doc_param_int(doc, varname, desc, units, val, default, layoutParam)
178 character(len=*),
intent(in) :: varname, desc, units
179 integer,
intent(in) :: val
180 integer,
optional,
intent(in) :: default
181 logical,
optional,
intent(in) :: layoutParam
183 character(len=mLen) :: mesg
184 character(len=doc%commentColumn) :: valstring
185 logical :: equalsDefault
187 if (.not. (
is_root_pe() .and.
associated(doc)))
return 190 if (doc%filesAreOpen)
then 194 equalsdefault = .false.
195 if (
present(default))
then 196 if (val == default) equalsdefault = .true.
197 mesg = trim(mesg)//
" default = "//(trim(
int_string(default)))
207 character(len=*),
intent(in) :: varname, desc, units
208 integer,
intent(in) :: vals(:)
209 integer,
optional,
intent(in) :: default
210 logical,
optional,
intent(in) :: layoutParam
213 character(len=mLen) :: mesg
214 character(len=mLen) :: valstring
215 logical :: equalsDefault
217 if (.not. (
is_root_pe() .and.
associated(doc)))
return 220 if (doc%filesAreOpen)
then 222 do i=2,min(
size(vals),128)
223 valstring = trim(valstring)//
", "//trim(
int_string(vals(i)))
228 equalsdefault = .false.
229 if (
present(default))
then 230 equalsdefault = .true.
231 do i=1,
size(vals) ;
if (vals(i) /= default) equalsdefault = .false. ;
enddo 232 mesg = trim(mesg)//
" default = "//(trim(
int_string(default)))
241 subroutine doc_param_real(doc, varname, desc, units, val, default)
243 character(len=*),
intent(in) :: varname, desc, units
244 real,
intent(in) :: val
245 real,
optional,
intent(in) :: default
247 character(len=mLen) :: mesg
248 character(len=doc%commentColumn) :: valstring
249 logical :: equalsDefault
251 if (.not. (
is_root_pe() .and.
associated(doc)))
return 254 if (doc%filesAreOpen)
then 258 equalsdefault = .false.
259 if (
present(default))
then 260 if (val == default) equalsdefault = .true.
261 mesg = trim(mesg)//
" default = "//trim(
real_string(default))
271 character(len=*),
intent(in) :: varname, desc, units
272 real,
intent(in) :: vals(:)
273 real,
optional,
intent(in) :: default
276 character(len=mLen) :: mesg
277 character(len=mLen) :: valstring
278 logical :: equalsDefault
280 if (.not. (
is_root_pe() .and.
associated(doc)))
return 283 if (doc%filesAreOpen)
then 288 equalsdefault = .false.
289 if (
present(default))
then 290 equalsdefault = .true.
291 do i=1,
size(vals) ;
if (vals(i) /= default) equalsdefault = .false. ;
enddo 292 mesg = trim(mesg)//
" default = "//trim(
real_string(default))
301 subroutine doc_param_char(doc, varname, desc, units, val, default, layoutParam)
303 character(len=*),
intent(in) :: varname, desc, units
304 character(len=*),
intent(in) :: val
305 character(len=*),
optional,
intent(in) :: default
306 logical,
optional,
intent(in) :: layoutParam
308 character(len=mLen) :: mesg
309 logical :: equalsDefault
311 if (.not. (
is_root_pe() .and.
associated(doc)))
return 314 if (doc%filesAreOpen)
then 317 equalsdefault = .false.
318 if (
present(default))
then 319 if (trim(val) == trim(default)) equalsdefault = .true.
320 mesg = trim(mesg)//
' default = "'//trim(adjustl(default))//
'"' 331 character(len=*),
intent(in) :: blockName
332 character(len=*),
optional,
intent(in) :: desc
334 character(len=mLen) :: mesg
335 character(len=doc%commentColumn) :: valstring
337 if (.not. (
is_root_pe() .and.
associated(doc)))
return 340 if (doc%filesAreOpen)
then 341 mesg = trim(blockname)//
'%' 343 if (
present(desc))
then 349 doc%blockPrefix = trim(doc%blockPrefix)//trim(blockname)//
'%' 354 character(len=*),
intent(in) :: blockName
356 character(len=mLen) :: mesg
357 character(len=doc%commentColumn) :: valstring
360 if (.not. (
is_root_pe() .and.
associated(doc)))
return 363 if (doc%filesAreOpen)
then 364 mesg =
'%'//trim(blockname)
368 i = index(trim(doc%blockPrefix), trim(blockname)//
'%', .true.)
370 doc%blockPrefix = trim(doc%blockPrefix(1:i-1))
376 subroutine doc_param_time(doc, varname, desc, units, val, default, layoutParam)
378 character(len=*),
intent(in) :: varname, desc, units
379 type(time_type),
intent(in) :: val
380 type(time_type),
optional,
intent(in) :: default
381 logical,
optional,
intent(in) :: layoutParam
385 character(len=mLen) :: mesg
386 logical :: equalsDefault
388 if (.not. (
is_root_pe() .and.
associated(doc)))
return 391 equalsdefault = .false.
392 if (doc%filesAreOpen)
then 393 numspc = max(1,doc%commentColumn-18-len_trim(varname))
394 mesg =
"#define "//trim(varname)//
" Time-type"//repeat(
" ",numspc)//
"!" 395 if (len_trim(units) > 0) mesg = trim(mesg)//
" ["//trim(units)//
"]" 405 character(len=*),
intent(in) :: vmesg, desc
406 logical,
optional,
intent(in) :: valueWasDefault
407 integer,
optional,
intent(in) :: indent
408 logical,
optional,
intent(in) :: layoutParam
409 character(len=mLen) :: mesg
410 integer :: start_ind = 1, end_ind, indnt, tab, len_tab, len_nl
411 logical :: all, short, layout
414 if (
present(layoutparam)) layout = layoutparam
415 all = doc%complete .and. (doc%unitAll > 0) .and. .not. layout
416 short = doc%minimal .and. (doc%unitShort > 0) .and. .not. layout
417 if (
present(valuewasdefault)) short = short .and. (.not. valuewasdefault)
419 if (all)
write(doc%unitAll,
'(a)') trim(vmesg)
420 if (short)
write(doc%unitShort,
'(a)') trim(vmesg)
421 if (layout)
write(doc%unitLayout,
'(a)') trim(vmesg)
423 if (len_trim(desc) == 0)
return 425 len_tab = len_trim(
"_\t_") - 2
426 len_nl = len_trim(
"_\n_") -2
428 indnt = doc%commentColumn ;
if (
present(indent)) indnt = indent
431 if (len_trim(desc(start_ind:)) < 1)
exit 433 end_ind = index(desc(start_ind:),
"\n")
435 if (end_ind > 0)
then 436 mesg = repeat(
" ",indnt)//
"! "//trim(desc(start_ind:start_ind+end_ind-2))
437 start_ind = start_ind + end_ind - 1 + len_nl
439 do ; tab = index(mesg,
"\t")
441 mesg(tab:) =
" "//trim(mesg(tab+len_tab:))
443 if (all)
write(doc%unitAll,
'(a)') trim(mesg)
444 if (short)
write(doc%unitShort,
'(a)') trim(mesg)
445 if (layout)
write(doc%unitLayout,
'(a)') trim(mesg)
447 mesg = repeat(
" ",indnt)//
"! "//trim(desc(start_ind:))
448 do ; tab = index(mesg,
"\t")
450 mesg(tab:) =
" "//trim(mesg(tab+len_tab:))
452 if (all)
write(doc%unitAll,
'(a)') trim(mesg)
453 if (short)
write(doc%unitShort,
'(a)') trim(mesg)
454 if (layout)
write(doc%unitLayout,
'(a)') trim(mesg)
464 real,
intent(in) :: val
465 character(len=32) :: real_string
469 if ((abs(val) < 1.0e4) .and. (abs(val) >= 1.0e-3))
then 470 write(real_string,
'(F30.11)') val
472 write(real_string,
'(F30.12)') val
474 write(real_string,
'(F30.13)') val
476 write(real_string,
'(F30.14)') val
478 write(real_string,
'(F30.15)') val
480 write(real_string,
'(F30.16)') val
487 len = len_trim(real_string)
488 if ((len<2) .or. (real_string(len-1:len) ==
".0") .or. &
489 (real_string(len:len) /=
"0"))
exit 490 real_string(len:len) =
" " 492 elseif (val == 0.)
then 495 write(real_string(1:32),
'(ES23.14)') val
497 write(real_string(1:32),
'(ES23.15)') val
500 ind = index(real_string,
"0E")
502 if (real_string(ind-1:ind-1) ==
".")
exit 503 real_string = real_string(1:ind-1)//real_string(ind+1:)
506 real_string = adjustl(real_string)
510 character(len=1320) :: real_array_string
511 real,
intent(in) :: vals(:)
512 character(len=*),
optional :: sep
516 integer :: j, n, b, ns
518 character(len=10) :: separator
519 n=1 ; dowrite=.true. ; real_array_string=
'' ; b=1
520 if (
present(sep))
then 521 separator=sep ; ns=len(sep)
523 separator=
', ' ; ns=2
527 if (j<
size(vals))
then 528 if (vals(j)==vals(j+1))
then 535 write(real_array_string(b:),
'(A)') separator
541 write(real_array_string(b:),
'(A)') trim(
real_string(vals(j)))
543 n=1 ; b=len_trim(real_array_string)+1
549 character(len=*),
intent(in) :: str
550 real,
intent(in) :: val
551 logical :: testFormattedFloatIsReal
555 read(str(1:),*) scannedval
556 if (scannedval == val)
then 557 testformattedfloatisreal=.true.
559 testformattedfloatisreal=.false.
564 integer,
intent(in) :: val
565 character(len=24) :: int_string
567 write(int_string,
'(i24)') val
568 int_string = adjustl(int_string)
572 logical,
intent(in) :: val
573 character(len=24) :: logical_string
575 write(logical_string,
'(l24)') val
576 logical_string = adjustl(logical_string)
581 character(len=*),
intent(in) :: varName, valString, units
582 character(len=mLen) :: define_string
585 define_string = repeat(
" ",
mlen)
586 if (doc%defineSyntax)
then 587 define_string =
"#define "//trim(varname)//
" "//valstring
589 define_string = trim(varname)//
" = "//valstring
591 numspaces = max(1, doc%commentColumn - len_trim(define_string) )
592 define_string = trim(define_string)//repeat(
" ",numspaces)//
"!" 593 if (len_trim(units) > 0) define_string = trim(define_string)//
" ["//trim(units)//
"]" 598 character(len=*),
intent(in) :: varName, units
599 character(len=mLen) :: undef_string
602 undef_string = repeat(
" ",240)
603 undef_string =
"#undef "//trim(varname)
604 if (doc%defineSyntax)
then 605 undef_string =
"#undef "//trim(varname)
609 numspaces = max(1, doc%commentColumn - len_trim(undef_string) )
610 undef_string = trim(undef_string)//repeat(
" ",numspaces)//
"!" 611 if (len_trim(units) > 0) undef_string = trim(undef_string)//
" ["//trim(units)//
"]" 618 character(len=*),
intent(in) :: modname, desc
620 character(len=mLen) :: mesg
622 if (.not. (is_root_pe() .and.
associated(doc)))
return 625 if (doc%filesAreOpen)
then 627 mesg =
"! === module "//trim(modname)//
" ===" 634 character(len=*),
intent(in) :: modname, subname, desc
636 if (.not. (is_root_pe() .and.
associated(doc)))
return 643 character(len=*),
intent(in) :: modname, fnname, desc
645 if (.not. (is_root_pe() .and.
associated(doc)))
return 652 subroutine doc_init(docFileBase, doc, minimal, complete)
653 character(len=*),
intent(in) :: docFileBase
655 logical,
optional,
intent(in) :: minimal, complete
659 if (.not.
associated(doc))
then 663 doc%docFileBase = docfilebase
664 if (
present(minimal)) doc%minimal = minimal
665 if (
present(minimal)) doc%complete = complete
671 logical :: opened, new_file
673 character(len=240) :: fileName
675 if (.not. (is_root_pe() .and.
associated(doc)))
return 677 if ((len_trim(doc%docFileBase) > 0) .and. doc%complete .and. (doc%unitAll<0))
then 678 new_file = .true. ;
if (doc%unitAll /= -1) new_file = .false.
681 write(filename(1:240),
'(a)') trim(doc%docFileBase)//
'.all' 683 open(doc%unitAll, file=trim(filename), access=
'SEQUENTIAL', form=
'FORMATTED', &
684 action=
'WRITE', status=
'REPLACE', iostat=ios)
685 write(doc%unitAll,
'(a)') &
686 '! This file was written by the model and records all non-layout parameters used at run-time.' 688 open(doc%unitAll, file=trim(filename), access=
'SEQUENTIAL', form=
'FORMATTED', &
689 action=
'WRITE', status=
'OLD', position=
'APPEND', iostat=ios)
691 inquire(doc%unitAll, opened=opened)
692 if ((.not.opened) .or. (ios /= 0))
then 693 call mom_error(fatal,
"Failed to open doc file "//trim(filename)//
".")
695 doc%filesAreOpen = .true.
698 if ((len_trim(doc%docFileBase) > 0) .and. doc%minimal .and. (doc%unitShort<0))
then 699 new_file = .true. ;
if (doc%unitShort /= -1) new_file = .false.
702 write(filename(1:240),
'(a)') trim(doc%docFileBase)//
'.short' 704 open(doc%unitShort, file=trim(filename), access=
'SEQUENTIAL', form=
'FORMATTED', &
705 action=
'WRITE', status=
'REPLACE', iostat=ios)
706 write(doc%unitShort,
'(a)') &
707 '! This file was written by the model and records the non-default parameters used at run-time.' 709 open(doc%unitShort, file=trim(filename), access=
'SEQUENTIAL', form=
'FORMATTED', &
710 action=
'WRITE', status=
'OLD', position=
'APPEND', iostat=ios)
712 inquire(doc%unitShort, opened=opened)
713 if ((.not.opened) .or. (ios /= 0))
then 714 call mom_error(fatal,
"Failed to open doc file "//trim(filename)//
".")
716 doc%filesAreOpen = .true.
719 if ((len_trim(doc%docFileBase) > 0) .and. doc%layout .and. (doc%unitLayout<0))
then 720 new_file = .true. ;
if (doc%unitLayout /= -1) new_file = .false.
723 write(filename(1:240),
'(a)') trim(doc%docFileBase)//
'.layout' 725 open(doc%unitLayout, file=trim(filename), access=
'SEQUENTIAL', form=
'FORMATTED', &
726 action=
'WRITE', status=
'REPLACE', iostat=ios)
727 write(doc%unitLayout,
'(a)') &
728 '! This file was written by the model and records the layout parameters used at run-time.' 730 open(doc%unitLayout, file=trim(filename), access=
'SEQUENTIAL', form=
'FORMATTED', &
731 action=
'WRITE', status=
'OLD', position=
'APPEND', iostat=ios)
733 inquire(doc%unitLayout, opened=opened)
734 if ((.not.opened) .or. (ios /= 0))
then 735 call mom_error(fatal,
"Failed to open doc file "//trim(filename)//
".")
737 doc%filesAreOpen = .true.
745 integer :: find_unused_unit_number
747 do find_unused_unit_number=512,42,-1
748 inquire( find_unused_unit_number, opened=opened)
749 if (.not.opened)
exit 752 "doc_init failed to find an unused unit number.")
757 type(
link_msg),
pointer :: this, next
759 if (.not.
associated(doc))
return 761 if (doc%unitAll > 0)
then 766 if (doc%unitShort > 0)
then 771 if (doc%unitLayout > 0)
then 772 close(doc%unitLayout)
776 doc%filesAreOpen = .false.
778 this => doc%chain_msg
779 do while(
associated(this) )
790 character(len=*),
intent(in) :: varName, mesg
791 logical :: mesgHasBeenDocumented
793 type(
link_msg),
pointer :: newLink, this, last
795 mesghasbeendocumented = .false.
801 this => doc%chain_msg
802 do while(
associated(this) )
803 if (trim(doc%blockPrefix)//trim(varname) == trim(this%name))
then 804 mesghasbeendocumented = .true.
805 if (trim(mesg) == trim(this%msg))
return 807 if (mesg(1:1) ==
'!')
return 808 call mom_error(warning,
"Previous msg:"//trim(this%msg))
809 call mom_error(warning,
"New message :"//trim(mesg))
810 call mom_error(warning,
"Encountered inconsistent documentation line for parameter "&
811 //trim(varname)//
"!")
819 newlink%name = trim(doc%blockPrefix)//trim(varname)
820 newlink%msg = trim(mesg)
821 newlink%next => null()
822 if (.not.
associated(doc%chain_msg))
then 823 doc%chain_msg => newlink
825 if (.not.
associated(last))
call mom_error(fatal, &
826 "Unassociated LINK in mesgHasBeenDocumented: "//trim(mesg))
character(len=24) function logical_string(val)
subroutine doc_param_int_array(doc, varname, desc, units, vals, default, layoutParam)
character(len=32) function real_string(val)
subroutine doc_param_none(doc, varname, desc, units)
subroutine, public doc_end(doc)
subroutine, public doc_init(docFileBase, doc, minimal, complete)
subroutine, public doc_subroutine(doc, modname, subname, desc)
character(len=24) function int_string(val)
subroutine doc_param_logical(doc, varname, desc, units, val, default, layoutParam)
character(len=4), parameter string_true
subroutine open_doc_file(doc)
subroutine doc_param_real_array(doc, varname, desc, units, vals, default)
character(len=mlen) function undef_string(doc, varName, units)
character(len=mlen) function define_string(doc, varName, valString, units)
integer function find_unused_unit_number()
subroutine, public doc_closeblock(doc, blockName)
subroutine, public doc_openblock(doc, blockName, desc)
logical function, public is_root_pe()
subroutine writemessageanddesc(doc, vmesg, desc, valueWasDefault, indent, layoutParam)
subroutine doc_param_logical_array(doc, varname, desc, units, vals, default, layoutParam)
subroutine doc_param_int(doc, varname, desc, units, val, default, layoutParam)
subroutine doc_param_char(doc, varname, desc, units, val, default, layoutParam)
subroutine doc_param_time(doc, varname, desc, units, val, default, layoutParam)
character(len=5), parameter string_false
character(len=1320) function real_array_string(vals, sep)
subroutine doc_param_real(doc, varname, desc, units, val, default)
logical function testformattedfloatisreal(str, val)
subroutine, public doc_function(doc, modname, fnname, desc)
subroutine, public mom_error(level, message, all_print)
logical function mesghasbeendocumented(doc, varName, mesg)
subroutine, public doc_module(doc, modname, desc)