Skip to content

Commit b9c9366

Browse files
authored
Merge pull request #328 from jgray-19/dkd_convergence
Convergence Test Suite
2 parents 1f843d7 + 6f76f3f commit b9c9366

File tree

6 files changed

+574
-90
lines changed

6 files changed

+574
-90
lines changed
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
-- locals ---------------------------------------------------------------------o
2+
local mtable, damap, plot, tostring in MAD
3+
local openfile, fileexists, tblcat, tblcpy in MAD.utility
4+
local max, abs in math
5+
local eps in MAD.constant
6+
7+
package.path = package.path .. ";../tools/?.mad"
8+
local get_diff, save_results, gen_cfg, in_dir, out_dir, plt_dir,
9+
plot_trk_res, get_prev_res, prnt_results, add_trk_gen_cols,
10+
show_res, get_trk_y_val, newSID, get_lower_bnds, X0s, ptc_strs,
11+
create_madx_seq, get_last_ptc_map in require "track-tool"
12+
13+
local create_dif = require "madl_dbgmap".cmpmdump
14+
local coord_str, test_ctx, madx_script in ptc_strs
15+
local method_list = {2, 4, 6, 8, "'teapot'"}
16+
17+
local ref_file = openfile(in_dir("ref.madx"), "r")
18+
local madx_ref = ref_file:read("*a")
19+
ref_file:close()
20+
local ref_file = openfile(in_dir("ref.mad"), "r")
21+
local mad_ref, mad_file = ref_file:read("*a")
22+
ref_file:close()
23+
-------------------------------------------------------------------------------o
24+
25+
-- Run track and PTC from cur_cfg and create results --------------------------o
26+
local function do_trck(cfg)
27+
-- Create results table
28+
local res = {}
29+
30+
-- Create element sequence w/ current cfg context
31+
create_madx_seq(cfg)
32+
local ptc_res
33+
if cfg.doptc and cfg.cur_cfg.method ~= "'teapot'" then
34+
-- Run MAD-X-PTC
35+
assert(os.execute(
36+
'../madx64 '.. in_dir(cfg.name .. "_ref.madx")
37+
..' >' .. out_dir(cfg.name .. "_p.txt")
38+
))
39+
-- Grab PTC last map from out file and get diff with mflw[1]
40+
ptc_res = get_last_ptc_map(out_dir(cfg.name .. "_p.txt")):fromptc()
41+
end
42+
MADX.elm = nil -- Remove element so that next test can be run with new values
43+
44+
-- Run MAD-NG track
45+
local _, mflw = mad_file()
46+
47+
return mflw[1], ptc_res
48+
end
49+
50+
local function run_cfg (cfg, results)
51+
-- Run track for a single configuration
52+
if cfg.cur_cfg.nslice == 1 and cfg.cur_cfg.method == method_list[1] then -- All of one configuration about to start
53+
local cur_cfg = cfg.cur_cfg -- Cache the current cfg
54+
55+
cfg.cur_cfg = cfg.converge_to -- Set the cfg to converge to
56+
cfg.ng_target, cfg.ptc_target = do_trck(cfg) -- Run track to get the target result
57+
cfg.cur_cfg = cur_cfg -- Restore the current cfg
58+
end
59+
local ng_target in cfg
60+
local ng_res, ptc_res = do_trck(cfg)
61+
local ng_dif = get_diff(ng_target, ng_res, cfg.order)
62+
local ptc_dif = ptc_res and get_diff(cfg.ptc_target, ptc_res, cfg.order)
63+
64+
ng_dif["converged"] = true
65+
for i = 0, cfg.order do
66+
if abs(ng_dif["order"..i.."_eps"]) > cfg.tol then
67+
ng_dif["converged"] = false
68+
break
69+
end
70+
end
71+
ng_dif.ptc_or_ng = "ng"
72+
save_results(cfg, ng_dif, results)
73+
prnt_results(cfg, ng_dif, results)
74+
if ptc_res then
75+
ptc_dif["converged"] = true
76+
for i = 0, cfg.order do
77+
if abs(ptc_dif["order"..i.."_eps"]) > cfg.tol then
78+
ptc_dif["converged"] = false
79+
break
80+
end
81+
end
82+
ptc_dif.ptc_or_ng = "ptc"
83+
save_results(cfg, ptc_dif, results)
84+
prnt_results(cfg, ptc_dif, results)
85+
end
86+
end
87+
88+
-- Plot the results -----------------------------------------------------------o
89+
local to_text = \str-> str:gsub("{", ""):gsub("}", ""):gsub("%$", "")
90+
local series = {}
91+
local legend = {}
92+
for i, method in ipairs(method_list) do
93+
series[i] = "${method} == "..method
94+
legend["y"..i] = "Method "..tostring(method):gsub("'teapot'", "t")
95+
end
96+
local plot_template = plot {
97+
prolog = [[
98+
reset
99+
f(x) = a*x**b
100+
FIT_LIMIT = 1e-20
101+
set format "%.1t{\327}10^{%T}"
102+
set key noautotitle
103+
]],
104+
ylabel = "Error",
105+
yrange = {1e-16, 1e1},
106+
wsizex = 1080,
107+
wsizey = 720,
108+
series = series,
109+
legend = legend,
110+
scrdump = "plot.gp",
111+
exec = false,
112+
}
113+
114+
local colours = {
115+
"red", "blue", "green", "orange", "purple", "brown", "pink", "grey", "black"
116+
}
117+
118+
-- Plot the results -----------------------------------------------------------o
119+
local function plot_res(res_cfg_tbl, cfg, plt_dir, dofit, cfg_tbl_)
120+
local legend, series = tblcpy(plot_template.legend), plot_template.series
121+
local cfg_tbl, dif_tbl = cfg_tbl_ or res_cfg_tbl, res_cfg_tbl
122+
for _, ptc_or_ng in ipairs {"ng", cfg.doptc and "ptc" or nil} do
123+
local is_ptc = ptc_or_ng == "ptc"
124+
local plot_info = cfg.plot_info or {}
125+
local x_axis_attr, filename, pointtype in plot_info
126+
local cfg_plot = plot_template {
127+
sid = newSID(),
128+
title = cfg.name,
129+
output = filename and plt_dir(ptc_or_ng .. "_" .. filename) or 2,
130+
exec = false,
131+
}
132+
local n_points, n_series = #dif_tbl, is_ptc and 4 or #series ! Should be 5
133+
local x_data, y_data = table.new(n_series, 0), table.new(n_series, 0)
134+
local converge_points = {} -- Set default instead?
135+
for i = 1, n_series do
136+
x_data[i], y_data[i] = {}, {}
137+
local cnt = 1
138+
for j = 1, n_points do
139+
if dif_tbl[j].ptc_or_ng == ptc_or_ng and
140+
assert(loadstring("return " .. series[i] % cfg_tbl[j]))() then
141+
x_data[i][cnt] = assert(loadstring("return " .. x_axis_attr % cfg_tbl[j]))()
142+
local y_value = get_trk_y_val(dif_tbl[j], cfg)
143+
y_data[i][cnt] = y_value > 1e-16 and y_value or 1e-16
144+
if dif_tbl[j].converged then
145+
converge_points[i] = math.max(x_data[i][cnt], converge_points[i] or 0)
146+
end
147+
cnt = cnt + 1
148+
end
149+
end
150+
end
151+
152+
cfg_plot.data, cfg_plot.datastyles, cfg_plot.x1y1 = {}, {}, {}
153+
local plotcfg, epilog = "", ""
154+
for i = 1, n_series do
155+
cfg_plot.data["x"..i] = x_data[i]
156+
cfg_plot.data["y"..i] = y_data[i]
157+
cfg_plot.x1y1["x"..i] = "y"..i
158+
cfg_plot.datastyles["y"..i] = {
159+
style = "points",
160+
pointtype = i,!assert(loadstring("return " .. (pointtype or i) % cfg_tbl[j]))(),
161+
color = colours[i],
162+
}
163+
cfg_plot.legend["y"..i] = legend["y"..i] .. (dofit and
164+
"'.sprintf('= %.2g x^{%.2g}', a"..i..", b"..i..").'" or "")
165+
if dofit then
166+
plotcfg = plotcfg .. string.format("b%d=%d\nf%d(x) = a%d*x**b%d\n", i, 2, i, i, i)
167+
plotcfg = plotcfg .. string.format("fit [%.3f:10] f%d(x) '$MAD_DATA' index %d using 1:2::($2**(0.5)) via a%d, b%d\n", converge_points[i] or 0, i, i-1, i, i)
168+
epilog = epilog .. "replot ["..(converge_points[i] or eps)..":10] f"..i.."(x) linecolor '"..colours[i].."'\n"
169+
end
170+
end
171+
plot_info.plotcfg = plotcfg ..
172+
[[
173+
set logscale y
174+
]] .. (plot_info.plotcfg or "")
175+
plot_info.prolog = cfg_plot.prolog .. (plot_info.prolog or "")
176+
plot_info.epilog = epilog .. [[
177+
set term png enhanced font ',' size 1080,720;
178+
set out "]]..cfg_plot.output..[[";
179+
replot
180+
]]
181+
plot_info.title = plot_info.title and ptc_or_ng:upper() .. " " .. plot_info.title or nil
182+
183+
cfg_plot (plot_info)
184+
end
185+
end
186+
-- Run test -------------------------------------------------------------------o
187+
local function run_test(cfg)
188+
-- If the user does not want to run the test,
189+
-- just show results from previous run
190+
if not cfg.dorun then
191+
local cfg_tbl, res_tbl = get_prev_res(cfg.name, out_dir)
192+
res_tbl.max_order = cfg.order
193+
if cfg.doprnt then show_res(res_tbl, cfg_tbl, cfg_tbl:colnames(), cfg.tol) end
194+
if cfg.doplot then plot_res(res_tbl, cfg, plt_dir, cfg.dofit, cfg_tbl) end
195+
return
196+
end
197+
198+
-- Generate the reference file
199+
cfg.seq_file = in_dir(cfg.name.."_seq.seq")
200+
openfile(in_dir(cfg.name .. "_ref.madx"), "w"):write(madx_ref % cfg):close()
201+
202+
-- Load the MAD reference file and set the input sequence name
203+
mad_file = loadstring(mad_ref % cfg)
204+
205+
-- Create new table for cur_cfg for each cfg set
206+
cfg.cur_cfg = {cfgid = 0}
207+
208+
-- Create the mtable to store the results
209+
local results = mtable(cfg.name){
210+
"__cfg", "__res",
211+
max_order = cfg.order,
212+
run_tol = cfg.tol,
213+
}
214+
215+
if cfg.doprnt then
216+
io.write("Running ", cfg.name, " (tol = ", cfg.tol, ")\n")
217+
for i = 0, cfg.order do io.write("order ", i, "\t") end
218+
io.write("\n")
219+
end
220+
221+
cfg.alist[#cfg.alist+1] = "method"
222+
cfg.alist[#cfg.alist+1] = "nslice"
223+
cfg.nslice = {
224+
1,2,4,6,8,10,
225+
20,40,60,80,100,
226+
125,150,175,200,500,1000,
227+
-- 2000,5000,10000
228+
}
229+
cfg.method = method_list
230+
231+
-- Fill the mtable with the cfg and results
232+
gen_cfg(cfg, 1, \-> run_cfg(cfg, results))
233+
234+
-- Add the generator columns to the results table
235+
add_trk_gen_cols(results, cfg)
236+
results:addcol("converged", \ri,m->m.__res[ri].converged)
237+
results:addcol("ptc_or_ng", \ri,m->m.__res[ri].ptc_or_ng)
238+
239+
-- Decide whether to save the results
240+
local dosave = cfg.dosave or not (
241+
fileexists(out_dir(results.name.."_cfg.tfs")) and
242+
fileexists(out_dir(results.name.."_res.tfs"))
243+
)
244+
245+
-- Save the results (if required)
246+
if dosave then
247+
local hdr_lst = {"name", "date", "time", "origin", "max_order", "run_tol"}
248+
results:write(
249+
out_dir(results.name.."_cfg.tfs"),
250+
tblcat({"cfgid"}, cfg.alist),
251+
hdr_lst
252+
)
253+
results:write(
254+
out_dir(results.name.."_res.tfs"),
255+
tblcat({"cfgid", "converged", "ptc_or_ng"}, results.res_cols),
256+
hdr_lst
257+
)
258+
end
259+
260+
-- Print the results
261+
if cfg.doprnt then
262+
show_res(results, results, cfg.alist, cfg.tol)
263+
end
264+
265+
-- -- Plot the results
266+
if cfg.doplot then
267+
plot_res(results, cfg, plt_dir, cfg.dofit)
268+
end
269+
270+
-- Cleanup excess files if the program is not stopped mid-test
271+
if not cfg.stop then
272+
os.remove(in_dir( cfg.name .. "_ref.madx"))
273+
os.remove(out_dir(cfg.name .. "_p.txt" ))
274+
os.remove(in_dir( cfg.name .. "_seq.seq" ))
275+
os.remove("internal_mag_pot.txt")
276+
os.remove("fort.18")
277+
end
278+
end
279+
-------------------------------------------------------------------------------o
280+
return { run_test = run_test }
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
local models = {'DKD', 'TKT'}
2+
3+
MADX:load("${seq_file}") -- Static as MAD-X is also static
4+
5+
return MAD.track { -- see ref_cfg for list of values
6+
beam = MAD.beam {energy = MADX.energy},
7+
sequence = MADX.seq,
8+
X0 = {x=MADX.x0, px=MADX.px0, y=MADX.y0, py=MADX.py0, t=MADX.t0, pt=MADX.pt0},
9+
mapdef = MADX.order,
10+
model = models[MADX.model],
11+
method = MADX.method,
12+
nslice = MADX.nslice,
13+
snm = MADX.snm,
14+
ptcmodel = MADX.ptcmodel,
15+
debug = MADX.debug,
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
option, rbarc=false, echo=false;
2+
3+
call file="${seq_file}";
4+
5+
beam, energy=energy;
6+
use, sequence=seq ;
7+
if (snm > 0) {
8+
ptc_create_universe, sector_nmul = snm, sector_nmul_max = snm;
9+
} else {
10+
ptc_create_universe;
11+
}
12+
ptc_setswitch, debuglevel=2, mapdump=3, madprint=true, exact_mis=true, time=true, totalpath=false;
13+
if (FRINGE > 0) { ptc_setswitch, fringe=true; }
14+
ptc_create_layout, model=MODEL, method=METHOD, nst=NSLICE, time=true, exact=true, closed_layout=false;
15+
ptc_normal, closed_orbit=false, time=true, no=ORDER, icase=ICASE, x=x0, px=px0, y=y0, py=py0, t=t0, pt=pt0;
16+
ptc_end;

0 commit comments

Comments
 (0)