Skip to content

Commit 75fc067

Browse files
committed
update front tracker script
1 parent 8f117cb commit 75fc067

File tree

2 files changed

+122
-25
lines changed

2 files changed

+122
-25
lines changed

Exec/science/xrb_spherical/analysis/front_tracker.py

Lines changed: 117 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,105 @@
44
import glob
55
import yt
66
import numpy as np
7+
import argparse
8+
from concurrent.futures import ProcessPoolExecutor, as_completed
79
from yt.frontends.boxlib.api import CastroDataset
10+
from yt.units import cm
811

9-
10-
def track_flame_front(ds, metric):
12+
def track_flame_front(ds, args):
1113
'''
1214
This function tracks the flame front position for a given dataset.
13-
It returns a tuple of the form: (Time (in ms), Theta)
15+
It returns a list of the form: [Time (in ms), Theta, averaged_max_field, Theta_max, max_field]
1416
1517
Procedure to determine flame front:
16-
1) User selects e a quantity to use as a metric: enuc or Temp
18+
1) User selects a quantity to use as a metric: enuc or Temp
1719
2) Determine the global max of that quantity
18-
3) Determine the theta position of the cell that contains the global max
20+
3) Determine the minimum value required to consider averaging zones based on global max
1921
4) Do a radial average of the data set to convert to 1D as a function of theta
20-
5) Determine flame front at theta where the metric quantity drops to
21-
metric_number * global_max of that quantity.
22+
5) Determine flame front at theta where the radially averaged quantity drops to
23+
percent * averaged_max of that quantity.
2224
'''
2325

2426
time = ds.current_time.in_units("ms")
27+
rr = ds.domain_right_edge[0].in_units("cm")
28+
rl = ds.domain_left_edge[0].in_units("cm")
29+
30+
thetar = ds.domain_right_edge[1]
31+
thetal = ds.domain_left_edge[1]
32+
dtheta = thetar - thetal
33+
34+
max_level = ds.index.max_level
35+
ref_ratio = int(np.prod(ds.ref_factors[0:max_level]))
36+
37+
# Assume default resolution using finest grid
38+
default_res = ds.domain_dimensions * ref_ratio
39+
40+
###
41+
### Select data by choosing rays at different theta
42+
###
43+
44+
# Get different possible cell-centered thetas
45+
thetas = np.linspace(thetal, thetar, default_res[1], endpoint=False) + 0.5 * dtheta / default_res[1]
46+
47+
# Container for the radially averaged field quantity, enuc or temp
48+
averaged_field = []
49+
50+
# First determine the global max of field quantity
51+
max_val = ds.all_data()[args.field].max()
52+
53+
# Determine a threshold of selecting zones for the average, i.e. minimum value allowed
54+
min_val = max_val * args.threshold
2555

56+
# track the theta that has the maximum global value
57+
max_theta_loc = 0.0
2658

27-
ad = ds.all_data()
59+
# Loop over different thetas
60+
for theta in thetas:
61+
# simply go from (rmin, theta) -> (rmax, theta). Doesn't need to convert to physical R-Z
62+
ray = ds.ray((rl, theta, 0*cm), (rr, theta, 0*cm))
2863

64+
# sort by "t", which goes from 0 to 1 representing the spatial order.
65+
# isrt = np.argsort(ray["t"])
2966

30-
# 1) Global max temperature: this can be used to track initial
31-
# detonation resulted from the initial temperature perturbation
32-
# 2) Use radially averaged enuc then find flame front is determined by
33-
# when the averaged enuc drops below some percentage of max global enuc
67+
# Do the tracking
68+
if any(ray[args.field) == max_val):
69+
max_theta_loc = theta
3470

71+
# Consider zones that are larger than minimum value
72+
valid_zones = ray[args.field] > min_val
73+
valid_values = ray[args.field][valid_zones]
74+
75+
if len(valid_values) > 0:
76+
averaged_field.append(valid_values.mean())
77+
else:
78+
averaged_field.append(0.0)
79+
80+
# Now Determine the index of the maximum radially averaged field
81+
max_index = np.argmax(averaged_field)
82+
83+
# Now assuming flame moves forward in theta, find theta such that the field drops below some threshold of the averaged max
84+
loc_index = averaged_field[max_index:] < args.percent * max(averaged_field)
85+
86+
# Find the first theta that the field drops below the threshold.
87+
theta_loc = thetas[max_index:][loc_index][0]
88+
89+
# Returns 5 quantities
90+
# 1) time in ms
91+
# 2) theta that corresponds to the maximum averaged value
92+
# 3) maximum averaged value
93+
# 4) theta that corresponds to the maximum global value
94+
# 5) maximum global value
95+
timeTheta = [time, theta_loc, max(averaged_field), max_theta_loc, max_val]
3596

3697
return timeTheta
3798

3899

100+
def process_dataset(fname, args):
101+
ds = CastroDataset(fname)
102+
103+
# Returns a list [time, theta, max averaged value, theta_max, max value]
104+
return track_flame_front(ds, args)
105+
39106
if __name__ == "__main__":
40107
parser = argparse.ArgumentParser(description="""
41108
This file tracks the flame front and writes them into a txt file.
@@ -47,9 +114,17 @@ def track_flame_front(ds, metric):
47114
metavar="{enuc, Temp}",
48115
help="""field parameter used as metric to determine flame front.
49116
Choose between {enuc, Temp}""")
50-
parser.add_argument('--percent', '-p', default=0.001, type=float,
117+
parser.add_argument('--percent', '-p', default=1e-3, type=float,
51118
help="""Float number between (0, 1]. Representing the percent of
52-
the global maximum of the field quantity used to track the flame.""")
119+
the averaged maximum of the field quantity used to track the flame.""")
120+
parser.add_argument('--threshold', '-t', default=1.e-6, type=float,
121+
help="""Float number between (0, 1]. Representine the percent of
122+
the global maximum of the field quantity used to select valid zones
123+
for averaging""")
124+
parser.add_argument('--jobs', '-j', default=1, type=int,
125+
help="""Number of workers to process plot files in parallel"""
126+
parser.add_argument('--out', '-o', default="front_tracking.dat", type=str,
127+
help="""Output filename for the tracking information""")
53128

54129
args = parser.parse_args()
55130

@@ -58,20 +133,38 @@ def track_flame_front(ds, metric):
58133
parser.error("field must be either enuc or Temp")
59134

60135
if args.percent <= 0.0 or args.percent > 1.0:
61-
parser.error("metric must be a float between (0, 1]")
136+
parser.error("percent must be a float between (0, 1]")
62137

63-
metric = (args.field, args.percent)
64-
timeThetaArray = []
138+
if args.threshold <= 0.0 or args.percent > 1.0:
139+
parser.error("threshold must be a float between (0, 1]")
65140

66-
for fname in args.fnames:
67-
ds = CastroDataset(fname)
141+
timeThetaArray = []
68142

69-
# Get tuple in form (theta, time)
70-
timeTheta= track_flame_front(ds, metric)
71-
timeThetaArray.append(timeTheta)
143+
###
144+
### Parallelize the loop. Copied from flame_wave/analysis/front_tracker.py
145+
###
146+
with concurrent.futures.ProcessPoolExecutor(max_workers=args.jobs) as executor:
147+
future_to_index = {
148+
executor.submit(process_dataset, fname, args): i
149+
for i, fname in enumerate(args.fnames)
150+
}
151+
try:
152+
for future in as_completed(future_to_index):
153+
i = future_to_index.pop(future)
154+
try:
155+
timeThetaArray.append(future.result())
156+
except Exception as exc:
157+
print(f"{args.fnames[i]} generated an exception: {exc}", file=sys.stderr, flush=True)
158+
except KeyboardInterrupt:
159+
print(
160+
"\n*** got ctrl-c, cancelling remaining tasks and waiting for existing ones to finish...\n",
161+
flush=True,
162+
)
163+
executor.shutdown(wait=True, cancel_futures=True)
164+
sys.exit(1)
72165

73166
# Sort array by time and write to file
74-
timeThetaArray.sort()
167+
timeThetaArray.sort(key=lambda x: x[0])
75168
timeThetaArray = np.array(timeThetaArray)
76169

77-
np.savetxt('front_tracking.dat', timeThetaArray, delimiter=',')
170+
np.savetxt(args.out, timeThetaArray, delimiter=',')

Exec/science/xrb_spherical/analysis/slice.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,12 @@ def slice(fnames:List[str], fields:List[str],
9898
elif field == "Temp":
9999
sp.set_zlim(field, 5.e7, 2.5e9)
100100
sp.set_cmap(field, "magma_r")
101+
elif field == "abar":
102+
sp.set_zlim(field, 4, 8)
103+
sp.set_log(field, False)
104+
sp.set_cmap(field, "plasma_r")
101105
elif field == "enuc":
102-
sp.set_zlim(field, 1.e18, 1.e20)
106+
sp.set_zlim(field, 1.e15, 1.e20)
103107
elif field == "density":
104108
sp.set_zlim(field, 1.e-3, 5.e8)
105109

0 commit comments

Comments
 (0)