2 .. module:: graph_transport.py
3 :synopsis: simulate transport on a pipe network representaiton of a DFN
4 .. moduleauthor:: Shriram Srinivasan <shrirams@lanl.gov>
14 import multiprocessing
as mp
21 """ Create a list of downstream neighbor vertices for every vertex on NetworkX graph obtained after running graph_flow
25 Gtilde: NetworkX graph
26 obtained from output of graph_flow
30 dict : nested dictionary.
34 dict[n]['child'] is a list of vertices downstream to vertex n
35 dict[n]['prob'] is a list of probabilities for choosing a downstream node for vertex n
40 for i
in nx.nodes(Gtilde):
42 if Gtilde.nodes[i][
'outletflag']:
49 for v
in nx.neighbors(Gtilde, i):
50 delta_p = Gtilde.nodes[i][
'pressure'] - Gtilde.nodes[v][
'pressure']
52 if delta_p > np.spacing(Gtilde.nodes[i][
'pressure']):
54 prob_list.append(Gtilde.edges[i, v][
'flux'])
57 nbrs_dict[i][
'child'] = node_list
58 nbrs_dict[i][
'prob'] = np.array(prob_list,
59 dtype=float) / sum(prob_list)
61 nbrs_dict[i][
'child'] =
None
62 nbrs_dict[i][
'prob'] =
None
69 Class for graph particle tracking, instantiated for each particle
73 * time : Total advective time of travel of particle [s]
74 * tdrw_time : Total advection+diffusion time of travel of particle [s]
75 * dist : total distance travelled in advection [m]
76 * flag : True if particle exited system, else False
77 * frac_seq : Dictionary, contains information about fractures through which the particle went
87 """ Set initial value for travel time and distance
106 def track(self, Gtilde, nbrs_dict, frac_porosity, tdrw_flag,
107 matrix_porosity, matrix_diffusivity):
108 """ track a particle from inlet vertex to outlet vertex
112 Gtilde : NetworkX graph
113 graph obtained from graph_flow
115 nbrs_dict: nested dictionary
116 dictionary of downstream neighbors for each vertex
123 Inlet = [v
for v
in nx.nodes(Gtilde)
if Gtilde.nodes[v][
'inletflag']]
125 curr_v = numpy.random.choice(Inlet)
129 if Gtilde.nodes[curr_v][
'outletflag']:
133 if nbrs_dict[curr_v][
'child']
is None:
134 self.
flagflag =
False
137 next_v = numpy.random.choice(nbrs_dict[curr_v][
'child'],
138 p=nbrs_dict[curr_v][
'prob'])
140 frac = Gtilde.edges[curr_v, next_v][
'frac']
142 t = Gtilde.edges[curr_v, next_v][
'time'] * frac_porosity
145 a_nondim = matrix_porosity * math.sqrt(
147 (12 * Gtilde.edges[curr_v, next_v][
'perm']))
148 xi = numpy.random.random_sample()
149 t_tdrw = t + math.pow(a_nondim * t / scipy.special.erfcinv(xi),
154 L = Gtilde.edges[curr_v, next_v][
'length']
159 """ add details of fracture through which particle traversed
166 index of fracture in graph
186 self.
frac_seqfrac_seq[frac][
'time'] += t
187 self.
frac_seqfrac_seq[frac][
'tdrw_time'] += t_tdrw
188 self.
frac_seqfrac_seq[frac][
'dist'] += L
194 """ write particle data to output files, if supplied
200 partime_file : string
201 name of file to which the total travel times and lengths will be written for each particle, default is None
203 frac_id_file : string
204 name of file to which detailed information of each particle's travel will be written, default is None
210 if partime_file
is not None:
211 with open(partime_file,
"a")
as f1:
212 f1.write(
"{:3.3E} {:3.3E} {:3.3E} {:3.3E} \n".format(
216 if frac_id_file
is not None:
219 key
for key
in self.
frac_seqfrac_seq
if isinstance(key, dict)
is False
222 with open(frac_id_file,
"a")
as f2:
224 f2.write(
"{:d} ".format(data1[i]))
243 """ opens the output files partime_file and frac_id_file and writes the
249 partime_file : string
250 name of file to which the total travel times and lengths will be written for each particle
252 frac_id_file : string
253 name of file to which detailed information of each particle's travel will be written
261 with open(partime_file,
"w")
as f1:
263 "# advective time (s) advection+diffusion time (s) diffusion time (s) total advection distance covered (m)\n"
266 error =
"ERROR: Unable to open supplied partime_file file {}\n".format(
268 sys.stderr.write(error)
272 with open(frac_id_file,
"w")
as f2:
273 f2.write(
"# List of fractures each particle visits\n")
278 error =
"ERROR: Unable to open supplied frac_id_file file {}\n".format(
280 sys.stderr.write(error)
285 """ If running graph transport in parallel, this function dumps out all the
286 particle information is a single pass rather then opening and closing the
287 files for every particle
293 list of particle objects
295 partime_file : string
296 name of file to which the total travel times and lengths will be written for each particle
298 frac_id_file : string
299 name of file to which detailed information of each particle's travel will be written
304 Number of particles that do not exit the domain
310 f1 = open(partime_file,
"a")
311 f2 = open(frac_id_file,
"a")
315 for particle
in particles:
317 f1.write(
"{:3.3E} {:3.3E} {:3.3E} {:3.3E} \n".format(
318 particle.time, particle.tdrw_time,
319 particle.tdrw_time - particle.time, particle.dist))
322 key
for key
in particle.frac_seq
323 if isinstance(key, dict)
is False
328 f2.write(
"{:d} ".format(data1[i]))
353 """ Tracks a single particle through the graph
355 all input parameters are in the dictionary named data
360 Gtilde : NetworkX graph
361 obtained from graph_flow
364 see function create_neighbor_list
367 porosity of fracture, default is 1.0
370 if False, matrix_porosity, matrix_diffusivity are ignored
372 matrix_porosity: float
375 matrix_diffusivity: float
376 default is 1e-11 m^2/s
381 particle trajectory information
386 particle.set_start_time_dist(0, 0)
387 particle.track(data[
"Gtilde"], data[
"nbrs_dict"], data[
"frac_porosity"],
388 data[
"tdrw_flag"], data[
"matrix_porosity"],
389 data[
"matrix_diffusivity"])
401 matrix_porosity=0.02,
402 matrix_diffusivity=1e-11):
403 """ Run particle tracking on the given NetworkX graph
410 Gtilde : NetworkX graph
411 obtained from graph_flow
416 partime_file : string
417 name of file to which the total travel times and lengths will be written for each particle
419 frac_id_file : string
420 name of file to which detailed information of each particle's travel will be written
423 porosity of fracture, default is 1.0
425 if False, matrix_porosity, matrix_diffusivity are ignored
426 matrix_porosity: float
428 matrix_diffusivity: float
429 default is 1e-11 in SI units
436 Information on individual functions is found therein
441 print(
"--> Creating downstream neighbor list")
443 Inlet = [v
for v
in nx.nodes(Gtilde)
if Gtilde.nodes[v][
'inletflag']]
446 print(
"--> Starting particle tracking for %d particles" % nparticles)
449 print(
"--> Using %d processors" % self.ncpu)
451 for i
in range(nparticles):
454 data[
"Gtilde"] = Gtilde
455 data[
"nbrs_dict"] = nbrs_dict
456 data[
"frac_porosity"] = frac_porosity
457 data[
"tdrw_flag"] = tdrw_flag
458 data[
"matrix_porosity"] = matrix_porosity
459 data[
"matrix_diffusivity"] = matrix_diffusivity
462 mp_input.append(data)
464 pool = mp.Pool(self.ncpu)
465 particles = pool.map(track_particle, mp_input)
469 print(
"--> Tracking Complete")
470 print(
"--> Writing Data to files: {} and {}".format(
471 partime_file, frac_id_file))
473 print(
"--> Writing Data Complete")
477 for i
in range(nparticles):
479 print(
"--> Starting particle %d out of %d" % (i, nparticles))
481 particle_i.set_start_time_dist(0, 0)
482 particle_i.track(Gtilde, nbrs_dict, frac_porosity, tdrw_flag,
483 matrix_porosity, matrix_diffusivity)
486 particle_i.write_file(partime_file, frac_id_file)
491 print(
"--> All particles exited")
493 print(
"--> Out of {} particles, {} particles did not exit".format(
494 nparticles, pfailcount))
def track(self, Gtilde, nbrs_dict, frac_porosity, tdrw_flag, matrix_porosity, matrix_diffusivity)
def set_start_time_dist(self, t, L)
def write_file(self, partime_file=None, frac_id_file=None)
def add_frac_data(self, frac, t, t_tdrw, L)
def dump_particle_info(particles, partime_file, frac_id_file)
def create_neighbor_list(Gtilde)
def run_graph_transport(self, Gtilde, nparticles, partime_file, frac_id_file, frac_porosity=1.0, tdrw_flag=False, matrix_porosity=0.02, matrix_diffusivity=1e-11)
def prepare_output_files(partime_file, frac_id_file)