Skip to content

Commit 9f58e21

Browse files
authored
Fix expression evaluation problem. (bmad-sim#1729)
* Fix expression evaluation problem.
1 parent 4b547b1 commit 9f58e21

File tree

11 files changed

+109
-48
lines changed

11 files changed

+109
-48
lines changed

bmad/modules/bmad_struct.f90

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,13 +2454,13 @@ module bmad_struct
24542454
integer, parameter :: acosh$ = 54, atanh$ = 55, acoth$ = 56, min$ = 57, max$ = 58, modulo$ = 59
24552455
integer, parameter :: root$ = 60, parens$ = 61, square_brackets$ = 62, curly_brackets$ = 63, func_parens$ = 64
24562456
integer, parameter :: arrow$ = 65, equal$ = 66, colon$ = 67, double_colon$ = 68, compound$ = 69, function$ = 70
2457-
integer, parameter :: vertical_bar$ = 71, blank$ = 72
2457+
integer, parameter :: vertical_bar$ = 71, blank$ = 72, ampersand$ = 73
24582458

24592459
! Names beginning with "?!+" are place holders that will never match to anything in an expression string.
24602460
! Note: "min", "max", "rms" and "average" are not implemented in Bmad but is used by Tao.
24612461
! Note: "species" is a function and "species_const" is a specified species like "electron".
24622462

2463-
character(20), parameter :: expression_op_name(72) = [character(20) :: '+', '-', '*', '/', &
2463+
character(20), parameter :: expression_op_name(73) = [character(20) :: '+', '-', '*', '/', &
24642464
'(', ')', '^', '-', '+', '', 'sin', 'cos', 'tan', &
24652465
'asin', 'acos', 'atan', 'abs', 'sqrt', 'log', 'exp', 'ran', &
24662466
'ran_gauss', 'atan2', 'factorial', 'int', 'nint', 'floor', 'ceiling', &
@@ -2469,7 +2469,7 @@ module bmad_struct
24692469
'(', '?!+Arg Count', 'antiparticle', 'cot', 'sec', 'csc', 'sign', &
24702470
'sinh', 'cosh', 'tanh', 'coth', 'asinh', 'acosh', 'atanh', 'acoth', 'min', &
24712471
'max', 'modulo', 'root', '()', '[]', '{}', '()', '->', '=', ':', '::', 'compound', &
2472-
'?!+Function', '|', ' ']
2472+
'?!+Function', '|', ' ', '&']
24732473

24742474
integer, parameter :: expression_eval_level(69) = [1, 1, 3, 3, 0, 0, 4, 2, 2, -1, &
24752475
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, &

bmad/modules/expression_mod.f90

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ module expression_mod
3636
!
3737
! An expression string will be split on:
3838
! Two character operators: "->", "::"
39-
! operators: +-*/^=:
40-
! brackets: [](){}
39+
! operators: + - * / ^ = : &
40+
! brackets: [] () {}
4141
! comma: ,
4242
!
4343
! Root node name is "root" and is of type root$
@@ -142,7 +142,7 @@ recursive subroutine parse_pass(parse_line, tree, err_flag, err_str, tree_name)
142142
!
143143

144144
parsing_loop: do
145-
call get_next_chunk (parse_line, word, ix_word, ' []+-*/()^,{}=:|', delim, delim_found)
145+
call get_next_chunk (parse_line, word, ix_word, ' &[]+-*/()^,{}=:|', delim, delim_found)
146146
if (ix_word == 0 .and. .not. delim_found) then
147147
if (tree%type /= root$ .and. tree%type /= equal$) then
148148
call set_this_err('Mismatched brackets. Cannot find closing bracket for opening: ' // quote(tree%name(1:1)) // &
@@ -164,7 +164,7 @@ recursive subroutine parse_pass(parse_line, tree, err_flag, err_str, tree_name)
164164
if (do_combine) then
165165
cc = upcase(word(ix_word:ix_word))
166166
if (cc /= 'E' .and. cc /= 'D') do_combine = .false.
167-
if (.not. is_integer(parse_line, delims = '+-*/()^,[]{}=:| ', ix_word = ixe)) do_combine = .false.
167+
if (.not. is_integer(parse_line, delims = '&+-*/()^,[]{}=:| ', ix_word = ixe)) do_combine = .false.
168168

169169
do i = 1, ix_word-1
170170
if (index('.0123456789', word(i:i)) == 0) do_combine = .false.
@@ -250,6 +250,7 @@ recursive subroutine node_markup_pass(tree, err_flag, err_str)
250250
'ANOMALOUS_MOMENT_OF', 'COTH', 'SINH', 'COSH', 'TANH', 'ACOTH', 'ASINH', 'SUM', &
251251
'AVERAGE', 'RMS')
252252
node%type = function$
253+
case ('&'); node%type = ampersand$
253254
case ('->'); node%type = arrow$
254255
case ('*'); node%type = times$
255256
case ('/'); node%type = divide$
@@ -268,16 +269,24 @@ recursive subroutine node_markup_pass(tree, err_flag, err_str)
268269
node%type = plus$
269270
if (in == 1) then
270271
node%type = unary_plus$
271-
elseif (index('+-*/^,', trim(tree%node(in-1)%name)) > 0) then
272-
node%type = unary_plus$
272+
else
273+
select case(tree%node(in-1)%type)
274+
case(constant$, variable$, func_parens$, parens$, square_brackets$, curly_brackets$)
275+
case default
276+
node%type = unary_plus$
277+
end select
273278
endif
274279

275280
case ('-')
276281
node%type = minus$
277282
if (in == 1) then
278283
node%type = unary_minus$
279-
elseif (index('+-*/^,', trim(tree%node(in-1)%name)) > 0) then
280-
node%type = unary_minus$
284+
else
285+
select case(tree%node(in-1)%type)
286+
case(constant$, variable$, func_parens$, parens$, square_brackets$, curly_brackets$)
287+
case default
288+
node%type = unary_plus$
289+
end select
281290
endif
282291

283292
case default

regression_tests/tao_test/output.correct

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"max([-1,-2,-3]) + min(4,2,3) + sum(-3,-1.0e-1)" REL 1E-9 -2.1000000000E+00
99
"atan2(1,2)" REL 1E-9 4.6364760900E-01
1010
"data::twiss.end[1]|model-design+1e-10" REL 1E-9 1.0000000000E-10
11+
"[1,2] - [3, lat::r.11[beginning&end->-0.5*l]]" REL 1E-9 -2.0000000000E+00 1.0000000000E+00
1112
"sum(abs(ele::sbend::b*[angle]))" REL 1E-9 6.9813170080E-01
1213
"spin-tune" ABS 4E-9 -1.5719006590E+05
1314
"Polarization Limit ST" REL 4E-9 0.0000000000E+00

tao/code/tao_evaluate_expression_new.f90

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -231,23 +231,21 @@ recursive subroutine bmad_tree_to_tao_tree(tree, tao_tree, expression, err_flag)
231231

232232
! A variable or datume like "[email protected]|model" is split by the parser into an array of nodes.
233233
! Needed is to combine the nodes
234-
235-
split_variable = .false.
236-
do in = 1, n_node
237-
node => tree%node(in)
238-
select case (node%type)
239-
case (colon$, double_colon$, vertical_bar$)
240-
split_variable = .true.
241-
exit
242-
case (square_brackets$) ! "abc[...]" is a var or datum. "abc + [...]" is not.
243-
if (in == 1) cycle
244-
select case (tree%node(in-1)%name)
245-
case ('+', '-', '*', '/', '^')
246-
case default
247-
split_variable = .true.
234+
! Note: "abc[...]" is a var or datum. "abc + [...]" is not.
235+
236+
select case (tree%type)
237+
case(square_brackets$, func_parens$, parens$, curly_brackets$)
238+
split_variable = .false.
239+
case default
240+
split_variable = (n_node > 1)
241+
do in = 1, n_node
242+
node => tree%node(in)
243+
select case (node%type)
244+
case (plus$, minus$, unary_plus$, unary_minus$, times$, divide$, power$, func_parens$)
245+
split_variable = .false.
248246
end select
249-
end select
250-
enddo
247+
enddo
248+
end select
251249

252250
if (split_variable) then
253251
allocate(tao_tree%node(1))

tao/code/tao_regression_test.f90

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ subroutine tao_regression_test()
2020
logical err
2121

2222
character(200) excite_zero(3), veto
23-
character(60) :: expr(11) = [character(60):: &
23+
character(60) :: expr(12) = [character(60):: &
2424
'[anomalous_moment_of(proton), mass_of(electron)]', &
2525
'(46.5/anomalous_moment_of(proton))^2-pi', &
2626
'[3,4] * [1]@ele::q1[k1]', &
@@ -31,6 +31,7 @@ subroutine tao_regression_test()
3131
'max([-1,-2,-3]) + min(4,2,3) + sum(-3,-1.0e-1)', &
3232
'atan2(1,2)', &
3333
'data::twiss.end[1]|model-design+1e-10', &
34+
'[1,2] - [3, lat::r.11[beginning&end->-0.5*l]]', &
3435
'sum(abs(ele::sbend::b*[angle]))' ]
3536

3637
!

tao/doc/cover-page.tex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
\begin{flushright}
44
\large
5-
Revision: September 15, 2025 \\
5+
Revision: September 21, 2025 \\
66
\end{flushright}
77

88
\vfill

tao/doc/generate_tex_for_pipe_commands.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
#!/usr/bin/env python3
22
# coding: utf-8
33

4-
# # Generate documentation for Tao pipe commands.
5-
# This will auto-generate documentation for the tao pipe commands.
6-
# It extracts a formatted comment above each case statement.
7-
8-
# Note: To run tests, do the following:
9-
# 1) python generate_tex_for_pipe_commands.py # Run this file
10-
# 2) cd to pytao directory
11-
# 3) python generate_interface_commands.py
12-
# 4) python run_tests.py
4+
# This script will:
5+
# 1) Generate documentation (pipe-interface-commands.tex) for the tao pipe commands.
6+
# This is done by extracting the formatted comment above each case statement in tao_pipe_cmd.f90.
7+
# 2) Generate the pipe-interface-commands.json file for use in testing.
8+
9+
# Note: To run tests, see the documentation in the regression_tests/README.md file.
1310

1411
import json
1512
import os

tao/doc/pipe-interface-commands.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

tao/doc/pipe-interface-commands.tex

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
% WARNING: this is automatically generated. DO NOT EDIT.
1+
% WARNING: THIS FILE IS AUTOMATICALLY GENERATED! DO NOT EDIT!
22

33
%% pipe beam ------------------------------------
44
\subsection{pipe beam}
@@ -805,17 +805,15 @@ \subsection{pipe ele:lord_slave}
805805
Output the lord/slave tree of an element.
806806

807807
\begin{example}
808-
pipe ele:lord_slave \{ele_id\}|\{which\}
808+
pipe ele:lord_slave \{ele_id\}
809809
\end{example}
810810
\begin{verbatim}
811811
Where:
812812
{ele_id} is an element name or index.
813-
{which} is one of: "model", "base" or "design"
814813
815814
Example:
816-
pipe ele:lord_slave 3@1>>7|model
815+
pipe ele:lord_slave 3@1>>7
817816
This gives lord and slave info on element number 7 in branch 1 of universe 3.
818-
Note: The lord/slave info is independent of the setting of {which}.
819817
820818
The output is a number of lines.
821819
Each line gives information on an element (element index, etc.).
@@ -1379,6 +1377,36 @@ \subsection{pipe lat_param_units}
13791377
13801378
\end{verbatim}
13811379

1380+
%% pipe lord_control ------------------------------------
1381+
\subsection{pipe lord_control}
1382+
\index{pipe!lord_control}
1383+
\label{p:lord.control}
1384+
1385+
1386+
Output lord information for a given slave element.
1387+
1388+
\begin{example}
1389+
pipe lord_control \{ele_id\}
1390+
\end{example}
1391+
\begin{verbatim}
1392+
Where:
1393+
{ele_id} is the slave element.
1394+
1395+
Example:
1396+
pipe lord_control 2@1>>q01w
1397+
1398+
The output is a number of lines with each line giving the information:
1399+
Lord-index;Lord-name;Lord-type;Attribute-controlled;Control-expression;Value
1400+
1401+
Note: The last three fields will only be non-blank for ramper, overlay and group lords with
1402+
the value field being blank for rampers.
1403+
1404+
Note: For control expressed as a set of knot points (as opposed to an expression),
1405+
the control-expression will be "knots".
1406+
1407+
Note: The Value field is the contribution to the slave attribute value due to the lord.
1408+
\end{verbatim}
1409+
13821410
%% pipe matrix ------------------------------------
13831411
\subsection{pipe matrix}
13841412
\index{pipe!matrix}
@@ -1853,6 +1881,36 @@ \subsection{pipe show}
18531881
pipe show lattice -pipe
18541882
\end{verbatim}
18551883

1884+
%% pipe slave_control ------------------------------------
1885+
\subsection{pipe slave_control}
1886+
\index{pipe!slave_control}
1887+
\label{p:slave.control}
1888+
1889+
1890+
Output slave information for a given lord element.
1891+
1892+
\begin{example}
1893+
pipe slave_control \{ele_id\}
1894+
\end{example}
1895+
\begin{verbatim}
1896+
Where:
1897+
{ele_id} is the lord element.
1898+
1899+
Example:
1900+
pipe slave_control 2@1>>q01w
1901+
1902+
The output is a number of lines with each line giving the information:
1903+
Slave-branch;Slave-index;Slave-name;Slave-type;Attribute-controlled;Control-expression;Value
1904+
1905+
Note: The last three fields will only be non-blank for ramper, overlay and group lords with
1906+
the value field being blank for ramper lords.
1907+
1908+
Note: For control expressed as a set of knot points (as opposed to an expression),
1909+
the control-expression will be "knots".
1910+
1911+
Note: The Value field is the contribution to the slave attribute value due to the lord.
1912+
\end{verbatim}
1913+
18561914
%% pipe space_charge_com ------------------------------------
18571915
\subsection{pipe space_charge_com}
18581916
\index{pipe!space_charge_com}

tao/python/README

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)