Skip to content

Commit cc7d883

Browse files
authored
convergence utility tools (#165)
This pr makes convergence.py more general so that it takes any variable we want with the arbitrary multiplicative factor of resolutions apart between plot files. The default is still density and twice the resolution between the fine plot files and the coarse plot files. convergence_plot.py allows users to give 3 or more output files which the corresponding resolution differs by a constant multiplicative constant. The goal here is to duplicate the Richardson utility tool that we had in castro/amrex. It outputs the table that tells us the convergence rate as well as a plot.
1 parent 8cf6576 commit cc7d883

4 files changed

Lines changed: 172 additions & 13 deletions

File tree

docs/source/analysis.rst

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,31 @@ with their data.
2323
* ``analysis/``
2424

2525
* ``convergence.py``: this compares two files with different
26-
resolutions (one a factor of 2 finer than the other). It coarsens
27-
the finer data and then computes the norm of the difference. This
28-
is used to test the convergence of solvers.
29-
26+
resolutions (one a factor of N-times, (default is 2 times or ``N=2``) finer
27+
than the other). It coarsens the finer data and then computes the
28+
norm of the difference. The default variable being compared is
29+
density, ``var_name=density`` by default. but it takes arbitary variable name as input.
30+
This is used to test the convergence of solvers.
31+
32+
usage: ``./convergence.py fine_file coarse_file var_name N``
33+
34+
* ``convergence_plot.py``: this compares 3 or more files with different
35+
resolutions (factor of N-times (default is 2 times) finer than the
36+
others) by using ``convergence.py``. It prints out a table that summarizes
37+
the L-2 Norm of all the variables by coarsening the finer data and comparing
38+
it to the coarse data, as well as the order of convergence between the
39+
L-2 Norms. It also produces a plot in the end to compare the theoretical
40+
order of convergence to the actual order of convergence from the datasets.
41+
The data files should enter from the finest file to the coarsest file. There
42+
should be at least 3 files, ``fine_file med_file coarse_file``.
43+
The output file name for the convergence plot, ``out = convergence_plot.pdf``,
44+
by default. The theoretical order of convergence for the solver, ``order=2``,
45+
by default. The multiplicative factor between each data files are, ``resolution=2``,
46+
by default. This is used to test the order of the convergence of the solver
47+
when there is no analytical solutions.
48+
49+
usage: ``./convergence_plot.py files* -o out -n order -r resolution``
50+
3051
* ``dam_compare.py``: this takes an output file from the
3152
shallow water dam break problem and plots a slice through the domain
3253
together with the analytic solution (calculated in the script).

pyro/analysis/convergence.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,39 +6,47 @@
66

77
import pyro.util.io_pyro as io
88

9-
# read in two files -- one twice the resolution of the other, and
9+
# read in two files -- one N-times the resolution of the other, and
1010
# compute the error by averaging down
1111

1212
usage = """
13-
usage: ./convergence.py fine coarse
13+
usage: ./convergence.py fine coarse variable_name[optional, default=density] N[default=2]
1414
"""
1515

1616

17-
def compare(fine, coarse):
17+
def compare(fine, coarse, var_name, N):
1818

19-
dens = coarse.get_var("density")
20-
dens_avg = fine.restrict("density", N=2)
19+
var = coarse.get_var(var_name)
20+
var_avg = fine.restrict(var_name, N=N)
2121

2222
e = coarse.grid.scratch_array()
23-
e.v()[:, :] = dens.v() - dens_avg.v()
23+
e.v()[:, :] = var.v() - var_avg.v()
2424

2525
return float(np.abs(e).max()), e.norm()
2626

2727

2828
def main():
29-
if len(sys.argv) != 3:
29+
if (len(sys.argv) > 5 or len(sys.argv) < 3):
3030
print(usage)
3131
sys.exit(2)
3232

3333
fine = sys.argv[1]
3434
coarse = sys.argv[2]
3535

36+
var_name = "density"
37+
N = 2
38+
if len(sys.argv) > 3:
39+
var_name = sys.argv[3]
40+
41+
if len(sys.argv) > 4:
42+
N = int(sys.argv[4])
43+
3644
ff = io.read(fine)
3745
cc = io.read(coarse)
3846

39-
result = compare(ff.cc_data, cc.cc_data)
47+
result = compare(ff.cc_data, cc.cc_data, var_name, N)
4048

41-
print("inf norm of density: ", result)
49+
print(f"inf norm and L2 norm of {var_name}: ", result)
4250

4351

4452
if __name__ == "__main__":

pyro/analysis/convergence_plot.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#!/usr/bin/env python3
2+
3+
'''
4+
This file prints summary of the convergence given at least 3 plot files
5+
that are differ by a constant multiplicative resolution factor.
6+
It prints out a table as well as a plot.
7+
'''
8+
9+
import argparse
10+
import sys
11+
12+
import convergence
13+
import matplotlib.pyplot as plt
14+
import numpy as np
15+
from prettytable import PrettyTable
16+
17+
import pyro.util.io_pyro as io
18+
19+
DESCRIPTION = "This file does convergence plotting for the solvers"
20+
INPUT_HELP = '''The location of the input files: enter from finest to coarset files,
21+
at least 3 files are required'''
22+
OUTPUT_HELP = "File name for the convergence plot"
23+
ORDER_HELP = "The theoretical order of convergence for the solver"
24+
RESOLUTION_HELP = "Multiplicative Resolution between input files, default is 2"
25+
26+
parser = argparse.ArgumentParser(description=DESCRIPTION)
27+
parser.add_argument("input_file", nargs='+', help=INPUT_HELP)
28+
parser.add_argument("-o", "--out", default="convergence_plot.pdf", help=OUTPUT_HELP)
29+
parser.add_argument("-n", "--order", default=2, type=int, help=ORDER_HELP)
30+
parser.add_argument("-r", "--resolution", default=2, type=int, help=RESOLUTION_HELP)
31+
32+
args = parser.parse_args(sys.argv[1:])
33+
34+
35+
def convergence_plot(dataset, fname=None, order=2):
36+
''' Plot the error and its theoretical convergence line. '''
37+
38+
figure = plt.figure(figsize=(12, 7))
39+
40+
tot = len(dataset)
41+
cols = 2
42+
rows = tot // 2
43+
44+
if tot % cols != 0:
45+
rows += 1
46+
47+
for k, data in enumerate(dataset):
48+
49+
ax = figure.add_subplot(rows, cols, k+1)
50+
ax.set(
51+
title=f"{data[0]}",
52+
xlabel="$N$",
53+
ylabel="L2 Norm of Error",
54+
yscale="log",
55+
xscale="log"
56+
)
57+
58+
err = np.array(data[1])
59+
nx = np.array(data[2])
60+
61+
ax.scatter(nx, err, marker='x', color='k', label="Solutions")
62+
ax.plot(nx, err[-1]*(nx[-1]/nx)**2, linestyle="--",
63+
label=r"$\mathcal{O}$" + fr"$(\Delta x^{order})$")
64+
65+
ax.legend()
66+
67+
plt.tight_layout()
68+
69+
if fname is not None:
70+
plt.savefig(fname, format="pdf", bbox_inches="tight")
71+
72+
plt.show()
73+
74+
75+
def main():
76+
''' main driver '''
77+
78+
if len(args.input_file) < 3:
79+
raise ValueError('''Must use at least 3 plotfiles with 3 different
80+
resolutions that differ by the same multiplicative factor''')
81+
82+
field_names = ["Variable Name", "L2 Norm(1) (Finest)"]
83+
84+
for i in range(len(args.input_file[2:])):
85+
field_names.append(f"Order of Conv({i+1}:{i+2})")
86+
field_names.append(f"L2 Norm({i+2})")
87+
88+
field_names[-1] += " (Coarsest)"
89+
90+
table = PrettyTable(field_names)
91+
92+
data_file = []
93+
94+
temp_file = io.read(args.input_file[0])
95+
for variable in temp_file.cc_data.names:
96+
97+
l2norms = []
98+
nx_list = []
99+
row = [variable]
100+
101+
fdata = io.read(args.input_file[0])
102+
cdata = io.read(args.input_file[1])
103+
_, l2norm = convergence.compare(fdata.cc_data, cdata.cc_data, variable, args.resolution)
104+
105+
l2norms.append(l2norm)
106+
nx_list.append(cdata.cc_data.grid.nx)
107+
row.append(l2norm)
108+
109+
for n in range(1, len(args.input_file[:-1])):
110+
fdata = io.read(args.input_file[n])
111+
cdata = io.read(args.input_file[n+1])
112+
113+
_, l2norm = convergence.compare(fdata.cc_data, cdata.cc_data, variable, args.resolution)
114+
115+
order_conv = np.sqrt(l2norm/l2norms[-1])
116+
117+
l2norms.append(l2norm)
118+
nx_list.append(cdata.cc_data.grid.nx)
119+
row.extend([order_conv, l2norm])
120+
121+
data_file.append([variable, l2norms, nx_list])
122+
table.add_row(row)
123+
124+
print(table)
125+
convergence_plot(data_file, fname=args.out, order=args.order)
126+
127+
128+
if __name__ == "__main__":
129+
main()

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ sphinxcontrib_bibtex
1515
sphinx-copybutton
1616
sphinx-prompt
1717
importlib-metadata
18+
prettytable

0 commit comments

Comments
 (0)