Neko 1.99.7
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
expression.f90
Go to the documentation of this file.
1! Copyright (c) 2026, The Neko Authors
2! All rights reserved.
3!
4! Redistribution and use in source and binary forms, with or without
5! modification, are permitted provided that the following conditions
6! are met:
7!
8! * Redistributions of source code must retain the above copyright
9! notice, this list of conditions and the following disclaimer.
10!
11! * Redistributions in binary form must reproduce the above
12! copyright notice, this list of conditions and the following
13! disclaimer in the documentation and/or other materials provided
14! with the distribution.
15!
16! * Neither the name of the authors nor the names of its
17! contributors may be used to endorse or promote products derived
18! from this software without specific prior written permission.
19!
20! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24! COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31! POSSIBILITY OF SUCH DAMAGE.
32!
45 use num_types, only : rp
46 use utils, only : neko_error
48 use, intrinsic :: ieee_arithmetic, only : ieee_is_finite
49 implicit none
50 private
51
53
55 integer, public, parameter :: neko_expr_len = 256
56
58 integer, parameter :: expr_max_ident = 64
59
63 integer, parameter :: expr_chunk = 1024
64
65 !
66 ! Opcodes. The evaluator itself does not care about the numbering, but the
67 ! parser derives the arity of an opcode from the ranges below, so nullary
68 ! opcodes must stay below OP_UNARY and binary ones at or above OP_BINARY.
69 !
70 integer, parameter :: op_lit = 1, op_x = 2, op_y = 3, op_z = 4, &
71 op_t = 5, op_dt = 6
72
73 integer, parameter :: op_unary = 20
74 integer, parameter :: op_neg = 20, op_sin = 21, op_cos = 22, op_tan = 23, &
75 op_asin = 24, op_acos = 25, op_atan = 26, op_sinh = 27, &
76 op_cosh = 28, op_tanh = 29, op_exp = 30, op_log = 31, &
77 op_log10 = 32, op_sqrt = 33, op_abs = 34, op_erf = 35, &
78 op_erfc = 36, op_step = 37, op_ipow = 38
79
80 integer, parameter :: op_binary = 60
81 integer, parameter :: op_add = 60, op_sub = 61, op_mul = 62, op_div = 63, &
82 op_pow = 64, op_atan2 = 65, op_min = 66, op_max = 67, op_mod = 68
83
84 !
85 ! Token kinds produced by the tokenizer.
86 !
87 integer, parameter :: tk_end = 0, tk_num = 1, tk_ident = 2, tk_plus = 3, &
88 tk_minus = 4, tk_star = 5, tk_slash = 6, tk_pow = 7, tk_lpar = 8, &
89 tk_rpar = 9, tk_comma = 10
90
92 type :: token_t
93 integer :: kind = tk_end
94 real(kind=rp) :: num = 0.0_rp
95 character(len=EXPR_MAX_IDENT) :: name = ''
98 integer :: pos = 0
99 end type token_t
100
102 type :: parser_t
103 type(token_t), allocatable :: tok(:)
104 integer :: n_tok = 0
105 integer :: cur = 1
106 character(len=:), allocatable :: src
107 integer, allocatable :: op(:)
108 real(kind=rp), allocatable :: lit(:)
109 integer :: n_op = 0
111 integer :: depth = 0
112 integer :: max_depth = 0
113 logical :: time_dependent = .false.
114 end type parser_t
115
117 type, public :: expression_t
119 integer, allocatable :: op(:)
122 real(kind=rp), allocatable :: lit(:)
124 integer :: n_op = 0
126 integer :: depth = 0
128 logical :: time_dependent = .false.
130 character(len=:), allocatable :: src
132 real(kind=rp), allocatable, private :: stk(:,:)
133 contains
135 procedure, pass(this) :: init => expression_init
137 procedure, pass(this) :: eval => expression_eval
139 procedure, pass(this) :: free => expression_free
140 end type expression_t
141
142contains
143
146 subroutine expression_init(this, str)
147 class(expression_t), intent(inout) :: this
148 character(len=*), intent(in) :: str
149 type(parser_t) :: p
150
151 call this%free()
152
153 p%src = trim(str)
154 if (len(p%src) .eq. 0) call neko_error("Empty expression")
155
156 call expr_tokenize(p)
157
158 allocate(p%op(p%n_tok + 1))
159 allocate(p%lit(p%n_tok + 1))
160
161 call parse_expr(p)
162 if (p%tok(p%cur)%kind .ne. tk_end) then
163 call expr_error(p, "unexpected trailing input", p%tok(p%cur)%pos)
164 end if
165
166 this%n_op = p%n_op
167 this%depth = p%max_depth
168 this%time_dependent = p%time_dependent
169 this%src = p%src
170
171 allocate(this%op(this%n_op), this%lit(this%n_op))
172 this%op = p%op(1:this%n_op)
173 this%lit = p%lit(1:this%n_op)
174
175 allocate(this%stk(expr_chunk, max(this%depth, 1)))
176
177 call parser_free(p)
178
179 end subroutine expression_init
180
183 subroutine parser_free(p)
184 type(parser_t), intent(inout) :: p
185
186 if (allocated(p%tok)) deallocate(p%tok)
187 if (allocated(p%src)) deallocate(p%src)
188 if (allocated(p%op)) deallocate(p%op)
189 if (allocated(p%lit)) deallocate(p%lit)
190
191 p%n_tok = 0
192 p%cur = 1
193 p%n_op = 0
194 p%depth = 0
195 p%max_depth = 0
196 p%time_dependent = .false.
197
198 end subroutine parser_free
199
201 subroutine expression_free(this)
202 class(expression_t), intent(inout) :: this
203
204 if (allocated(this%op)) deallocate(this%op)
205 if (allocated(this%lit)) deallocate(this%lit)
206 if (allocated(this%src)) deallocate(this%src)
207 if (allocated(this%stk)) deallocate(this%stk)
208 this%n_op = 0
209 this%depth = 0
210 this%time_dependent = .false.
211
212 end subroutine expression_free
213
226 subroutine expression_eval(this, res, n, x, y, z, t, dt)
227 class(expression_t), intent(inout) :: this
228 integer, intent(in) :: n
229 real(kind=rp), intent(inout) :: res(n)
230 real(kind=rp), intent(in) :: x(n)
231 real(kind=rp), intent(in) :: y(n)
232 real(kind=rp), intent(in) :: z(n)
233 real(kind=rp), intent(in), optional :: t
234 real(kind=rp), intent(in), optional :: dt
235 real(kind=rp) :: t_, dt_
236 integer :: i0, nb, ip, sp, i, k
237
238 if (this%n_op .eq. 0) call neko_error("Evaluating an uncompiled expression")
239
240 if (this%time_dependent .and. .not. present(t)) then
241 call neko_error("The expression '" // this%src // "' depends on " // &
242 "time, but no time is available where it is used")
243 end if
244
245 t_ = 0.0_rp
246 dt_ = 0.0_rp
247 if (present(t)) t_ = t
248 if (present(dt)) dt_ = dt
249
250 do i0 = 1, n, expr_chunk
251 nb = min(expr_chunk, n - i0 + 1)
252 sp = 0
253
254 do ip = 1, this%n_op
255 select case (this%op(ip))
256
257 !
258 ! Operand pushes
259 !
260 case (op_lit)
261 sp = sp + 1
262 do i = 1, nb
263 this%stk(i, sp) = this%lit(ip)
264 end do
265 case (op_x)
266 sp = sp + 1
267 do i = 1, nb
268 this%stk(i, sp) = x(i0 + i - 1)
269 end do
270 case (op_y)
271 sp = sp + 1
272 do i = 1, nb
273 this%stk(i, sp) = y(i0 + i - 1)
274 end do
275 case (op_z)
276 sp = sp + 1
277 do i = 1, nb
278 this%stk(i, sp) = z(i0 + i - 1)
279 end do
280 case (op_t)
281 sp = sp + 1
282 do i = 1, nb
283 this%stk(i, sp) = t_
284 end do
285 case (op_dt)
286 sp = sp + 1
287 do i = 1, nb
288 this%stk(i, sp) = dt_
289 end do
290
291 !
292 ! Unary operators
293 !
294 case (op_neg)
295 do i = 1, nb
296 this%stk(i, sp) = -this%stk(i, sp)
297 end do
298 case (op_sin)
299 do i = 1, nb
300 this%stk(i, sp) = sin(this%stk(i, sp))
301 end do
302 case (op_cos)
303 do i = 1, nb
304 this%stk(i, sp) = cos(this%stk(i, sp))
305 end do
306 case (op_tan)
307 do i = 1, nb
308 this%stk(i, sp) = tan(this%stk(i, sp))
309 end do
310 case (op_asin)
311 do i = 1, nb
312 this%stk(i, sp) = asin(this%stk(i, sp))
313 end do
314 case (op_acos)
315 do i = 1, nb
316 this%stk(i, sp) = acos(this%stk(i, sp))
317 end do
318 case (op_atan)
319 do i = 1, nb
320 this%stk(i, sp) = atan(this%stk(i, sp))
321 end do
322 case (op_sinh)
323 do i = 1, nb
324 this%stk(i, sp) = sinh(this%stk(i, sp))
325 end do
326 case (op_cosh)
327 do i = 1, nb
328 this%stk(i, sp) = cosh(this%stk(i, sp))
329 end do
330 case (op_tanh)
331 do i = 1, nb
332 this%stk(i, sp) = tanh(this%stk(i, sp))
333 end do
334 case (op_exp)
335 do i = 1, nb
336 this%stk(i, sp) = exp(this%stk(i, sp))
337 end do
338 case (op_log)
339 do i = 1, nb
340 this%stk(i, sp) = log(this%stk(i, sp))
341 end do
342 case (op_log10)
343 do i = 1, nb
344 this%stk(i, sp) = log10(this%stk(i, sp))
345 end do
346 case (op_sqrt)
347 do i = 1, nb
348 this%stk(i, sp) = sqrt(this%stk(i, sp))
349 end do
350 case (op_abs)
351 do i = 1, nb
352 this%stk(i, sp) = abs(this%stk(i, sp))
353 end do
354 case (op_erf)
355 do i = 1, nb
356 this%stk(i, sp) = erf(this%stk(i, sp))
357 end do
358 case (op_erfc)
359 do i = 1, nb
360 this%stk(i, sp) = erfc(this%stk(i, sp))
361 end do
362 case (op_step)
363 do i = 1, nb
364 if (this%stk(i, sp) .lt. 0.0_rp) then
365 this%stk(i, sp) = 0.0_rp
366 else
367 this%stk(i, sp) = 1.0_rp
368 end if
369 end do
370 case (op_ipow)
371 ! Integer exponent, folded by the parser so that a negative base
372 ! raised to a whole power stays well defined.
373 k = nint(this%lit(ip))
374 do i = 1, nb
375 this%stk(i, sp) = this%stk(i, sp) ** k
376 end do
377
378 !
379 ! Binary operators
380 !
381 case (op_add)
382 do i = 1, nb
383 this%stk(i, sp-1) = this%stk(i, sp-1) + this%stk(i, sp)
384 end do
385 sp = sp - 1
386 case (op_sub)
387 do i = 1, nb
388 this%stk(i, sp-1) = this%stk(i, sp-1) - this%stk(i, sp)
389 end do
390 sp = sp - 1
391 case (op_mul)
392 do i = 1, nb
393 this%stk(i, sp-1) = this%stk(i, sp-1) * this%stk(i, sp)
394 end do
395 sp = sp - 1
396 case (op_div)
397 do i = 1, nb
398 this%stk(i, sp-1) = this%stk(i, sp-1) / this%stk(i, sp)
399 end do
400 sp = sp - 1
401 case (op_pow)
402 do i = 1, nb
403 this%stk(i, sp-1) = this%stk(i, sp-1) ** this%stk(i, sp)
404 end do
405 sp = sp - 1
406 case (op_atan2)
407 do i = 1, nb
408 this%stk(i, sp-1) = atan2(this%stk(i, sp-1), this%stk(i, sp))
409 end do
410 sp = sp - 1
411 case (op_min)
412 do i = 1, nb
413 this%stk(i, sp-1) = min(this%stk(i, sp-1), this%stk(i, sp))
414 end do
415 sp = sp - 1
416 case (op_max)
417 do i = 1, nb
418 this%stk(i, sp-1) = max(this%stk(i, sp-1), this%stk(i, sp))
419 end do
420 sp = sp - 1
421 case (op_mod)
422 do i = 1, nb
423 this%stk(i, sp-1) = mod(this%stk(i, sp-1), this%stk(i, sp))
424 end do
425 sp = sp - 1
426
427 case default
428 call neko_error("Corrupt expression opcode stream")
429 end select
430 end do
431
432 if (sp .ne. 1) call neko_error("Corrupt expression opcode stream")
433
434 do i = 1, nb
435 res(i0 + i - 1) = this%stk(i, 1)
436 end do
437 end do
438
439 end subroutine expression_eval
440
455 subroutine expression_eval_static(str, res, n, x, y, z, usage)
456 character(len=*), intent(in) :: str
457 integer, intent(in) :: n
458 real(kind=rp), intent(inout) :: res(n)
459 real(kind=rp), intent(in) :: x(n)
460 real(kind=rp), intent(in) :: y(n)
461 real(kind=rp), intent(in) :: z(n)
462 character(len=*), intent(in) :: usage
463 type(expression_t) :: e
464
465 if (len_trim(str) .eq. 0) then
466 call neko_error("The " // usage // " needs a non-empty expression")
467 end if
468
469 call e%init(str)
470
471 if (e%time_dependent) then
472 call neko_error("The " // usage // " expression '" // trim(str) // &
473 "' depends on time, which is not available where it is used")
474 end if
475
476 call e%eval(res, n, x, y, z)
477 call e%free()
478
479 call expression_check_finite(str, res, n, usage)
480
481 end subroutine expression_eval_static
482
492 subroutine expression_check_finite(str, res, n, usage)
493 character(len=*), intent(in) :: str
494 integer, intent(in) :: n
495 real(kind=rp), intent(in) :: res(n)
496 character(len=*), intent(in) :: usage
497 integer :: i
498
499 do i = 1, n
500 if (.not. ieee_is_finite(res(i))) then
501 call neko_error("The " // usage // " expression '" // trim(str) // &
502 "' does not evaluate to a finite value everywhere")
503 end if
504 end do
505
506 end subroutine expression_check_finite
507
508 !
509 ! Tokenizer
510 !
511
514 subroutine expr_tokenize(p)
515 type(parser_t), intent(inout) :: p
516 integer :: i, j, k, n, ios
517 logical :: seen_dot
518 character(len=1) :: c
519 character(len=EXPR_MAX_IDENT) :: buf
520
521 n = len(p%src)
522 allocate(p%tok(n + 1))
523 p%n_tok = 0
524
525 i = 1
526 do while (i .le. n)
527 c = p%src(i:i)
528
529 if (c .eq. ' ' .or. c .eq. achar(9)) then
530 i = i + 1
531 cycle
532 end if
533
534 if (is_digit(c) .or. (c .eq. '.' .and. i .lt. n .and. &
535 is_digit(p%src(min(i+1, n):min(i+1, n))))) then
536 ! Mantissa
537 j = i
538 seen_dot = .false.
539 do while (j .le. n)
540 if (is_digit(p%src(j:j))) then
541 j = j + 1
542 else if (p%src(j:j) .eq. '.' .and. .not. seen_dot) then
543 seen_dot = .true.
544 j = j + 1
545 else
546 exit
547 end if
548 end do
549 ! Exponent, but only if it is actually followed by digits, so that
550 ! e.g. `2*d` is not mistaken for a malformed number.
551 if (j .le. n) then
552 if (scan(p%src(j:j), 'eEdD') .gt. 0) then
553 k = j + 1
554 if (k .le. n) then
555 if (scan(p%src(k:k), '+-') .gt. 0) k = k + 1
556 end if
557 if (k .le. n) then
558 if (is_digit(p%src(k:k))) then
559 do while (k .le. n)
560 if (.not. is_digit(p%src(k:k))) exit
561 k = k + 1
562 end do
563 j = k
564 end if
565 end if
566 end if
567 end if
568
569 if (j - i .gt. expr_max_ident) then
570 call expr_error(p, "numeric literal is too long", i)
571 end if
572 buf = p%src(i:j-1)
573 ! Fortran list-directed input does not accept a `d` exponent in
574 ! every implementation, so normalise it.
575 do k = 1, len_trim(buf)
576 if (buf(k:k) .eq. 'd' .or. buf(k:k) .eq. 'D') buf(k:k) = 'e'
577 end do
578 call push_token(p, tk_num, i)
579 read (buf, *, iostat = ios) p%tok(p%n_tok)%num
580 if (ios .ne. 0) call expr_error(p, "malformed number", i)
581 i = j
582 cycle
583 end if
584
585 if (is_alpha(c)) then
586 j = i
587 do while (j .le. n)
588 if (is_alpha(p%src(j:j)) .or. is_digit(p%src(j:j)) .or. &
589 p%src(j:j) .eq. '_') then
590 j = j + 1
591 else
592 exit
593 end if
594 end do
595 if (j - i .gt. expr_max_ident) then
596 call expr_error(p, "identifier is too long", i)
597 end if
598 call push_token(p, tk_ident, i)
599 p%tok(p%n_tok)%name = p%src(i:j-1)
600 i = j
601 cycle
602 end if
603
604 select case (c)
605 case ('+')
606 call push_token(p, tk_plus, i)
607 case ('-')
608 call push_token(p, tk_minus, i)
609 case ('*')
610 if (i .lt. n) then
611 if (p%src(i+1:i+1) .eq. '*') then
612 call push_token(p, tk_pow, i)
613 i = i + 2
614 cycle
615 end if
616 end if
617 call push_token(p, tk_star, i)
618 case ('/')
619 call push_token(p, tk_slash, i)
620 case ('^')
621 call push_token(p, tk_pow, i)
622 case ('(')
623 call push_token(p, tk_lpar, i)
624 case (')')
625 call push_token(p, tk_rpar, i)
626 case (',')
627 call push_token(p, tk_comma, i)
628 case default
629 call expr_error(p, "unexpected character '" // c // "'", i)
630 end select
631 i = i + 1
632 end do
633
634 call push_token(p, tk_end, n + 1)
635
636 end subroutine expr_tokenize
637
642 subroutine push_token(p, kind, pos)
643 type(parser_t), intent(inout) :: p
644 integer, intent(in) :: kind
645 integer, intent(in) :: pos
646
647 p%n_tok = p%n_tok + 1
648 p%tok(p%n_tok)%kind = kind
649 p%tok(p%n_tok)%pos = pos
650
651 end subroutine push_token
652
653 !
654 ! Recursive descent parser, emitting postfix opcodes as it goes.
655 !
656 ! expr := term (('+' | '-') term)*
657 ! term := factor (('*' | '/') factor)*
658 ! factor := ('+' | '-') factor | power
659 ! power := primary ['^' factor]
660 ! primary := NUM | IDENT | IDENT '(' expr (',' expr)* ')' | '(' expr ')'
661 !
662
665 recursive subroutine parse_expr(p)
666 type(parser_t), intent(inout) :: p
667
668 call parse_term(p)
669 do
670 if (p%tok(p%cur)%kind .eq. tk_plus) then
671 p%cur = p%cur + 1
672 call parse_term(p)
673 call emit(p, op_add)
674 else if (p%tok(p%cur)%kind .eq. tk_minus) then
675 p%cur = p%cur + 1
676 call parse_term(p)
677 call emit(p, op_sub)
678 else
679 exit
680 end if
681 end do
682
683 end subroutine parse_expr
684
687 recursive subroutine parse_term(p)
688 type(parser_t), intent(inout) :: p
689
690 call parse_factor(p)
691 do
692 if (p%tok(p%cur)%kind .eq. tk_star) then
693 p%cur = p%cur + 1
694 call parse_factor(p)
695 call emit(p, op_mul)
696 else if (p%tok(p%cur)%kind .eq. tk_slash) then
697 p%cur = p%cur + 1
698 call parse_factor(p)
699 call emit(p, op_div)
700 else
701 exit
702 end if
703 end do
704
705 end subroutine parse_term
706
710 recursive subroutine parse_factor(p)
711 type(parser_t), intent(inout) :: p
712 integer :: before
713
714 if (p%tok(p%cur)%kind .eq. tk_minus) then
715 p%cur = p%cur + 1
716 before = p%n_op
717 call parse_factor(p)
718 if (p%n_op .eq. before + 1 .and. p%op(p%n_op) .eq. op_lit) then
719 ! Fold the sign into the literal, so that e.g. `y^-2` can use an
720 ! integer power rather than the real valued one.
721 p%lit(p%n_op) = -p%lit(p%n_op)
722 else
723 call emit(p, op_neg)
724 end if
725 else if (p%tok(p%cur)%kind .eq. tk_plus) then
726 p%cur = p%cur + 1
727 call parse_factor(p)
728 else
729 call parse_power(p)
730 end if
731
732 end subroutine parse_factor
733
736 recursive subroutine parse_power(p)
737 type(parser_t), intent(inout) :: p
738 integer :: before
739
740 call parse_primary(p)
741
742 if (p%tok(p%cur)%kind .eq. tk_pow) then
743 p%cur = p%cur + 1
744 before = p%n_op
745 call parse_factor(p)
746
747 if (p%n_op .eq. before + 1 .and. p%op(p%n_op) .eq. op_lit) then
748 ! Tested exactly, and not within a tolerance: an exponent such as
749 ! `2.0000005` is not a whole number, and folding it would silently
750 ! turn an undefined negative base power into a finite value.
751 if (p%lit(p%n_op) .eq. anint(p%lit(p%n_op)) .and. &
752 abs(p%lit(p%n_op)) .lt. 1.0e4_rp) then
753 ! Whole exponent. Rewrite the literal push into a unary integer
754 ! power, which unlike `**` with a real exponent is also defined
755 ! for a negative base.
756 p%op(p%n_op) = op_ipow
757 p%depth = p%depth - 1
758 return
759 end if
760 end if
761
762 call emit(p, op_pow)
763 end if
764
765 end subroutine parse_power
766
769 recursive subroutine parse_primary(p)
770 type(parser_t), intent(inout) :: p
771 integer :: k, op, nargs
772
773 select case (p%tok(p%cur)%kind)
774 case (tk_num)
775 call emit(p, op_lit, p%tok(p%cur)%num)
776 p%cur = p%cur + 1
777
778 case (tk_lpar)
779 p%cur = p%cur + 1
780 call parse_expr(p)
781 if (p%tok(p%cur)%kind .ne. tk_rpar) then
782 call expr_error(p, "expected ')'", p%tok(p%cur)%pos)
783 end if
784 p%cur = p%cur + 1
785
786 case (tk_ident)
787 k = p%cur
788 p%cur = p%cur + 1
789 if (p%tok(p%cur)%kind .eq. tk_lpar) then
790 p%cur = p%cur + 1
791 call parse_expr(p)
792 nargs = 1
793 do while (p%tok(p%cur)%kind .eq. tk_comma)
794 p%cur = p%cur + 1
795 call parse_expr(p)
796 nargs = nargs + 1
797 end do
798 if (p%tok(p%cur)%kind .ne. tk_rpar) then
799 call expr_error(p, "expected ')' closing the argument list of '" &
800 // trim(p%tok(k)%name) // "'", p%tok(p%cur)%pos)
801 end if
802 p%cur = p%cur + 1
803
804 op = func_op(trim(p%tok(k)%name), nargs)
805 if (op .eq. -1) then
806 call expr_error(p, "unknown function '" // &
807 trim(p%tok(k)%name) // "'", p%tok(k)%pos)
808 else if (op .eq. -2) then
809 call expr_error(p, "wrong number of arguments to '" // &
810 trim(p%tok(k)%name) // "'", p%tok(k)%pos)
811 end if
812 call emit(p, op)
813 else
814 call parse_symbol(p, k)
815 end if
816
817 case default
818 call expr_error(p, "expected a value", p%tok(p%cur)%pos)
819 end select
820
821 end subroutine parse_primary
822
830 subroutine parse_symbol(p, k)
831 type(parser_t), intent(inout) :: p
832 integer, intent(in) :: k
833 character(len=:), allocatable :: name
834 real(kind=rp), pointer :: rval
835 integer, pointer :: ival
836
837 name = trim(p%tok(k)%name)
838
839 select case (name)
840 case ('x')
841 call emit(p, op_x)
842 case ('y')
843 call emit(p, op_y)
844 case ('z')
845 call emit(p, op_z)
846 case ('t')
847 call emit(p, op_t)
848 p%time_dependent = .true.
849 case ('dt')
850 call emit(p, op_dt)
851 p%time_dependent = .true.
852 case ('pi')
853 call emit(p, op_lit, 4.0_rp * atan(1.0_rp))
854 case default
855 if (neko_const_registry%real_scalar_exists(name)) then
856 rval => neko_const_registry%get_real_scalar(name)
857 call emit(p, op_lit, rval)
858 else if (neko_const_registry%integer_scalar_exists(name)) then
859 ival => neko_const_registry%get_integer_scalar(name)
860 call emit(p, op_lit, real(ival, kind=rp))
861 else
862 call expr_error(p, "unknown symbol '" // name // "', it is " // &
863 "neither a coordinate nor a constant declared under " // &
864 "case.constants", p%tok(k)%pos)
865 end if
866 end select
867
868 if (allocated(name)) deallocate(name)
869
870 end subroutine parse_symbol
871
877 function func_op(name, nargs) result(op)
878 character(len=*), intent(in) :: name
879 integer, intent(in) :: nargs
880 integer :: op, arity
881
882 arity = 1
883 select case (name)
884 case ('sin')
885 op = op_sin
886 case ('cos')
887 op = op_cos
888 case ('tan')
889 op = op_tan
890 case ('asin')
891 op = op_asin
892 case ('acos')
893 op = op_acos
894 case ('atan')
895 op = op_atan
896 case ('sinh')
897 op = op_sinh
898 case ('cosh')
899 op = op_cosh
900 case ('tanh')
901 op = op_tanh
902 case ('exp')
903 op = op_exp
904 case ('log')
905 op = op_log
906 case ('log10')
907 op = op_log10
908 case ('sqrt')
909 op = op_sqrt
910 case ('abs')
911 op = op_abs
912 case ('erf')
913 op = op_erf
914 case ('erfc')
915 op = op_erfc
916 case ('step')
917 op = op_step
918 case ('atan2')
919 op = op_atan2
920 arity = 2
921 case ('min')
922 op = op_min
923 arity = 2
924 case ('max')
925 op = op_max
926 arity = 2
927 case ('mod')
928 op = op_mod
929 arity = 2
930 case default
931 op = -1
932 return
933 end select
934
935 if (nargs .ne. arity) op = -2
936
937 end function func_op
938
943 subroutine emit(p, op, val)
944 type(parser_t), intent(inout) :: p
945 integer, intent(in) :: op
946 real(kind=rp), intent(in), optional :: val
947
948 if (p%n_op .ge. size(p%op)) then
949 call neko_error("Expression '" // p%src // "' is too complex")
950 end if
951
952 p%n_op = p%n_op + 1
953 p%op(p%n_op) = op
954 if (present(val)) then
955 p%lit(p%n_op) = val
956 else
957 p%lit(p%n_op) = 0.0_rp
958 end if
959
960 if (op .lt. op_unary) then
961 p%depth = p%depth + 1
962 else if (op .ge. op_binary) then
963 p%depth = p%depth - 1
964 end if
965 p%max_depth = max(p%max_depth, p%depth)
966
967 end subroutine emit
968
973 subroutine expr_error(p, msg, pos)
974 type(parser_t), intent(in) :: p
975 character(len=*), intent(in) :: msg
976 integer, intent(in) :: pos
977 character(len=16) :: buf
978
979 write (buf, '(I0)') pos
980 call neko_error("Invalid expression '" // p%src // "', at character " // &
981 trim(buf) // ": " // msg)
982
983 end subroutine expr_error
984
987 pure function is_digit(c) result(res)
988 character(len=1), intent(in) :: c
989 logical :: res
990
991 res = (c .ge. '0' .and. c .le. '9')
992
993 end function is_digit
994
997 pure function is_alpha(c) result(res)
998 character(len=1), intent(in) :: c
999 logical :: res
1000
1001 res = (c .ge. 'a' .and. c .le. 'z') .or. (c .ge. 'A' .and. c .le. 'Z')
1002
1003 end function is_alpha
1004
1005end module expression
subroutine usage()
double real
Evaluation of mathematical expressions given as strings in the case file.
integer, parameter op_tan
subroutine push_token(p, kind, pos)
Append a token to the token stream.
integer, parameter tk_rpar
integer, parameter op_sqrt
integer, parameter tk_pow
integer, parameter op_asin
recursive subroutine parse_term(p)
Parse a multiplicative expression.
integer, parameter op_erf
integer, parameter op_t
subroutine expression_eval(this, res, n, x, y, z, t, dt)
Evaluate the expression in n points.
integer, parameter op_erfc
integer, parameter tk_end
pure logical function is_alpha(c)
True if c is a letter.
recursive subroutine parse_factor(p)
Parse a possibly signed factor. Unary minus binds looser than ^, so -x^2 is -(x^2),...
integer, parameter op_div
integer, parameter tk_lpar
integer, parameter op_abs
subroutine, public expression_check_finite(str, res, n, usage)
Abort if an expression did not evaluate to a finite value everywhere.
integer, parameter op_add
integer, parameter op_neg
subroutine expr_error(p, msg, pos)
Abort with a message pointing at the offending part of the expression.
integer, parameter op_log
integer, parameter op_cos
integer, parameter tk_ident
subroutine parser_free(p)
Release the memory held by the parser state.
integer, parameter op_binary
integer, parameter, public neko_expr_len
Maximum length of an expression string read from the case file.
integer, parameter op_log10
integer, parameter op_sub
integer, parameter op_mod
integer, parameter op_lit
subroutine expression_init(this, str)
Compile an expression.
pure logical function is_digit(c)
True if c is a decimal digit.
integer, parameter op_dt
subroutine expression_free(this)
Destructor.
integer, parameter tk_plus
integer, parameter expr_chunk
Number of points evaluated per pass over the opcode stream. Chosen so that the evaluation stack stays...
integer, parameter expr_max_ident
Maximum length of an identifier appearing in an expression.
integer, parameter tk_slash
integer, parameter op_atan2
integer function func_op(name, nargs)
Look up the opcode of a function.
integer, parameter op_min
integer, parameter op_cosh
integer, parameter op_step
integer, parameter op_y
integer, parameter op_z
integer, parameter op_atan
integer, parameter op_acos
subroutine emit(p, op, val)
Append an opcode, keeping track of how deep the evaluation stack gets.
recursive subroutine parse_primary(p)
Parse a literal, a symbol, a function call or a parenthesised expression.
integer, parameter op_unary
integer, parameter tk_num
integer, parameter tk_star
integer, parameter tk_minus
integer, parameter op_x
integer, parameter op_max
integer, parameter op_tanh
subroutine expr_tokenize(p)
Split the source string into a stream of tokens.
integer, parameter op_pow
subroutine, public expression_eval_static(str, res, n, x, y, z, usage)
Compile an expression and evaluate it in a set of points, in a context where there is no time state.
integer, parameter op_exp
integer, parameter op_ipow
recursive subroutine parse_expr(p)
Parse an additive expression.
integer, parameter op_mul
integer, parameter op_sin
recursive subroutine parse_power(p)
Parse an exponentiation. Right associative, so 2^3^2 is 2^(3^2).
subroutine parse_symbol(p, k)
Resolve a bare identifier into either a variable opcode or a literal.
integer, parameter tk_comma
integer, parameter op_sinh
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Defines a registry for storing solution fields.
Definition registry.f90:34
type(registry_t), target, public neko_const_registry
This registry is used to store user-defined scalars and vectors, provided under the constants section...
Definition registry.f90:150
Utilities.
Definition utils.f90:35
A compiled mathematical expression.
State of the recursive descent parser.
A single token of an expression.
#define max(a, b)
Definition tensor.cu:40