pydfnWorks
python wrapper for dfnWorks
pflotran.py
Go to the documentation of this file.
1 """
2 functions for using pflotran in dfnworks
3 """
4 import os
5 import subprocess
6 import sys
7 import glob
8 import shutil
9 import ntpath
10 from time import time
11 import numpy as np
12 
13 
14 def lagrit2pflotran(self, inp_file='', mesh_type='', hex2tet=False):
15  """ Takes output from LaGriT and processes it for use in PFLOTRAN.
16  Calls the functuon write_perms_and_correct_volumes_areas() and zone2ex
17 
18  Parameters
19  --------------
20  self : object
21  DFN Class
22  inp_file : str
23  Name of the inp (AVS) file produced by LaGriT
24  mesh_type : str
25  The type of mesh
26  hex2tet : bool
27  True if hex mesh elements should be converted to tet elements, False otherwise.
28 
29  Returns
30  --------
31  None
32 
33  Notes
34  --------
35  None
36 
37  """
38  if self.flow_solver != "PFLOTRAN":
39  error = "ERROR! Wrong flow solver requested\n"
40  sys.stderr.write(error)
41  sys.exit(1)
42 
43  print('=' * 80)
44  print("Starting conversion of files for PFLOTRAN ")
45  print('=' * 80)
46  if inp_file:
47  self.inp_file = inp_file
48  else:
49  inp_file = self.inp_file
50 
51  if inp_file == '':
52  error = 'ERROR: Please provide inp filename!\n'
53  sys.stderr.write(error)
54  sys.exit(1)
55 
56  if mesh_type:
57  if mesh_type in mesh_types_allowed:
58  self.mesh_type = mesh_type
59  else:
60  error = 'ERROR: Unknown mesh type. Select one of dfn, volume or mixed!\n'
61  sys.stderr.write(error)
62  sys.exit(1)
63  else:
64  mesh_type = self.mesh_type
65 
66  if mesh_type == '':
67  error = 'ERROR: Please provide mesh type!\n'
68  sys.stderr.write(error)
69  sys.exit(1)
70 
71  # Check if UGE file was created by LaGriT, if it does not exists, exit
72  self.uge_file = inp_file[:-4] + '.uge'
73  if not os.path.isfile(self.uge_file):
74  error = 'ERROR!!! Cannot find .uge file\nExiting\n'
75  sys.stderr.write(error)
76  sys.exit(1)
77 
78  if mesh_type == 'dfn':
79  self.write_perms_and_correct_volumes_areas(
80  ) # Make sure perm and aper files are specified
81 
82  # Convert zone files to ex format
83  #self.zone2ex(zone_file='boundary_back_s.zone',face='south')
84  #self.zone2ex(zone_file='boundary_front_n.zone',face='north')
85  #self.zone2ex(zone_file='boundary_left_w.zone',face='west')
86  #self.zone2ex(zone_file='boundary_right_e.zone',face='east')
87  #self.zone2ex(zone_file='boundary_top.zone',face='top')
88  #self.zone2ex(zone_file='boundary_bottom.zone',face='bottom')
89  self.zone2ex(zone_file='all')
90  print('=' * 80)
91  print("Conversion of files for PFLOTRAN complete")
92  print('=' * 80)
93  print("\n\n")
94 
95 
96 def zone2ex(self,
97  uge_file='',
98  zone_file='',
99  face='',
100  boundary_cell_area=1.e-1):
101  """
102  Convert zone files from LaGriT into ex format for LaGriT
103 
104  Parameters
105  -----------
106  self : object
107  DFN Class
108  uge_file : string
109  Name of uge file
110  zone_file : string
111  Name of zone file
112  Face : Face of the plane corresponding to the zone file
113  zone_file : string
114  Name of zone file to work on. Can be 'all' processes all directions, top, bottom, left, right, front, back
115  boundary_cell_area : double
116  should be a large value relative to the mesh size to force pressure boundary conditions.
117 
118  Returns
119  ----------
120  None
121 
122  Notes
123  ----------
124  the boundary_cell_area should be a function of h, the mesh resolution
125  """
126 
127  print('--> Converting zone files to ex')
128  if self.uge_file:
129  uge_file = self.uge_file
130  else:
131  self.uge_file = uge_file
132 
133  uge_file = self.uge_file
134  if uge_file == '':
135  error = 'ERROR: Please provide uge filename!\n'
136  sys.stderr.write(error)
137  sys.exit(1)
138 
139  # Opening uge file
140  print('\n--> Opening uge file')
141  fuge = open(uge_file, 'r')
142 
143  # Reading cell ids, cells centers and cell volumes
144  line = fuge.readline()
145  line = line.split()
146  NumCells = int(line[1])
147 
148  Cell_id = np.zeros(NumCells, 'int')
149  Cell_coord = np.zeros((NumCells, 3), 'float')
150  Cell_vol = np.zeros(NumCells, 'float')
151 
152  for cells in range(NumCells):
153  line = fuge.readline()
154  line = line.split()
155  Cell_id[cells] = int(line.pop(0))
156  line = [float(id) for id in line]
157  Cell_vol[cells] = line.pop(3)
158  Cell_coord[cells] = line
159  fuge.close()
160 
161  print('--> Finished with uge file\n')
162 
163  # loop through zone files
164  if zone_file == 'all':
165  zone_files = ['pboundary_front_n.zone', 'pboundary_back_s.zone', 'pboundary_left_w.zone', \
166  'pboundary_right_e.zone', 'pboundary_top.zone', 'pboundary_bottom.zone']
167  face_names = ['north', 'south', 'west', 'east', 'top', 'bottom']
168  else:
169  if zone_file == '':
170  error = 'ERROR: Please provide boundary zone filename!\n'
171  sys.stderr.write(error)
172  sys.exit(1)
173  if face == '':
174  error = 'ERROR: Please provide face name among: top, bottom, north, south, east, west !\n'
175  sys.stderr.write(error)
176  sys.exit(1)
177  zone_files = [zone_file]
178  face_names = [face]
179 
180  for iface, zone_file in enumerate(zone_files):
181  face = face_names[iface]
182  # Ex filename
183  ex_file = zone_file.strip('zone') + 'ex'
184 
185  # Opening the input file
186  print('--> Opening zone file: ', zone_file)
187  fzone = open(zone_file, 'r')
188  fzone.readline()
189  fzone.readline()
190  fzone.readline()
191 
192  # Read number of boundary nodes
193  print('--> Calculating number of nodes')
194  num_nodes = int(fzone.readline())
195  Node_array = np.zeros(num_nodes, 'int')
196  # Read the boundary node ids
197  print('--> Reading boundary node ids')
198 
199  if (num_nodes < 10):
200  g = fzone.readline()
201  node_array = g.split()
202  # Convert string to integer array
203  node_array = [int(id) for id in node_array]
204  Node_array = np.asarray(node_array)
205  else:
206  for i in range(int(num_nodes / 10 + (num_nodes % 10 != 0))):
207  g = fzone.readline()
208  node_array = g.split()
209  # Convert string to integer array
210  node_array = [int(id) for id in node_array]
211  if (num_nodes - 10 * i < 10):
212  for j in range(num_nodes % 10):
213  Node_array[i * 10 + j] = node_array[j]
214  else:
215  for j in range(10):
216  Node_array[i * 10 + j] = node_array[j]
217  fzone.close()
218  print('--> Finished with zone file')
219 
220  if self.h == "":
221  from pydfnworks.dfnGen.meshing.mesh_dfn_helper import parse_params_file
222  _, self.h, _, _, _ = parse_params_file(quiet=True)
223 
224  Boundary_cell_area = np.zeros(num_nodes, 'float')
225  for i in range(num_nodes):
226  Boundary_cell_area[
227  i] = boundary_cell_area # Fix the area to a large number
228 
229  print('--> Finished calculating boundary connections')
230  boundary_cell_coord = [
231  Cell_coord[Cell_id[i - 1] - 1] for i in Node_array
232  ]
233  epsilon = self.h * 10**-3
234 
235  if (face == 'top'):
236  boundary_cell_coord = [[cell[0], cell[1], cell[2] + epsilon]
237  for cell in boundary_cell_coord]
238  elif (face == 'bottom'):
239  boundary_cell_coord = [[cell[0], cell[1], cell[2] - epsilon]
240  for cell in boundary_cell_coord]
241  elif (face == 'north'):
242  boundary_cell_coord = [[cell[0], cell[1] + epsilon, cell[2]]
243  for cell in boundary_cell_coord]
244  elif (face == 'south'):
245  boundary_cell_coord = [[cell[0], cell[1] - epsilon, cell[2]]
246  for cell in boundary_cell_coord]
247  elif (face == 'east'):
248  boundary_cell_coord = [[cell[0] + epsilon, cell[1], cell[2]]
249  for cell in boundary_cell_coord]
250  elif (face == 'west'):
251  boundary_cell_coord = [[cell[0] - epsilon, cell[1], cell[2]]
252  for cell in boundary_cell_coord]
253  elif (face == 'well'):
254  boundary_cell_coord = [[cell[0], cell[1], cell[2] + epsilon]
255  for cell in boundary_cell_coord]
256  elif (face == 'none'):
257  boundary_cell_coord = [[cell[0], cell[1], cell[2]]
258  for cell in boundary_cell_coord]
259  else:
260  error = 'ERROR: unknown face. Select one of: top, bottom, east, west, north, south.\n'
261  sys.stderr.write(error)
262  sys.exit(1)
263 
264  with open(ex_file, 'w') as f:
265  f.write('CONNECTIONS\t%i\n' % Node_array.size)
266  for idx, cell in enumerate(boundary_cell_coord):
267  f.write('%i\t%.6e\t%.6e\t%.6e\t%.6e\n' %
268  (Node_array[idx], cell[0], cell[1], cell[2],
269  Boundary_cell_area[idx]))
270  print('--> Finished writing ex file "' + ex_file +
271  '" corresponding to the zone file: ' + zone_file + '\n')
272 
273  print('--> Converting zone files to ex complete')
274 
275 
277  """ Write permeability values to perm_file, write aperture values to aper_file, and correct volume areas in uge_file
278 
279  Parameters
280  ----------
281  self : object
282  DFN Class
283 
284  Returns
285  ---------
286  None
287 
288  Notes
289  ----------
290  Calls executable correct_uge
291  """
292  import h5py
293  if self.flow_solver != "PFLOTRAN":
294  error = "ERROR! Wrong flow solver requested\n"
295  sys.stderr.write(error)
296  sys.exit(1)
297 
298  print("--> Writing Perms and Correct Volume Areas")
299  inp_file = self.inp_file
300  if inp_file == '':
301  error = 'ERROR: inp file must be specified!\n'
302  sys.stderr.write(error)
303  sys.exit(1)
304 
305  uge_file = self.uge_file
306  if uge_file == '':
307  error = 'ERROR: uge file must be specified!\n'
308  sys.stderr.write(error)
309  sys.exit(1)
310 
311  perm_file = self.perm_file
312  if perm_file == '' and self.perm_cell_file == '':
313  error = 'ERROR: perm file must be specified!\n'
314  sys.stderr.write(error)
315  sys.exit(1)
316 
317  aper_file = self.aper_file
318  aper_cell_file = self.aper_cell_file
319  if aper_file == '' and self.aper_cell_file == '':
320  error = 'ERROR: aperture file must be specified!\n'
321  sys.stderr.write(error)
322  sys.exit(1)
323 
324  mat_file = 'materialid.dat'
325  t = time()
326  # Make input file for C UGE converter
327  f = open("convert_uge_params.txt", "w")
328  f.write("%s\n" % inp_file)
329  f.write("%s\n" % mat_file)
330  f.write("%s\n" % uge_file)
331  f.write("%s" % (uge_file[:-4] + '_vol_area.uge\n'))
332  if self.aper_cell_file:
333  f.write("%s\n" % self.aper_cell_file)
334  f.write("1\n")
335  else:
336  f.write("%s\n" % self.aper_file)
337  f.write("-1\n")
338  f.close()
339 
340  cmd = os.environ['CORRECT_UGE_EXE'] + ' convert_uge_params.txt'
341  failure = subprocess.call(cmd, shell=True)
342  if failure > 0:
343  error = 'ERROR: UGE conversion failed\nExiting Program\n'
344  sys.stderr.write(error)
345  sys.exit(1)
346 
347  elapsed = time() - t
348  print('--> Time elapsed for UGE file conversion: %0.3f seconds\n' %
349  elapsed)
350  # need number of nodes and mat ID file
351  print('--> Writing HDF5 File')
352  materialid = np.genfromtxt(mat_file, skip_header=3).astype(int)
353  materialid = -1 * materialid - 6
354  NumIntNodes = len(materialid)
355 
356  if perm_file:
357  filename = 'dfn_properties.h5'
358  h5file = h5py.File(filename, mode='w')
359  print('--> Beginning writing to HDF5 file')
360  print('--> Allocating cell index array')
361  iarray = np.zeros(NumIntNodes, '=i4')
362  print('--> Writing cell indices')
363  # add cell ids to file
364  for i in range(NumIntNodes):
365  iarray[i] = i + 1
366  dataset_name = 'Cell Ids'
367  h5dset = h5file.create_dataset(dataset_name, data=iarray)
368 
369  print('--> Allocating permeability array')
370  perm = np.zeros(NumIntNodes, '=f8')
371 
372  print('--> reading permeability data')
373  print('--> Note: this script assumes isotropic permeability')
374  perm_list = np.genfromtxt(perm_file, skip_header=1)
375  perm_list = np.delete(perm_list, np.s_[1:5], 1)
376 
377  matid_index = -1 * materialid - 7
378  for i in range(NumIntNodes):
379  j = matid_index[i]
380  if int(perm_list[j, 0]) == materialid[i]:
381  perm[i] = perm_list[j, 1]
382  else:
383  error = 'Indexing Error in Perm File\n'
384  sys.stderr.write(error)
385  sys.exit(1)
386 
387  dataset_name = 'Permeability'
388  h5dset = h5file.create_dataset(dataset_name, data=perm)
389 
390  h5file.close()
391  print("--> Done writing permeability to h5 file")
392  del perm_list
393 
394  if self.perm_cell_file:
395  filename = 'dfn_properties.h5'
396  h5file = h5py.File(filename, mode='w')
397 
398  print('--> Beginning writing to HDF5 file')
399  print('--> Allocating cell index array')
400  iarray = np.zeros(NumIntNodes, '=i4')
401  print('--> Writing cell indices')
402  # add cell ids to file
403  for i in range(NumIntNodes):
404  iarray[i] = i + 1
405  dataset_name = 'Cell Ids'
406  h5dset = h5file.create_dataset(dataset_name, data=iarray)
407  print('--> Allocating permeability array')
408  perm = np.zeros(NumIntNodes, '=f8')
409  print('--> reading permeability data')
410  print('--> Note: this script assumes isotropic permeability')
411  f = open(self.perm_cell_file, 'r')
412  f.readline()
413  perm_list = []
414  while True:
415  h = f.readline()
416  h = h.split()
417  if h == []:
418  break
419  h.pop(0)
420  perm_list.append(h)
421 
422  perm_list = [float(perm[0]) for perm in perm_list]
423 
424  dataset_name = 'Permeability'
425  h5dset = h5file.create_dataset(dataset_name, data=perm_list)
426  f.close()
427 
428  h5file.close()
429  print('--> Done writing permeability to h5 file')
430 
431 
432 def pflotran(self, transient=False, restart=False, restart_file=''):
433  """ Run PFLOTRAN. Copy PFLOTRAN run file into working directory and run with ncpus
434 
435  Parameters
436  ----------
437  self : object
438  DFN Class
439  transient : bool
440  Boolean if PFLOTRAN is running in transient mode
441  restart : bool
442  Boolean if PFLOTRAN is restarting from checkpoint
443  restart_file : string
444  Filename of restart file
445 
446  Returns
447  ----------
448  None
449 
450  Notes
451  ----------
452  Runs PFLOTRAN Executable, see http://www.pflotran.org/ for details on PFLOTRAN input cards
453  """
454  if self.flow_solver != "PFLOTRAN":
455  error = "ERROR! Wrong flow solver requested\n"
456  sys.stderr.write(error)
457  sys.exit(1)
458 
459  try:
460  shutil.copy(os.path.abspath(self.dfnFlow_file),
461  os.path.abspath(os.getcwd()))
462  except:
463  error = "--> ERROR!! Unable to copy PFLOTRAN input file\n"
464  sys.stderr.write(error)
465  sys.exit(1)
466 
467  print("=" * 80)
468  print("--> Running PFLOTRAN")
469  cmd = os.environ['PETSC_DIR']+'/'+os.environ['PETSC_ARCH']+'/bin/mpirun -np ' + str(self.ncpu) + \
470  ' ' + os.environ['PFLOTRAN_EXE'] + ' -pflotranin ' + self.local_dfnFlow_file
471  print("Running: %s" % cmd)
472  subprocess.call(cmd, shell=True)
473  if not transient:
474  if not check_pflotran_convergence(self.local_dfnFlow_file):
475  error = "ERROR!!!! PFLOTRAN did not converge. Consider running in transient mode to march to steady-state\n"
476  sys.stderr.write(error)
477  sys.exit(1)
478 
479  if restart:
480  try:
481  shutil.copy(os.path.abspath(restart_file),
482  os.path.abspath(os.getcwd()))
483  except:
484  error = "--> ERROR!! Unable to copy PFLOTRAN restart input file\n"
485  sys.stderr.write(error)
486  sys.exit(1)
487 
488  print("=" * 80)
489  print("--> Running PFLOTRAN")
490  cmd = os.environ['PETSC_DIR']+'/'+os.environ['PETSC_ARCH']+'/bin/mpirun -np ' + str(self.ncpu) + \
491  ' ' + os.environ['PFLOTRAN_EXE'] + ' -pflotranin ' + ntpath.basename(restart_file)
492  print("Running: %s" % cmd)
493  subprocess.call(cmd, shell=True)
494 
495  print('=' * 80)
496  print("--> Running PFLOTRAN Complete")
497  print('=' * 80)
498  print("\n")
499 
500 
501 def check_pflotran_convergence(pflotran_input_file):
502  """ checks pflotran_input_file.out for convergence
503 
504  Parameters
505  ----------
506  pflotran_input_file : string
507  pflotran_input_file
508 
509  Returns
510  ----------
511  bool
512  True if solver converged / False if not
513  Notes
514  ----------
515 """
516  # check if PFLOTRAN ran in steady-state mode
517  steady = False
518  with open(pflotran_input_file, "r") as fp:
519  for line in fp.readlines():
520  if "STEADY_STATE" in line:
521  steady = True
522 
523  if steady:
524  pflotran_output_file = pflotran_input_file[:-2] + "out"
525  print("\n--> Opening %s to check for convergence" %
526  pflotran_output_file)
527  with open(pflotran_output_file, "r") as fp:
528  for line in fp.readlines():
529  if "STEADY-SOLVE 1 snes_conv_reason:" in line:
530  print("--> PFLOTRAN converged")
531  print(line + "\n")
532  return True
533  return False
534  else:
535  print(
536  "PFLOTRAN ran in transient mode, unable to check for steady-state convergence"
537  )
538  return True
539 
540 
541 def pflotran_cleanup(self, index_start=0, index_finish=1, filename=''):
542  """pflotran_cleanup
543  Concatenate PFLOTRAN output files and then delete them
544 
545  Parameters
546  -----------
547  self : object
548  DFN Class
549  index : int
550  If PFLOTRAN has multiple dumps use this to pick which dump is put into cellinfo.dat and darcyvel.dat
551  Returns
552  ----------
553  None
554 
555  Notes
556  ----------
557  Can be run in a loop over all pflotran dumps
558  """
559  if self.flow_solver != "PFLOTRAN":
560  error = "ERROR! Wrong flow solver requested\n"
561  sys.stderr.write(error)
562  sys.exit(1)
563 
564  if filename == '':
565  filename = self.local_dfnFlow_file[:-3]
566  else:
567  filename = ntpath.basename(filename[:-3])
568 
569  print('--> Processing PFLOTRAN output')
570 
571  for index in range(index_start, index_finish + 1):
572  cmd = 'cat ' + filename + '-cellinfo-%03d-rank*.dat > cellinfo_%03d.dat' % (
573  index, index)
574  print("Running >> %s" % cmd)
575  subprocess.call(cmd, shell=True)
576 
577  cmd = 'cat ' + filename + '-darcyvel-%03d-rank*.dat > darcyvel_%03d.dat' % (
578  index, index)
579  print("Running >> %s" % cmd)
580  subprocess.call(cmd, shell=True)
581 
582  #for fl in glob.glob(self.local_dfnFlow_file[:-3]+'-cellinfo-000-rank*.dat'):
583  # os.remove(fl)
584  #for fl in glob.glob(self.local_dfnFlow_file[:-3]+'-darcyvel-000-rank*.dat'):
585  # os.remove(fl)
586 
587  for fl in glob.glob(filename + '-cellinfo-%03d-rank*.dat' % index):
588  os.remove(fl)
589  for fl in glob.glob(filename + '-darcyvel-%03d-rank*.dat' % index):
590  os.remove(fl)
591  try:
592  os.symlink("darcyvel_%03d.dat" % index_finish, "darcyvel.dat")
593  except:
594  print("--> WARNING!!! Unable to create symlink for darcyvel.dat")
595  try:
596  os.symlink("cellinfo_%03d.dat" % index_finish, "cellinfo.dat")
597  except:
598  print("--> WARNING!!! Unable to create symlink for cellinfo.dat")
599 
600 
601 
602 def parse_pflotran_vtk_python(self, grid_vtk_file=''):
603  """ Adds CELL_DATA to POINT_DATA in the VTK output from PFLOTRAN.
604  Parameters
605  ----------
606  self : object
607  DFN Class
608  grid_vtk_file : string
609  Name of vtk file with mesh. Typically local_dfnFlow_file.vtk
610 
611  Returns
612  --------
613  None
614 
615  Notes
616  --------
617  If DFN class does not have a vtk file, inp2vtk_python is called
618  """
619  print('--> Parsing PFLOTRAN output with Python')
620 
621  if self.flow_solver != "PFLOTRAN":
622  error = "ERROR! Wrong flow solver requested\n"
623  sys.stderr.write(error)
624  sys.exit(1)
625 
626  if grid_vtk_file:
627  self.vtk_file = grid_vtk_file
628  else:
629  self.inp2vtk_python()
630 
631  grid_file = self.vtk_file
632 
633  files = glob.glob('*-[0-9][0-9][0-9].vtk')
634  files.sort()
635 
636  with open(grid_file, 'r') as f:
637  grid = f.readlines()[3:]
638 
639  out_dir = 'parsed_vtk'
640 
641  for line in grid:
642  if 'POINTS' in line:
643  num_cells = line.strip(' ').split()[1]
644 
645  for file in files:
646  print(f"--> Processing file: {file}")
647  with open(file, 'r') as f:
648  pflotran_out = f.readlines()[4:]
649  pflotran_out = [
650  w.replace('CELL_DATA', 'POINT_DATA ') for w in pflotran_out
651  ]
652  header = [
653  '# vtk DataFile Version 2.0\n', 'PFLOTRAN output\n', 'ASCII\n'
654  ]
655  filename = out_dir + '/' + file
656  if not os.path.exists(os.path.dirname(filename)):
657  os.makedirs(os.path.dirname(filename))
658  with open(filename, 'w') as f:
659  for line in header:
660  f.write(line)
661  for line in grid:
662  f.write(line)
663  f.write('\n')
664  f.write('\n')
665  if 'vel' in file:
666  f.write('POINT_DATA\t ' + num_cells + '\n')
667  for line in pflotran_out:
668  f.write(line)
669  os.remove(file)
670  print('--> Parsing PFLOTRAN output complete')
def pflotran_cleanup(self, index_start=0, index_finish=1, filename='')
Definition: pflotran.py:541
def write_perms_and_correct_volumes_areas(self)
Definition: pflotran.py:276
def zone2ex(self, uge_file='', zone_file='', face='', boundary_cell_area=1.e-1)
Definition: pflotran.py:100
def parse_pflotran_vtk_python(self, grid_vtk_file='')
Definition: pflotran.py:602
def pflotran(self, transient=False, restart=False, restart_file='')
Definition: pflotran.py:432
def check_pflotran_convergence(pflotran_input_file)
Definition: pflotran.py:501
def lagrit2pflotran(self, inp_file='', mesh_type='', hex2tet=False)
Definition: pflotran.py:14