Peano
Loading...
Searching...
No Matches
compare_results.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2
3"""!
4Compare smoothing lengths that Peano yields and what you expect them to be.
5"""
6
7import h5py
8from scipy.spatial import KDTree
9import numpy as np
10import argparse
11import sys
12import peano4
14import swift2.sphtools
15import multiprocessing
16
17
18parser = argparse.ArgumentParser(
19 description="""
20 Compare Smoothing Length Results With Expectations. This script assumes that
21 particles may have variable smoothing lengths.
22
23 This script has two main modes of usage, which are determined by the
24 provided initial conditions file via the `--ic-file` argument.
25
26 If the IC file is a hdf5 file, the script assumes that we're running a
27 unit test where swift2 is computing smoothing lengths from randompy sampled
28 particles. It will then look for a `particles-0.vtu` file in this directory
29 to compare results to.
30
31 If the IC file is a .vtu file, the script assumes that you want to verify a
32 swift2 snapshot. It will then compute smoothing lengths for the particle positions
33 given in the .vtu file, and compare them to the smoothing lengths from the same
34 .vtu file.
35 """
36)
37parser.add_argument(
38 "-ic",
39 "--ic-file",
40 dest="ic_filename",
41 help="Initial conditions file. Must be a .hdf5 or .vtu file.",
42 required=True,
43)
44parser.add_argument(
45 "-n",
46 "--neighbours",
47 dest="nneigh",
48 type=float, # this is not a typo. We want average number of neighbours, which needn't be an int.
49 default=None,
50 help="Number of neighbours to search for. You must specify either --eta or --neighbours",
51)
52parser.add_argument(
53 "-e",
54 "--eta",
55 dest="eta",
56 type=float,
57 default=None,
58 help="Target resolution eta. You must specify either --eta or --neighbours",
59)
60parser.add_argument(
61 "-d",
62 "--ndim",
63 dest="ndim",
64 type=int,
65 help="Number of dimensions to work with",
66 required=True,
67)
68parser.add_argument(
69 "-ht",
70 "--h-tolerance",
71 dest="h_tol",
72 type=float,
73 default=1.0e-6,
74 help="Threshold for convergence of smoothing length",
75)
76parser.add_argument(
77 "-k",
78 "--kernel",
79 dest="kernel",
80 type=str,
81 default="quartic_spline",
82 help="SPH kernel function to use.",
83 choices=swift2.sphtools.sph_kernel_list,
84)
85
86args = parser.parse_args()
87icfile = args.ic_filename
88h_tolerance = args.h_tol
89kernel = args.kernel
90if kernel == "quartic_spline":
91 kernel = "quartic_spline_vectorized"
92ndim = args.ndim
93
94eta = args.eta
95nneigh = args.nneigh
96
97if eta is None and nneigh is None:
98 raise ValueError("You need to specify either --eta or --neighbours")
99if eta is not None and nneigh is not None:
100 raise ValueError("You need to specify either --eta or --neighbours, not both")
101if nneigh is not None:
102 eta = swift2.sphtools.eta_from_number_of_neighbours(
103 nneigh, kernel=kernel, ndim=ndim
104 )
105else:
106 nneigh = swift2.sphtools.number_of_neighbours_from_eta(
107 eta, kernel=kernel, ndim=ndim
108 )
109
110# above this (relative) threshold, treat deviations as an error
111# Make this tolerance dependant on the used h_tolerance. Add a
112# factor of 10 to allow for float round-off errors and the likes.
113# Naturally this assumes that h_tolerance is small. Typically <= 1e-4
114comparison_tolerance = 10.0 * h_tolerance
115comparison_tolerance = max(comparison_tolerance, 1.0e-4)
116
117
118print(" Running comparison")
119print(" --- IC file: ", icfile)
120print(" --- Dimensions: ", ndim)
121print(" --- SPH Kernel: ", kernel)
122print(" --- Target number of neighbours:", nneigh)
123print(" --- Target eta: ", eta)
124print(" --- h tolerance: ", h_tolerance)
125print(" --- comparison tolerance: ", comparison_tolerance)
126
127# Get IC data
128# -------------------------
129
130if icfile.endswith(".hdf5"):
131 # Doing a hdf5 run.
132 vtufile = "particles-1.vtu"
133
134 hfile = h5py.File(icfile, "r")
135 gas = hfile["PartType0"]
136 coords = gas["Coordinates"][:]
137 coords = coords[:, :ndim]
138 ids = gas["ParticleIDs"][:]
139 hfile.close()
140
141elif icfile.endswith(".vtu"):
142 # Doing a snapshot verification
143 vtufile = icfile
144
145 reader = ParticleVTUReader(vtufile=vtufile, verbose=False)
146 partData = reader.load()
147 coords = partData.x[:, :ndim]
148 ids = partData.partid
149
150else:
151 raise ValueError("Unrecognized file type {icfile}, looking for .vtu or .hdf5 file.")
152
153# Sort by particle ID
154sort_ic = np.argsort(ids)
155ids = ids[sort_ic]
156coords = coords[sort_ic]
157
158
159# Get expected results
160# -------------------------
161
162tree = KDTree(coords)
163distances, indexes = tree.query(coords, k=2 * nneigh + 10 * ndim)
164neighbours = coords[indexes]
165
166npart = coords.shape[0]
167sml_python = np.zeros((npart))
168
169
171 # the KDTree returns the particle itself as a neighbour too.
172 # it is stored at the first index, with distance 0.
173 xp = neighbours[i, 0, :]
174 xn = neighbours[i, 1:, :]
175
176 verb = False
177 h = swift2.sphtools.find_smoothing_length(
178 xp, xn, kernel=kernel, eta=eta, h_tolerance=h_tolerance, ndim=ndim, verbose=verb
179 )
180 return h
181
182
183pool = multiprocessing.Pool()
184sml_python[:] = pool.map(sml_search, (i for i in range(npart)))
185
186
187# Read in peano output
188# -------------------------------
189
190reader = ParticleVTUReader(vtufile=vtufile, verbose=False)
191partData = reader.load()
192sml_peano = partData.smoothingLength
193partIDs_peano = partData.partid
194
195# sort particles by ID
196sort_peano = np.argsort(partIDs_peano)
197sml_peano = sml_peano[sort_peano]
198partIDs_peano = partIDs_peano[sort_peano]
199
200
201# Compare resutls
202# -----------------------------
203
204npart = sml_peano.shape[0]
205
206errors = 0
207errorstr = (
208 "Found difference: "
209 + "particle ID = {0:4d} "
210 + "h_peano = {1:.5e} "
211 + "h_python = {2:.5e} "
212 + "diff = {3:.5e}"
213)
214
215for i in range(npart):
216 diff = 1.0 - sml_peano[i] / sml_python[i]
217
218 if abs(diff) > comparison_tolerance:
219 errors += 1
220 print(errorstr.format(int(partIDs_peano[i]), sml_peano[i], sml_python[i], diff))
221
222
223print(f"\nSummary run with {nneigh} neighbours:")
224print(f"Finished with {errors} errors.")
225if errors > 0:
226 print("Before you panic:")
227 print(" - Are you using the correct kernel in this script?")
228 print(" - Are you using the correct resolution eta in this script?")
229 print(" - Are you hitting h_min/h_max limits?")
230 print("Other ToDo's left for this script: ")
231 print(" - corrections for periodic wrapping ")
232
233
234sys.exit(errors)
Reads in particle fields from a single .vtu file, or all .vtu at a given snapshot time specified in t...
This file is part of the SWIFT 2 project.
Definition __init__.py:1