pydfnWorks
python wrapper for dfnWorks
mesh_dfn_helper.py
Go to the documentation of this file.
1 """
2 .. module:: mesh_dfn_helper.py
3  :synopsis: helper functions for meshing DFN using LaGriT
4 .. moduleauthor:: Jeffrey Hyman <jhyman@lanl.gov>
5 
6 """
7 import os
8 import glob
9 from numpy import genfromtxt, sort, zeros
10 import subprocess
11 
12 
13 def parse_params_file(quiet=False):
14  """ Reads params.txt file from DFNGen and parses information
15 
16  Parameters
17  ---------
18  quiet : bool
19  If True details are not printed to screen, if False they area
20 
21  Returns
22  -------
23  num_poly: int
24  Number of Polygons
25  h: float
26  Meshing length scale h
27  dudded_points: int
28  Expected number of dudded points in Filter (LaGriT)
29  visual_mode : bool
30  If True, reduced_mesh.inp is created (not suitable for flow and transport), if False, full_mesh.inp is created
31  domain: dict
32  x,y,z domain sizes
33 
34  Notes
35  -----
36  None
37  """
38  if not quiet:
39  print("\n--> Parsing params.txt")
40  fparams = open('params.txt', 'r')
41  # Line 1 is the number of polygons
42  num_poly = int(fparams.readline())
43  #Line 2 is the h scale
44  h = float(fparams.readline())
45  # Line 3 is the visualization mode: '1' is True, '0' is False.
46  visual_mode = int(fparams.readline())
47  # line 4 dudded points
48  dudded_points = int(fparams.readline())
49 
50  # Dict domain contains the length of the domain in x,y, and z
51  domain = {'x': 0, 'y': 0, 'z': 0}
52  #Line 5 is the x domain length
53  domain['x'] = (float(fparams.readline()))
54 
55  #Line 5 is the x domain length
56  domain['y'] = (float(fparams.readline()))
57 
58  #Line 5 is the x domain length
59  domain['z'] = (float(fparams.readline()))
60  fparams.close()
61 
62  if not quiet:
63  print("--> Number of Polygons: %d" % num_poly)
64  print("--> H_SCALE %f" % h)
65  if visual_mode > 0:
66  visual_mode = True
67  print("--> Visual mode is on")
68  else:
69  visual_mode = False
70  print("--> Visual mode is off")
71  print(f"--> Expected Number of dudded points: {dudded_points}")
72  print(f"--> X Domain Size {domain['x']} m")
73  print(f"--> Y Domain Size {domain['y']} m")
74  print(f"--> Z Domain Size {domain['z']} m")
75  print("--> Parsing params.txt complete\n")
76 
77  return (num_poly, h, visual_mode, dudded_points, domain)
78 
79 
80 def check_dudded_points(dudded, hard=False):
81  """Parses LaGrit log_merge_all.out and checks if number of dudded points is the expected number
82 
83  Parameters
84  ---------
85  dudded : int
86  Expected number of dudded points from params.txt
87  hard : bool
88  If hard is false, up to 1% of nodes in the mesh can be missed. If hard is True, no points can be missed.
89  Returns
90  ---------
91  True/False : bool
92  True if the number of dudded points is correct and False if the number of dudded points is incorrect
93 
94  Notes
95  -----
96  If number of dudded points is incorrect by over 1%, program will exit.
97 
98  """
99  print("--> Checking that number of dudded points is correct\n")
100  with open("lagrit_logs/log_merge_all.out", "r") as fp:
101  for line in fp.readlines():
102  if 'Dudding' in line:
103  print(f'--> From LaGriT: {line}')
104  try:
105  pts = int(line.split()[1])
106  except:
107  pts = int(line.split()[-1])
108  if 'RMPOINT:' in line:
109  print(f'--> From LaGriT: {line}')
110  total_points = int(line.split()[-1])
111  break
112 
113  diff = abs(dudded - pts)
114  print(f"--> Expected Number of dudded points: {dudded}")
115  print(f"--> Actual Number of dudded points: {pts}")
116  print(f"--> Difference between expected and actual dudded points: {diff}")
117  if diff == 0:
118  print('--> The correct number of points were removed. Onward!\n')
119  return True
120  elif diff > 0:
121 
122  print(
123  '--> WARNING!!! Number of points removed does not match the expected value'
124  )
125  diff_ratio = 100 * (float(diff) / float(total_points))
126  if diff_ratio < 0.01 and hard == False:
127  print(f"--> However value is small: {diff}")
128  print("--> Proceeding\n")
129  return True
130  else:
131  print('ERROR! Incorrect Number of points removed')
132  print(f"Over 0.01% of nodes removed. Value is {diff_ratio:.2f}")
133  return False
134 
135 
137  """ Removes meshing files
138 
139  Parameters
140  ----------
141  None
142 
143  Returns
144  -------
145  None
146 
147  Notes
148  -----
149  Only runs if production_mode is True
150  """
151 
152  files_to_remove = [
153  'part*', 'log_merge*', 'merge*', 'mesh_poly_CPU*', 'mesh*inp',
154  'mesh*lg'
155  ]
156  for name in files_to_remove:
157  for fl in glob.glob(name):
158  os.remove(fl)
159 
160 
161 def output_meshing_report(local_jobname, visual_mode):
162  """ Prints information about the final mesh to file
163 
164  Parameters
165  ----------
166  local_jobname : string
167  Name of current DFN job (not path)
168  visual_mode : bool
169  Determines is reduced_mesh or full_mesh is dumped
170 
171  Returns
172  -------
173  None
174 
175  Notes
176  -----
177  None
178 """
179 
180  f = open(local_jobname + '_mesh_information.txt', 'w')
181  f.write('The final mesh of DFN consists of: \n')
182  if not visual_mode:
183  print(
184  "--> Output files for flow calculations are written in : full_mesh.*"
185  )
186 
187  finp = open('full_mesh.inp', 'r')
188  g = finp.readline()
189  g = g.split()
190  NumElems = int(g.pop(1))
191  NumIntNodes = int(g.pop(0))
192  f.write(str(NumElems) + ' triangular elements; \n')
193  f.write(str(NumIntNodes) + ' nodes / control volume cells; \n')
194  finp.close()
195 
196  fstor = open('full_mesh.stor', 'r')
197  fstor.readline()
198  fstor.readline()
199  gs = fstor.readline()
200  gs = gs.split()
201  NumCoeff = int(gs.pop(0))
202  f.write(
203  str(NumCoeff) +
204  ' geometrical coefficients / control volume faces. \n')
205  fstor.close()
206  else:
207  print(
208  "--> Output files for visualization are written in : reduced_mesh.inp"
209  )
210  print("--> Warning!!! Mesh is not suitable for flow and transport.")
211 
212  finp = open('reduced_mesh.inp', 'r')
213  g = finp.readline()
214  g = g.split()
215  NumElems = int(g.pop(1))
216  NumIntNodes = int(g.pop(0))
217  f.write(str(NumElems) + ' triangular elements; \n')
218  f.write(str(NumIntNodes) + ' nodes / control volume cells. \n')
219  finp.close()
220  f.close()
221 
222 
224  ''' After pruning a DFN to only include the fractures in prune_file this function removes references to those fractures from params.txt, perm.dat, aperature.dat, and poly_info.dat
225 
226  Parameters
227  ----------
228  self : DFN object
229 
230  Returns
231  -------
232  None
233 
234  Notes
235  -----
236  This function should always be run after pruning if flow solution is going to be run
237 
238  '''
239 
240  print("--> Editing DFN file based on fractures in %s" % self.prune_file)
241  keep_list = sort(genfromtxt(self.prune_file).astype(int))
242  num_frac = len(keep_list)
243 
244  print("--> Editing params.txt file")
245  fin = open(self.path + '/params.txt')
246  try:
247  os.unlink('params.txt')
248  except:
249  pass
250  fout = open('params.txt', 'w')
251  line = fin.readline()
252  fout.write('%d\n' % num_frac)
253  for i in range(7):
254  line = fin.readline()
255  fout.write(line)
256  fin.close()
257  fout.close()
258  print("--> Complete")
259 
260  print("--> Editing poly_info.dat file")
261  poly_info = genfromtxt(self.path + 'poly_info.dat')[keep_list - 1, :]
262  try:
263  os.unlink('poly_info.dat')
264  except:
265  pass
266  f = open('poly_info.dat', 'w')
267  for i in range(num_frac):
268  f.write('%d %d %f %f %f %d %f %f %d\n' %
269  (i + 1, poly_info[i, 1], poly_info[i, 2], poly_info[i, 3],
270  poly_info[i, 4], poly_info[i, 5], poly_info[i, 6],
271  poly_info[i, 7], poly_info[i, 8]))
272  f.close()
273  print("--> Complete")
274 
275  print("--> Editing perm.dat file")
276  perm = genfromtxt(self.path + 'perm.dat', skip_header=1)[keep_list - 1, -1]
277  f = open('perm.dat', 'w+')
278  f.write('permeability\n')
279  for i in range(num_frac):
280  f.write('-%d 0 0 %e %e %e\n' % (7 + i, perm[i], perm[i], perm[i]))
281  f.close()
282  print("--> Complete")
283 
284  print("--> Editing aperture.dat file")
285  aperture = genfromtxt(self.path + 'aperture.dat',
286  skip_header=1)[keep_list - 1, -1]
287  f = open('aperture.dat', 'w+')
288  f.write('aperture\n')
289  for i in range(num_frac):
290  f.write('-%d 0 0 %e \n' % (7 + i, aperture[i]))
291  f.close()
292  print("--> Complete")
293 
294  print("--> Editing radii_Final.dat file")
295  fin = open(self.path + 'radii_Final.dat')
296  fout = open('radii_Final.dat', 'w')
297  # copy header
298  line = fin.readline()
299  fout.write(line)
300  line = fin.readline()
301  fout.write(line)
302  fin.close()
303  # write radii from remaining fractures
304  radii = genfromtxt(self.path + 'radii_Final.dat',
305  skip_header=2)[keep_list - 1, :]
306  for i in range(num_frac):
307  fout.write('%f %f %d\n' % (radii[i, 0], radii[i, 1], radii[i, 2]))
308  fout.close()
309  print("--> Complete")
310 
311  print("--> Editing normal_vectors.dat file")
312  fin = open(self.path + 'normal_vectors.dat')
313  fout = open('normal_vectors.dat', 'w')
314  # copy header
315  normal_vect = genfromtxt(self.path + 'normal_vectors.dat')[keep_list -
316  1, :]
317  for i in range(num_frac):
318  fout.write('%f %f %f\n' %
319  (normal_vect[i, 0], normal_vect[i, 1], normal_vect[i, 2]))
320  fout.close()
321  print("--> Complete")
322 
323  print("--> Editing translations.dat file")
324  fin = open(self.path + 'translations.dat')
325  fout = open('translations.dat', 'w')
326  # copy header
327  line = fin.readline()
328  fout.write(line)
329  points = []
330  for line in fin.readlines():
331  tmp = line.split(' ')
332  if tmp[-1] != 'R':
333  points.append((float(tmp[0]), float(tmp[1]), float(tmp[2])))
334  from numpy import asarray
335  points = asarray(points)
336  points = points[keep_list - 1, :]
337  for i in range(num_frac):
338  fout.write('%f %f %f\n' % (points[i, 0], points[i, 1], points[i, 2]))
339  fout.close()
340  print("--> Complete")
341 
342  print("--> Editing Fracture Files Complete")
343 
344 
345 def create_mesh_links(self,path):
346  ''' Makes symlinks for files in path required for meshing
347 
348  Parameters
349  ----------
350  self : DFN object
351  path : string
352  Path to where meshing files are located
353 
354  Returns
355  -------
356  None
357 
358  Notes
359  -----
360  None
361 
362  '''
363  import os.path
364  from shutil import rmtree
365  print("--> Creating links for meshing from %s" % path)
366  files = [
367  'params.txt', 'poly_info.dat', 'connectivity.dat', 'left.dat',
368  'right.dat', 'front.dat', 'back.dat', 'top.dat', 'bottom.dat', 'polys',
369  'intersections', 'aperture.dat', 'perm.dat'
370  ]
371  for f in files:
372  if os.path.isfile(f) or os.path.isdir(f):
373  print("Removing %s" % f)
374  try:
375  rmtree(f)
376  except:
377  print("Unable to remove %s" % f)
378  try:
379  os.symlink(path + f, f)
380  except:
381  print("Unable to make link for %s" % f)
382  pass
383  print("--> Complete")
384 
385 
386 def inp2gmv(self, inp_file=''):
387  """ Convert inp file to gmv file, for general mesh viewer. Name of output file for base.inp is base.gmv
388 
389  Parameters
390  ----------
391  self : object
392  DFN Class
393  inp_file : str
394  Name of inp file if not an attribure of self
395 
396  Returns
397  ----------
398  None
399 
400  Notes
401  ---------
402  """
403 
404  if inp_file:
405  self.inp_file = inp_file
406  else:
407  inp_file = self.inp_file
408 
409  if inp_file == '':
410  error = 'ERROR: inp file must be specified in inp2gmv!\n'
411  sys.stderr.write(error)
412  sys.exit(1)
413 
414  gmv_file = inp_file[:-4] + '.gmv'
415 
416  with open('inp2gmv.lgi', 'w') as fid:
417  fid.write(f'read / avs / {inp_file} / mo\n')
418  fid.write(f'dump / gmv / {gmv_file} / mo\n')
419  fid.write('finish \n\n')
420 
421  failure = run_lagrit_script('inp2gmv.lgi')
422 
423  if failure:
424  error = 'ERROR: Failed to run LaGrit to get gmv from inp file!\n'
425  sys.stderr.write(error)
426  sys.exit(1)
427  print("--> Finished writing gmv format from avs format")
428 
429 
430 def inp2vtk_python(self):
431  """ Using Python VTK library, convert inp file to VTK file.
432 
433  Parameters
434  ----------
435  self : object
436  DFN Class
437 
438  Returns
439  --------
440  None
441 
442  Notes
443  --------
444  For a mesh base.inp, this dumps a VTK file named base.vtk
445  """
446  import pyvtk as pv
447  if self.flow_solver != "PFLOTRAN":
448  error = "ERROR! Wrong flow solver requested\n"
449  sys.stderr.write(error)
450  sys.exit(1)
451 
452  print("--> Using Python to convert inp files to VTK files")
453  if self.inp_file:
454  inp_file = self.inp_file
455 
456  if inp_file == '':
457  error = 'ERROR: Please provide inp filename!\n'
458  sys.stderr.write(error)
459  sys.exit(1)
460 
461  if self.vtk_file:
462  vtk_file = self.vtk_file
463  else:
464  vtk_file = inp_file[:-4]
465  self.vtk_file = vtk_file + '.vtk'
466 
467  print("--> Reading inp data")
468 
469  with open(inp_file, 'r') as f:
470  line = f.readline()
471  num_nodes = int(line.strip(' ').split()[0])
472  num_elems = int(line.strip(' ').split()[1])
473 
474  coord = zeros((num_nodes, 3), 'float')
475  elem_list_tri = []
476  elem_list_tetra = []
477 
478  for i in range(num_nodes):
479  line = f.readline()
480  coord[i, 0] = float(line.strip(' ').split()[1])
481  coord[i, 1] = float(line.strip(' ').split()[2])
482  coord[i, 2] = float(line.strip(' ').split()[3])
483 
484  for i in range(num_elems):
485  line = f.readline().strip(' ').split()
486  line.pop(0)
487  line.pop(0)
488  elem_type = line.pop(0)
489  if elem_type == 'tri':
490  elem_list_tri.append([int(i) - 1 for i in line])
491  if elem_type == 'tet':
492  elem_list_tetra.append([int(i) - 1 for i in line])
493 
494  print('--> Writing inp data to vtk format')
495  vtk = pv.VtkData(
496  pv.UnstructuredGrid(coord,
497  tetra=elem_list_tetra,
498  triangle=elem_list_tri),
499  'Unstructured pflotran grid')
500 
501  vtk.tofile(vtk_file)
502 
503 
504 def run_lagrit_script(lagrit_file, output_file=None, quiet=False):
505  """
506  Runs LaGriT
507 
508  Parameters
509  -----------
510  ----------
511  lagrit_file : string
512  Name of LaGriT script to run
513  output_file : string
514  Name of file to dump LaGriT output
515  quiet : bool
516  If false, information will be printed to screen.
517 
518  Returns
519  ----------
520  failure: int
521  If the run was successful, then 0 is returned.
522 
523  """
524  if output_file == None:
525  cmd = f"{os.environ['LAGRIT_EXE']} < {lagrit_file}"
526  else:
527  cmd = f"{os.environ['LAGRIT_EXE']} < {lagrit_file} > {output_file}"
528  if not quiet:
529  print(f"--> Running: {cmd}")
530 
531  failure = subprocess.call(cmd, shell=True)
532  if failure:
533  error = f"ERROR running LaGriT on script {lagrit_file}. Exiting Program.\n"
534  return failure
def run_lagrit_script(lagrit_file, output_file=None, quiet=False)
def output_meshing_report(local_jobname, visual_mode)
def check_dudded_points(dudded, hard=False)