@@ -158,6 +158,8 @@ function read_input(filename) result(problems)
158158 ! Locals
159159 integer , parameter :: max_problems = 100
160160 integer :: fin, n, ierr
161+ type (ProblemDB), allocatable :: parsed_problems(:)
162+ character (512 ) :: line
161163
162164 call log_info(' Parsing input file: ' // trim (filename))
163165 open (newunit= fin, file= filename, &
@@ -166,29 +168,42 @@ function read_input(filename) result(problems)
166168
167169 n = 1
168170 do
171+ do
172+ read (fin, ' (a)' , iostat= ierr) line
173+ if (ierr /= 0 ) exit
174+ line = adjustl (line)
175+ if (.not. is_empty(line)) then
176+ backspace (fin)
177+ exit
178+ end if
179+ end do
180+ if (ierr /= 0 ) exit
181+
169182 call log_info(' Parsing problem specification ' // to_str(n))
170183 call assert(n <= max_problems, " Maximum problem count exceeded." )
171- problems(n) = read_problem(fin, ierr)
184+ call read_problem(fin, problems(n) , ierr)
172185 if (ierr /= 0 ) exit
173186 n = n+1
174187 end do
175188
176- problems = problems(1 :n-1 )
189+ allocate (parsed_problems(n-1 ))
190+ parsed_problems = problems(1 :n-1 )
191+ call move_alloc(parsed_problems, problems)
177192 call log_info(' Parsed ' // to_str(n-1 )// ' problems from ' // trim (filename))
178193
179194 close (fin)
180195
181196 return
182197 end function
183198
184- function read_problem (fin , ierr ) result( problem)
199+ subroutine read_problem (fin , problem , ierr )
185200 ! Reads a complete ProblemDB from the input stream.
186201 ! Aborts program if malformed input is discovered.
187202 ! Returns ierr < 0 if no further problems in the stream.
188203
189204 integer , intent (in ) :: fin
205+ type (ProblemDB), intent (out ) :: problem
190206 integer , intent (out ) :: ierr
191- type (ProblemDB) :: problem
192207
193208 character (512 ) :: line
194209 character (:), allocatable :: buffer, dsname
@@ -264,7 +279,7 @@ function read_problem(fin, ierr) result(problem)
264279 end select
265280
266281 end do
267- end function
282+ end subroutine
268283
269284 function parse_prob (buffer ) result(prob)
270285 ! Parse the prob dataset for a given problem specification
@@ -423,28 +438,42 @@ function parse_reac(buffer) result(reac)
423438
424439 type (string_scanner) :: scanner
425440 character (15 ) :: token
426- integer :: ierr, n, capacity, i
427- logical :: has_na, has_fuel_oxid
441+ character (2 ) :: token2
442+ character (:), allocatable :: token_lower
443+ integer :: ierr, n, capacity, i, len_tok
444+ logical :: has_na, has_fuel_oxid, has_mole_amount, has_weight_amount
445+ type (Schedule) :: amount_sched
428446
429447 scanner = string_scanner(replace_delimiters(buffer, replace_commas= .false. ))
430448 token = scanner% read_word() ! Skip 'reac' keyword
431449
432450 n = 0
433451 capacity = 32
434452 allocate (reac(capacity))
453+ has_mole_amount = .false.
454+ has_weight_amount = .false.
435455 do
436456 token = scanner% read_word(ierr)
437457 if (ierr < 0 ) exit ! Buffer exhausted
458+ token_lower = to_lower(trim (token))
459+ len_tok = len_trim (token_lower)
460+ if (len_tok == 0 ) cycle
461+ token2 = ' '
462+ if (len_tok >= 2 ) then
463+ token2 = token_lower(:2 )
464+ else
465+ token2(1 :1 ) = token_lower(1 :1 )
466+ end if
438467
439468 ! Start new reactant definition
440- select case (token(: 2 ) )
469+ select case (token2 )
441470 case (' fu' ,' ox' ,' na' )
442471 n = n+1
443472 if (n > capacity) then
444473 reac = [reac, reac]
445474 capacity = size (reac)
446475 end if
447- reac(n)% type = token (:2 )
476+ reac(n)% type = token_lower (:2 )
448477 reac(n)% name = scanner% read_word()
449478 call log_debug(' Parsing parameters for reactant ' // reac(n)% name)
450479 cycle
@@ -454,12 +483,32 @@ function parse_reac(buffer) result(reac)
454483 if (n == 0 ) then
455484 call abort(' reac dataset missing reactant definition before token: ' // token)
456485 end if
457- select case (token(:1 ))
458- case (' m' ,' w' ); reac(n)% amount = parse_schedule(scanner, token)
486+ if (is_molecular_weight_token(token_lower)) then
487+ reac(n)% molecular_weight = parse_molecular_weight(scanner, token)
488+ cycle
489+ end if
490+ if (is_formula_element_token(token)) then
491+ reac(n)% formula = parse_formula(scanner, token)
492+ cycle
493+ end if
494+ select case (token_lower(1 :1 ))
495+ case (' m' ,' w' )
496+ amount_sched = parse_schedule(scanner, token_lower)
497+ reac(n)% amount = amount_sched
498+ if (amount_sched% name == ' mole_frac' ) has_mole_amount = .true.
499+ if (amount_sched% name == ' weight_frac' ) has_weight_amount = .true.
459500 case (' t' ); reac(n)% temperature = parse_schedule(scanner, token)
460501 case (' h' ,' u' ); reac(n)% enthalpy = parse_schedule(scanner, token)
461- case (' r' ); reac(n)% density = parse_schedule(scanner, token)
462- case (' A' :' Z' ); reac(n)% formula = parse_formula(scanner, token)
502+ case (' r' )
503+ reac(n)% density = parse_schedule(scanner, token)
504+ if (len_trim (reac(n)% density% units) == 0 ) reac(n)% density% units = ' g/cc'
505+ case (' d' )
506+ if (is_density_token(token)) then
507+ reac(n)% density = parse_schedule(scanner, token)
508+ if (len_trim (reac(n)% density% units) == 0 ) reac(n)% density% units = ' g/cc'
509+ else
510+ call abort(' reac dataset contains unrecognized token: ' // token)
511+ end if
463512 case default
464513 call abort(' reac dataset contains unrecognized token: ' // token)
465514 end select
@@ -481,10 +530,6 @@ function parse_reac(buffer) result(reac)
481530 if (reac(i)% type == ' na' ) has_na = .true.
482531 if (reac(i)% type == ' fu' .or. reac(i)% type == ' ox' ) has_fuel_oxid = .true.
483532
484- if (reac(i)% type == ' na' ) then
485- call assert(allocated (reac(i)% amount), ' reac dataset missing amount for reactant #' // to_str(i))
486- end if
487-
488533 if (allocated (reac(i)% amount)) then
489534 call assert(size (reac(i)% amount% values) > 0 , &
490535 ' reac dataset missing amount values for reactant #' // to_str(i))
@@ -507,7 +552,88 @@ function parse_reac(buffer) result(reac)
507552 call abort(" reac dataset cannot mix 'name' reactants with 'fuel'/'oxid' reactants. " // &
508553 " Use only name=... for all reactants, or only fuel=/oxid= for all reactants." )
509554 end if
555+ if (has_mole_amount .and. has_weight_amount) then
556+ call abort(" reac dataset cannot mix mole-based and weight-based reactant amounts." )
557+ end if
558+
559+ end function
560+
561+ logical function is_density_token (token ) result(tf)
562+ character (* ), intent (in ) :: token
563+ character (:), allocatable :: tok
564+ integer :: l
565+
566+ tok = to_lower(trim (token))
567+ l = len_trim (tok)
568+ if (l == 0 ) then
569+ tf = .false.
570+ else
571+ tf = (tok(:min (3 , l)) == ' den' )
572+ end if
573+ end function
574+
575+ logical function is_molecular_weight_token (token ) result(tf)
576+ character (* ), intent (in ) :: token
577+ character (:), allocatable :: tok
578+
579+ tok = to_lower(trim (token))
580+ tf = (tok == ' wt/mol' ) .or. (tok == ' wt/mole' ) .or. (tok == ' molwt' ) .or. &
581+ (tok == ' mwt' ) .or. (tok == ' mw' )
582+ end function
583+
584+ logical function is_formula_element_token (token ) result(tf)
585+ character (* ), intent (in ) :: token
586+ character (:), allocatable :: tok
587+ integer :: l
588+
589+ tok = trim (token)
590+ l = len_trim (tok)
591+ if (l < 1 .or. l > 2 ) then
592+ tf = .false.
593+ return
594+ end if
595+ if (.not. (tok(1 :1 ) >= ' A' .and. tok(1 :1 ) <= ' Z' )) then
596+ tf = .false.
597+ return
598+ end if
599+ if (l == 2 ) then
600+ tf = (tok(2 :2 ) >= ' a' .and. tok(2 :2 ) <= ' z' )
601+ else
602+ tf = .true.
603+ end if
604+ end function
605+
606+ function parse_molecular_weight (scanner , token ) result(mw)
607+ type (string_scanner), intent (inout ) :: scanner
608+ character (* ), intent (in ) :: token
609+ real (dp) :: mw
610+ character (:), allocatable :: units
611+ integer :: ierr
612+
613+ mw = scanner% peek_real(ierr)
614+ if (ierr == 0 ) then
615+ mw = scanner% read_real(ierr)
616+ return
617+ end if
618+
619+ units = to_lower(scanner% read_word(ierr))
620+ if (ierr /= 0 ) then
621+ call abort(' reac dataset missing molecular weight value after token: ' // trim (token))
622+ end if
623+
624+ mw = scanner% read_real(ierr)
625+ if (ierr /= 0 ) then
626+ call abort(' reac dataset missing molecular weight value after units token: ' // trim (units))
627+ end if
510628
629+ select case (trim (units))
630+ case (' g/mol' , ' g/mole' , ' kg/kmol' )
631+ continue
632+ case (' kg/mol' , ' kg/mole' )
633+ mw = mw* 1.0d3
634+ case default
635+ call abort(' reac dataset has unrecognized molecular weight units: ' // trim (units))
636+ end select
511637 end function
512638
513639 function parse_species (buffer ) result(species)
@@ -637,6 +763,7 @@ function parse_formula(scanner, token) result(f)
637763 type (Formula) :: f
638764 integer , parameter :: max_values = 16
639765 integer :: i, n, ierr
766+ real (dp) :: val
640767 character (:), allocatable :: word
641768
642769 allocate (f% elements(max_values))
@@ -647,7 +774,9 @@ function parse_formula(scanner, token) result(f)
647774 call abort(' parse_formula: element symbol too long: ' // trim (token))
648775 end if
649776 f% elements(n) = token
650- f% coefficients(n) = scanner% read_real()
777+ f% coefficients(n) = 1.0d0
778+ val = scanner% peek_real(ierr)
779+ if (ierr == 0 ) f% coefficients(n) = scanner% read_real(ierr)
651780
652781 do i = 2 ,size (f% elements)
653782 word = scanner% peek_word(ierr)
@@ -659,7 +788,9 @@ function parse_formula(scanner, token) result(f)
659788 call abort(' parse_formula: element symbol too long: ' // trim (word))
660789 end if
661790 f% elements(i) = word
662- f% coefficients(i) = scanner% read_real()
791+ f% coefficients(i) = 1.0d0
792+ val = scanner% peek_real(ierr)
793+ if (ierr == 0 ) f% coefficients(i) = scanner% read_real(ierr)
663794 n = i
664795 case default
665796 exit ! Start of new keyword
@@ -792,6 +923,20 @@ function is_empty(line) result(tf)
792923 startswith(line,' !' )
793924 end function
794925
926+ function to_lower (txt ) result(out )
927+ character (* ), intent (in ) :: txt
928+ character (:), allocatable :: out
929+ integer :: i, code
930+
931+ out = txt
932+ do i = 1 , len (out )
933+ code = iachar (out (i:i))
934+ if (code >= iachar (' A' ) .and. code <= iachar (' Z' )) then
935+ out (i:i) = achar (code + 32 )
936+ end if
937+ end do
938+ end function
939+
795940 function is_keyword (line ) result(tf)
796941 character (* ), intent (in ) :: line
797942 logical :: tf
0 commit comments