pydfnWorks
python wrapper for dfnWorks
hydraulic_properties.py
Go to the documentation of this file.
1 import numpy as np
2 import sys
3 
4 
5 def get_units(variable):
6  """
7  Returns a string of appropriate units for different variable
8 
9  Parameters
10  -----------
11  variable : string
12  name of variable. Acceptable values are aperture, permeability, and transmissivity
13  Returns
14  ----------
15  units : string
16  appropriate units for provided variable
17  """
18 
19  if variable == "aperture":
20  units = "m"
21  elif variable == "permeability":
22  units = "m^2"
23  elif variable == "transmissivity":
24  units = "m^2/s"
25  else:
26  error = "ERROR!!! The variable of choice '{0}' is not known in the function get_units()\nAcceptable names are aperture, permeability, and transmissivity\nExiting.".format(
27  variable)
28  sys.stderr.write(error)
29  sys.exit(1)
30  return units
31 
32 
33 def check_key(dict, key):
34  '''
35  Checks if key is in dict
36 
37  Parameters
38  -----------
39  dict : dictionary
40  key : string
41  Returns
42  ----------
43  bool : bool
44  True if key is in dictionary, False if not
45 
46  '''
47 
48  if key in dict.keys():
49  return True
50  else:
51  return False
52 
53 
54 def load_fractures(filename, quiet):
55  '''
56  Loads fracture information from filename.
57 
58  Parameters
59  -----------
60  filename : string
61  name of fracture radii file
62  Returns
63  ----------
64  r : array of doubles
65  maximum radii of fractures
66 
67  family_id : array of ints
68  family id for each fractures
69  n : int
70  number of fractures in the domain
71 
72  '''
73  if not quiet:
74  print("--> Loading Fracture information from {0}".format(filename))
75 
76  data = np.genfromtxt(filename, skip_header=2)
77  family_id = (data[:, 2]).astype(int)
78  n, _ = np.shape(data)
79  r = np.zeros(n)
80  for i in range(n):
81  if data[i, 0] >= data[i, 1]:
82  r[i] = data[i, 0]
83  else:
84  r[i] = data[i, 1]
85  return r, family_id, n
86 
87 
88 def convert(x, source, target):
89  '''
90  converts between variables aperture, permeability, and transmissivity
91 
92  Parameters
93  -----------
94  x : numpy array
95  input values
96  source : string
97  variable name of source
98  target : string
99  variable name of output
100  Returns
101  ----------
102  y : numpy array
103  array of converted values
104 
105  Notes
106  -----
107  permeability/Transmissivty are defined using the cubic law
108 
109  k = b^2/12
110 
111  T = (b^3 rho g)/(12 mu)
112 
113  '''
114 
115  mu = 8.9e-4 #dynamic viscosity of water at 20 degrees C, Pa*s
116  g = 9.8 #gravity acceleration
117  rho = 997 # water density
118 
119  if source == "aperture" and target == "permeability":
120  perm = (x**2) / 12
121  return perm
122  if source == "aperture" and target == "transmissivity":
123  T = (x**3 * rho * g) / (12 * mu)
124  return T
125  if source == "permeability" and target == "aperture":
126  b = np.sqrt((12.0 * x))
127  return b
128 
129  if source == "permeability" and target == "transmissivity":
130  b = np.sqrt((12.0 * x))
131  T = (b * x * rho * g) / (12 * mu)
132  return T
133 
134  if source == "transmissivity" and target == "aperture":
135  b = ((x * 12 * mu) / (rho * g))**(1 / 3)
136  return b
137  if source == "transmissivity" and target == "permeability":
138  b = ((x * 12 * mu) / (rho * g))**(1 / 3)
139  perm = (b**2) / 12
140  return perm
141  else:
142  error = "ERROR!!! Unknown names is convert. Either '{0}' or '{1}' is not known\nAcceptable names are aperture, permeability, and transmissivity\nExiting.\n".format(
143  source, target)
144  sys.stderr.write(error)
145  sys.exit(1)
146 
147 
148 def log_normal(params, variable, number_of_fractures):
149  """ Creates Fracture Based Log-Normal values that is number_of_fractures long.
150  The values has a mean mu and log-variance sigma.
151 
152  Parameters
153  -----------
154  params : dict
155  Dictionary of parameters for the Log Normal values. Must contain keys mu and sigma.
156  variable : string
157  name of values being generated. Acceptable values are aperture, permeability, and transmissivity
158  number_of_fractures : int
159  number of fractures in the DFN
160  Returns
161  ----------
162  b : array
163  aperture values
164  perm : array
165  permeability values
166  T : array
167  transmissivity values
168 
169  Notes
170  ----------
171  values are generated for the variable provided. The two remaining variables are derived using those values
172  """
173  print('--> Creating uncorrelated lognormal {0} values.'.format(variable))
174  units = get_units(variable)
175  print("--> Mean: {0} {1}".format(params["mu"], units))
176  print("--> Log Variance: {0}".format(params["sigma"]))
177 
178  if variable == "aperture":
179  b = np.log(params["mu"]) * np.ones(number_of_fractures)
180  perturbation = np.random.normal(0.0, 1.0, number_of_fractures)
181  b = np.exp(b + np.sqrt(params["sigma"]) * perturbation)
182 
183  perm = convert(b, variable, "permeability")
184  T = convert(b, variable, "transmissivity")
185 
186  elif variable == "permeability":
187  perm = np.log(params["mu"]) * np.ones(number_of_fractures)
188  perturbation = np.random.normal(0.0, 1.0, number_of_fractures)
189  perm = np.exp(perm + np.sqrt(params["sigma"]) * perturbation)
190 
191  b = convert(perm, variable, "aperture")
192  T = convert(perm, variable, "transmissivity")
193 
194  elif variable == "transmissivity":
195  T = np.log(params["mu"]) * np.ones(number_of_fractures)
196  perturbation = np.random.normal(0.0, 1.0, number_of_fractures)
197  T = np.exp(T + np.sqrt(params["sigma"]) * perturbation)
198 
199  b = convert(T, variable, "aperture")
200  perm = convert(T, variable, "permeability")
201 
202  else:
203  error = "ERROR!!! The variable of choice '{0}'' is not known\nAcceptable names are aperture, permeability, and transmissivity\nExiting.\n".format(
204  variable)
205  sys.stderr.write(error)
206  sys.exit(1)
207  print('--> Complete\n')
208  return b, perm, T
209 
210 
211 def correlated(params, variable, radii):
212  """ Creates hydraulic properties of fractures based on power-law relationship with
213  fracture radius. For example, T = alpha*r^beta
214 
215  Parameters
216  -----------
217  params : dict
218  Dictionary of parameters for the power-law relationship. Must contain alpha and beta.
219  variable : string
220  name of values being generated. Acceptable values are aperture, permeability, and transmissivity
221  radii : array
222  array of fracture radii in the domain
223 
224  Returns
225  ----------
226  b : array
227  aperture values
228  perm : array
229  permeability values
230  T : array
231  transmissivity values
232 
233  Notes
234  ----------
235  Values are generated for the variable provided. The two remaining variables are derived using those values
236  """
237  print(
238  '--> Creating Perfectly Correlated {0} values based on fracture radius.'
239  .format(variable))
240  units = get_units(variable)
241  if variable == "aperture":
242  print("b ={1}*r^{2} {3}".format(variable, params["alpha"],
243  params["beta"], units))
244  if variable == "permeability":
245  print("k ={1}*r^{2} {3}".format(variable, params["alpha"],
246  params["beta"], units))
247  if variable == "transmissivity":
248  print("T ={1}*r^{2} {3}".format(variable, params["alpha"],
249  params["beta"], units))
250 
251  if variable == "aperture":
252  b = params["alpha"] * radii**params["beta"]
253 
254  perm = convert(b, variable, "permeability")
255  T = convert(b, variable, "transmissivity")
256 
257  elif variable == "permeability":
258  perm = params["alpha"] * radii**params["beta"]
259 
260  b = convert(perm, variable, "aperture")
261  T = convert(perm, variable, "transmissivity")
262 
263  elif variable == "transmissivity":
264  T = params["alpha"] * radii**params["beta"]
265  b = convert(T, variable, "aperture")
266  perm = convert(T, variable, "permeability")
267 
268  print("--> Complete\n")
269  return b, perm, T
270 
271 
272 def semi_correlated(params, variable, radii, number_of_fractures):
273  """ Creates hydraulic properties of fractures based on power-law relationship with
274  fracture radius with a noise term. For example, log(T) = log(alpha*r^beta) + sigma * N(0,1)
275 
276  Parameters
277  -----------
278  params : dict
279  Dictionary of parameters for the power-law relationship. Must contain alpha and beta.
280  variable : string
281  name of values being generated. Acceptable values are aperture, permeability, and transmissivity
282  radii : array
283  array of fracture radii in the domain
284  number_of_fractures : int
285  number of fractures in the DFN
286 
287  Returns
288  ----------
289  b : array
290  aperture values
291  perm : array
292  permeability values
293  T : array
294  transmissivity values
295 
296  Notes
297  ----------
298  Values are generated for the variable provided. The two remaining variables are derived using those values
299  """
300  print("--> Creating Semi-Correlated {0} values based on fracture radius.".
301  format(variable))
302  print('--> Coefficient: {0}'.format(params["alpha"]))
303  print('--> Exponent : {0}'.format(params["beta"]))
304  print('--> Log Variance: {0}'.format(params["sigma"]))
305 
306  if variable == "aperture":
307  b = params["alpha"] * radii**params["beta"]
308  perturbation = np.random.normal(0.0, 1.0, number_of_fractures)
309  b = np.exp(np.log(b) + np.sqrt(params["sigma"]) * perturbation)
310 
311  perm = convert(b, variable, "permeability")
312  T = convert(b, variable, "transmissivity")
313 
314  elif variable == "permeability":
315 
316  perm = params["alpha"] * radii**params["beta"]
317  perturbation = np.random.normal(0.0, 1.0, number_of_fractures)
318  perm = np.exp(np.log(perm) + np.sqrt(params["sigma"]) * perturbation)
319 
320  b = convert(perm, variable, "aperture")
321  T = convert(perm, variable, "transmissivity")
322 
323  elif variable == "transmissivity":
324 
325  T = params["alpha"] * radii**params["beta"]
326  perturbation = np.random.normal(0.0, 1.0, number_of_fractures)
327  T = np.exp(np.log(T) + np.sqrt(params["sigma"]) * perturbation)
328  b = convert(T, variable, "aperture")
329  perm = convert(T, variable, "permeability")
330 
331  print('--> Complete\n')
332  return b, perm, T
333 
334 
335 def constant(params, variable, number_of_fractures):
336  """ Creates hydraulic properties of fractures with constant values
337 
338  Parameters
339  -----------
340  params : dict
341  Dictionary of parameters for the power-law relationship. Must contain alpha and beta.
342  variable : string
343  name of values being generated. Acceptable values are aperture, permeability, and transmissivity
344  number_of_fractures : int
345  number of fractures in the DFN
346 
347  Returns
348  ----------
349  b : array
350  aperture values
351  perm : array
352  permeability values
353  T : array
354  transmissivity values
355  Returns
356  ----------
357  b : array
358  aperture values
359  perm : array
360  permeability values
361  T : array
362  transmissivity values
363 
364  Notes
365  ----------
366  Values are generated for the variable provided. The two remaining variables are derived using those values
367  """
368 
369  print("--> Creating constant {0} values.".format(variable))
370  units = get_units(variable)
371  print("--> Value: {0} {1}".format(params["mu"], units))
372 
373  if variable == "aperture":
374  b = params["mu"] * np.ones(number_of_fractures)
375  perm = convert(b, variable, "permeability")
376  T = convert(b, variable, "transmissivity")
377 
378  elif variable == "permeability":
379 
380  perm = params["mu"] * np.ones(number_of_fractures)
381  b = convert(perm, variable, "aperture")
382  T = convert(perm, variable, "transmissivity")
383 
384  elif variable == "transmissivity":
385 
386  T = params["mu"] * np.ones(number_of_fractures)
387  b = convert(T, variable, "aperture")
388  perm = convert(T, variable, "permeability")
389 
390  print('--> Complete\n')
391  return b, perm, T
392 
393 
394 def dump_hydraulic_values(self, b, perm, T, prefix=None):
395  """ Writes variable information to files.
396 
397  Parameters
398  -----------
399  prefix : string
400  prefix of aperture.dat and perm.dat file names
401  prefix_aperture.dat and prefix_perm.dat
402  b : array
403  aperture values
404  perm : array
405  permeability values
406  T : array
407  transmissivity values
408  Returns
409  ----------
410  None
411 
412  Notes
413  ----------
414  """
415  print("--> Dumping values to files")
416  n = len(b)
417  # Write out new aperture.dat
418  if prefix is not None:
419  aper_filename = prefix + '_aperture.dat'
420  perm_filename = prefix + '_perm.dat'
421  trans_filename = prefix + '_transmissivity.dat'
422  else:
423  aper_filename = "aperture.dat"
424  perm_filename = "perm.dat"
425  trans_filename = "transmissivity.dat"
426 
427  # write aperture file
428  print("--> Writing {0}".format(aper_filename))
429  with open(aper_filename, 'w+') as fp:
430  fp.write('aperture\n')
431  for i in range(n):
432  fp.write('-{0:d} 0 0 {1:0.5e}\n'.format(i + 7, b[i]))
433 
434  # write perm file
435  print("--> Writing {0}".format(perm_filename))
436  with open(perm_filename, 'w+') as fp:
437  fp.write('permeability\n')
438  for i in range(n):
439  fp.write('-{0:d} 0 0 {1:0.5e} {1:0.5e} {1:0.5e}\n'.format(
440  i + 7, perm[i]))
441 
442  print(f"--> Writing {trans_filename}")
443  with open(trans_filename, 'w+') as fp:
444  fp.write('transmissivty\n')
445  for i in range(n):
446  fp.write('-{0:d} {1:0.5e}\n'.format(i + 7, T[i]))
447  print("Complete")
448 
449 
451  variable,
452  relationship,
453  params,
454  radii_filename="radii_Final.dat",
455  family_id=None):
456  """ Generates hydraulic property values.
457 
458  Parameters
459  -----------
460  self : object
461  DFN Class
462  relationship : string
463  name of functional relationship for apertures.
464  options are log-normal, correlated, semi-correlated, and
465  constant
466  params : dictionary
467  dictionary of parameters for functional relationship
468  family_id : int
469  family id of fractures
470 
471  Returns
472  ----------
473  b : array
474  aperture values
475  perm : array
476  permeability values
477  T : array
478  transmissivity values
479  idx : array of bool
480  true / false of fracture families requested. If family_id = None, all entires are true.
481  Only family members entires of b, perm, and T will be non-zero
482 
483  Notes
484  ----------
485  See Hyman et al. 2016 "Fracture size and transmissivity correlations: Implications for transport simulations in sparse
486  three-dimensional discrete fracture networks following a truncated power law distribution of fracture size" Water Resources Research for more details
487  """
488 
489  # Check if the variable choice is defined
490  variables = ["aperture", "permeability", "transmissivity"]
491  if variable not in variables:
492  error = "ERROR!!! The variable of choice '{0}'' is not known\nAcceptable names are {1}, {2}, {3}\nExiting.\n".format(
493  variable, variables[0], variables[1], variables[2])
494  sys.stderr.write(error)
495  sys.exit(1)
496  # else:
497  # print(
498  # "Creating aperture, permeability, and transmissivity based on {0}."
499  # .format(variable))
500 
501  # check if the function is defined
502  functions = ["log-normal", "semi-correlated", "constant", "correlated"]
503  if relationship not in functions:
504  error = "ERROR!!! The provided relationship '{0}' is unknown\nAcceptable relationship are {1}, {2}, {3}, {4}\nExiting.\n".format(
505  relationship, functions[0], functions[1], functions[2],
506  functions[3])
507  sys.stderr.write(error)
508  sys.exit(1)
509  # else:
510  # print(
511  # "Creating aperture, permeability, and transmissivity using the {0} function."
512  # .format(relationship))
513 
514  # Load Fracture information
515  radii, families, number_of_fractures = load_fractures(radii_filename,
516  quiet=True)
517  if family_id is not None:
518  print("--> Working on Fracture Family {0}".format(family_id))
519 
520  if relationship == "log-normal":
521  keys = ["mu", "sigma"]
522  for key in keys:
523  if not check_key(params, key):
524  error = "ERROR!!! The required key '{0}' was not found in the params dictionary\nExiting\n".format(
525  key)
526  sys.stderr.write(error)
527  sys.exit(1)
528  b, perm, T = log_normal(params, variable, number_of_fractures)
529 
530  if relationship == "correlated":
531  keys = ["alpha", "beta"]
532  for key in keys:
533  if not check_key(params, key):
534  error = "ERROR!!! The required key '{0}' was not found in the params dictionary\nExiting\n".format(
535  key)
536  sys.stderr.write(error)
537  sys.exit(1)
538  b, perm, T = correlated(params, variable, radii)
539 
540  if relationship == "semi-correlated":
541  keys = ["alpha", "beta", "sigma"]
542  for key in keys:
543  if not check_key(params, key):
544  error = "ERROR!!! The required key '{0}' was not found in the params dictionary\nExiting\n\n".format(
545  key)
546  sys.stderr.write(error)
547  sys.exit(1)
548  b, perm, T = semi_correlated(params, variable, radii,
549  number_of_fractures)
550 
551  if relationship == "constant":
552  keys = ["mu"]
553  for key in keys:
554  if not check_key(params, key):
555  error = "ERROR!!! The required key '{0}' was not found in the params dictionary\nExiting\n\n".format(
556  key)
557  sys.stderr.write(error)
558  sys.exit(1)
559  b, perm, T = constant(params, variable, number_of_fractures)
560 
561  if family_id == None:
562  return b, perm, T
563  else:
564  # Sent entrites that are not in the requested family to None
565  idx = np.where(families != family_id)
566  b[idx] = 0
567  T[idx] = 0
568  perm[idx] = 0
569  return b, perm, T
570 
571 
572 # if __name__ == '__main__':
573 
574 # variable = "transmissivity"
575 
576 # function = "correlated"
577 # params = {"alpha":6.7*10**-9,"beta":1.4}
578 # _,_,T1 = generate_hydraulic_values(variable,function,params,radii_filename="/Users/jhyman/Desktop/radii_Final.dat",family_id=1)
579 
580 # function = "semi-correlated"
581 # params = {"alpha":6.3*10**-9,"beta":0.5,"sigma":1.0}
582 # _,_,T2 = generate_hydraulic_values(variable,function,params,radii_filename="/Users/jhyman/Desktop/radii_Final.dat",family_id=2)
583 
584 # #combine values
585 # T = T1 + T2
586 # print(T)
587 
588 # # convert to other variables
589 # perm = convert(T,"transmissivity","permeability")
590 # b = convert(T,"transmissivity","aperture")
591 # # write to file
592 # #dump_values("testing",b,perm,T)
def generate_hydraulic_values(self, variable, relationship, params, radii_filename="radii_Final.dat", family_id=None)
def semi_correlated(params, variable, radii, number_of_fractures)
def dump_hydraulic_values(self, b, perm, T, prefix=None)
def log_normal(params, variable, number_of_fractures)
def constant(params, variable, number_of_fractures)