13 """ Functions to help parse the input file and check input parameters.
16 * params (list): list of parameters specified in the input file.
17 * minFracSize (float): the minimum fracture size.
28 """ '{1,2,3}' --> [1,2,3]
30 return re.sub(
"{|}",
"", curlyList).strip().split(
",")
33 """ [1,2,3] --> '{1,2,3}' for writing output
35 curl = re.sub(
r'\[',
'{', strList)
36 curl = re.sub(
r'\]',
'}', curl)
37 curl = re.sub(
r"\'",
'', curl)
41 """ Checks to see that every { has a matching }.
43 if '{' in line
and '}' in line:
return True
44 elif '{' in line
or '}' in line:
46 "Line defining \"{}\" contains a single curly brace.".format(
51 """ Use to get key's value in params. writing always false
53 if (
not writing)
and (len(self.
paramsparams[key]) > 1):
55 "\"{}\" can only correspond to 1 list. {} lists have been defined."
56 .format(key, len(self.
paramsparams[key])))
58 val = self.
paramsparams[key][0]
59 if val ==
'' or val == []:
60 self.
errorerror(
"\"{}\" does not have a value.".format(key))
63 self.
errorerror(
"\"{}\" has not been defined.".format(
67 """ extract values between { and }
69 curlyGroup = re.compile(
'({.*?})')
70 groups = re.findall(curlyGroup, line)
72 line = line.replace(group,
'', 1)
75 if line.strip() !=
"":
77 "Unexpected character found while parsing \"{}\".".format(key))
80 """ pulls values from culry brackets
91 errString (str): a string describing the error
93 error =
"\nERROR --- " + errString +
"\n----Program terminated while parsing input----\n"
94 sys.stderr.write(error)
101 warnStinrg (str): a string with the warning
103 print(
"WARNING --- " + warnString)
105 def warning(self, warnString, warningFile=''):
106 """ print a warning to a file (currently does not work)"""
108 print(
"WARNING --- " + warnString)
112 """"returns True if num is negative, false otherwise
114 return True if num < 0
else False
117 """Makes sure at least one polygon family has been defined in nFamRect or nFamEll
118 OR that there is a user input file for polygons.
120 userDefExists = (self.
value_ofvalue_of(
'userEllipsesOnOff') ==
'1') |\
121 (self.
value_ofvalue_of(
'userRectanglesOnOff') ==
'1') |\
122 (self.
value_ofvalue_of(
'userRecByCoord') ==
'1') |\
123 (self.
value_ofvalue_of(
'userEllByCoord') ==
'1')
125 ellipseFams = len(self.
value_ofvalue_of(
'nFamRect'))
126 rectFams = len(self.
value_ofvalue_of(
'nFamEll'))
128 if ellipseFams + rectFams <= 0
and not userDefExists:
129 self.
errorerror(
"Zero polygon families have been defined. Please create at least one family "\
130 "of ellipses/rectagnles, or provide a user-defined-polygon input file path in "\
131 "\"UserEll_Input_File_Path\", \"UserRect_Input_File_Path\", \"UserEll_Input_File_Path\", or "\
132 "\"RectByCoord_Input_File_Path\" and set the corresponding flag to '1'.")
134 def scale(self, probList, warningFile):
135 """ scales list of probabilities (famProb) that doesn't add up to 1
136 ie [.2, .2, .4] --> [0.25, 0.25, 0.5]
138 total = sum(probList)
139 scaled = [float(
"{:.6}".format(x / total))
for x
in probList]
140 self.
warningwarningwarning(
"'famProb' probabilities did not add to 1 and have been scaled accordingly "\
141 "for their current sum, {:.6}. Scaled {} to {}".format(total, probList, scaled), warningFile)
142 return [x / total
for x
in probList]
145 """ returns True is there is a zero in valList of standard deviations
148 if float(val) == 0:
return True
151 """ Checks that the minimum parameter for a family is not greater or equal to the maximum parameter.
153 for minV, maxV
in zip(self.
value_ofvalue_of(minParam),
156 self.
errorerror(
"\"{}\" and \"{}\" contain equal values for the same {} family. "\
157 "If {} and {} were intended to be the same, use the constant distribution "\
158 "(4) instead.".format(minParam, maxParam, shape, minParam, maxParam))
161 "\"{}\" is greater than \"{}\" in a(n) {} family.".format(
162 minParam, maxParam, shape))
165 def check_mean(self, minParam, maxParam, meanParam, warningFile=''):
166 """ Warns the user if the minimum value of a parameter is greater than the family's mean value, or if the
167 maximum value of the parameter is less than the family's mean value.
169 for minV, meanV
in zip(self.
value_ofvalue_of(minParam),
172 self.
warningwarningwarning(
"\"{}\" contains a min value greater than its family's mean value in "\
173 "\"{}\". This could drastically increase computation time due to increased "\
174 "rejection rate of the most common fracture sizes.".format(minParam, meanParam), warningFile)
175 for maxV, meanV
in zip(self.
value_ofvalue_of(maxParam),
178 self.
warningwarningwarning(
"\"{}\" contains a max value less than its family's mean value in "\
179 "\"{}\". This could drastically increase computation time due to increased "\
180 "rejection rate of the most common fracture sizes.".format(maxParam, meanParam), warningFile)
183 """ Corrects the minimum fracture size if necessary, by looking at the values in valList.
187 if minFracSize ==
None:
189 elif val < minFracSize:
196 """Returns line without comments or white space.
200 line = line[:line.index(
202 while "*/" not in comment:
207 line = line[:line.index(
212 def find_val(self, line, key, inputIterator, unfoundKeys, warningFile):
213 """ Extract the value for key from line.
216 line = line[line.index(
":") + 1:].strip()
217 if line !=
"": self.
val_helperval_helper(line, valList, key)
220 while ':' not in line:
227 except StopIteration:
230 if valList == []
and key
in mandatory:
232 "\"{}\" is a mandatory parameter and must be defined.".format(
235 self.
paramsparams[key] = valList
if valList != []
else [
239 self.
process_lineprocess_line(line, unfoundKeys, inputIterator, warningFile)
241 def find_key(self, line, unfoundKeys, warningFile):
242 """ Input: line containing a parameter (key) preceding a ":"
245 * key -- if it has not been defined yet and is valid
246 * None -- if key does not exist
247 * exits -- if the key has already been defined to prevent duplicate confusion
249 key = line[:line.index(
":")].strip()
250 if key
in unfoundKeys:
251 unfoundKeys.remove(key)
255 self.
errorerror(
"\"{}\" has been defined more than once.".format(key))
258 "\"" + key +
"\" is not one of the valid parameter names.",
262 """ Find the key in a line, and the value for that key.
265 key = self.
find_keyfind_key(line, unfoundKeys, warningFile)
267 self.
find_valfind_val(line, key, inputIterator, unfoundKeys,
280 """ Verify that value is either a 0 or a 1.
282 if value
is '0' or value
is '1':
287 self.
errorerror(
"\"{}\" must be either '0' or '1'".format(key))
290 """ Verify that value is a positive float.
292 if type(value)
is list:
294 "\"{}\" contains curly braces {{}} but should not be a list value."
297 if noNeg
and float(value) < 0:
298 self.
errorerror(
"\"{}\" cannot be a negative number.".format(key))
301 if inList:
return None
303 self.
errorerror(
"\"{}\" contains an unexpected character. Must be a single "\
304 "floating point value (0.5, 1.6, 4.0, etc.)".format(key))
306 def verify_int(self, value, key="", inList=False, noNeg=False):
307 """ Verify that value is a positive integer.
309 if type(value)
is list:
311 "\"{}\" contains curly braces {{}} but should not be a list value."
314 if noNeg
and int(re.sub(
r'\.0*$',
'', value)) < 0:
315 self.
errorerror(
"\"{}\" cannot be a negative number.".format(key))
316 return int(re.sub(
r'\.0*$',
'',
319 if inList:
return None
321 self.
errorerror(
"\"{}\" contains an unexpected character. Must be a single "\
322 "integer value (0,1,2,3,etc.)".format(key))
331 """verifies input list that come in format {0, 1, 2, 3}
334 * valList - list of values (flags, floats, or ints) corresponding to a parameter
335 * key - the name of the parameter whose list is being verified
336 * verificationFn - (either verifyflag, verifyfloat or verifyint) checks each list element
337 * desiredLength - how many elements are supposed to be in the list
338 * noZeros - (optional) True for lists than cannot contain 0's, false if 0's are ok
339 * noNegs - (optional) True for lists than cannot contain negative numbers, false otherwise
341 * returns negative value of list length to indicate incorrect length and provide meaningful error message
342 * prints error and exits if a value of the wrong type is found in the list
343 * returns None if successful"""
345 if valList == [
'']:
return 0
346 if type(valList)
is not list:
348 "\"{}\"'s value must be a list enclosed in curly brackets {{}}."
350 if desiredLength != 0
and int(len(valList)) != int(desiredLength):
351 print(
'list desired length is ', desiredLength,
'but valList is ',
352 valList,
'with length ', len(valList))
354 for i, value
in enumerate(valList):
355 value = value.strip()
356 verifiedVal = verificationFn(value, inList=
True)
357 if verifiedVal ==
None:
362 verificationFn.__name__))
363 self.
errorerror(
"\"{}\" must be a list of {}s {}. non-{} found in "\
364 "list".format(key, listType, examples[listType], listType))
365 if noZeros
and verifiedVal == 0:
366 self.
errorerror(
"\"{}\" list cannot contain any zeros.".format(key))
367 if noNegs
and self.
is_negativeis_negative(float(verifiedVal)):
369 "\"{}\" list cannot contain negative values.".format(key))
370 valList[i] = verifiedVal
379 """Check input file for DFNGen to make sure all necessary parameters are defined
381 Input Format Requirements:
382 * Each parameter must be defined on its own line (separate by newline)
383 * A parameter (key) MUST be separated from its value by a colon ':' (ie. --> key: value)
384 * Values may also be placed on lines after the 'key'
385 * Comment Format: On a line containing // or / ``*``, nothing after ``*`` / or // will be processed but text before a comment will be processed
390 name of dfnGen input file
392 Name of stripped down input file for DFNGen (input_file_clean.dat)
400 There are warnings and errors raised in this function. Warning will let you continue while errors will stop the run. Continue past warnings are your own risk.
409 'insertUserRectanglesFirst': [],
410 'keepOnlyLargestCluster': [],
411 'keepIsolatedFractures': [],
415 'userRectanglesOnOff': [],
416 'printRejectReasons': [],
419 'RectByCoord_Input_File_Path': [],
422 'lengthCorrelatedAperture': [],
423 'ebetaDistribution': [],
424 'tripleIntersections': [],
429 'constantPermeability': [],
436 'outputAllRadii': [],
439 'userRecByCoord': [],
440 'RectByCoord_Input_File_Path': [],
441 'userRectanglesOnOff': [],
442 'UserRect_Input_File_Path': [],
443 'userEllByCoord': [],
444 'EllByCoord_Input_File_Path': [],
445 'userEllipsesOnOff': [],
446 'UserEll_Input_File_Path': [],
447 'userPolygonByCoord': [],
448 'PolygonByCoord_Input_File_Path': [],
450 'rbetaDistribution': [],
457 'domainSizeIncrease': [],
459 'outputFinalRadiiPerFamily': [],
469 'constantAperture': [],
479 'ignoreBoundaryFaces': [],
480 'visualizationMode': [],
481 'outputAcceptedRadiiPerFamily': [],
482 'apertureFromTransmissivity': [],
496 'rejectsPerFracture': [],
499 'forceLargeFractures': [],
500 'radiiListIncrease': [],
501 'removeFracturesLessThan': []
506 'stopCondition',
'nPoly',
'outputAllRadii',
'outputAllRadii',
507 'outputFinalRadiiPerFamily',
'outputAcceptedRadiiPerFamily',
508 'domainSize',
'numOfLayers',
'layers',
'numOfRegions',
'regions',
'h',
509 'tripleIntersections',
'printRejectReasons',
'disableFram',
510 'visualizationMode',
'seed',
'domainSizeIncrease',
511 'keepOnlyLargestCluster',
'keepIsolatedFractures',
512 'ignoreBoundaryFaces',
'boundaryFaces',
'rejectsPerFracture',
513 'famProb',
'insertUserRectanglesFirst',
'nFamEll',
'eLayer',
'eRegion',
514 'edistr',
'ebetaDistribution',
'e_p32Targets',
'easpect',
'enumPoints',
515 'eAngleOption',
'etheta',
'ephi',
'ebeta',
'ekappa',
'eLogMean',
'esd',
516 'eLogMin',
'eLogMax',
'eExpMean',
'eExpMin',
'eExpMax',
'econst',
517 'emin',
'emax',
'ealpha',
'nFamRect',
'rLayer',
'rRegion',
'rdistr',
518 'rbetaDistribution',
'r_p32Targets',
'raspect',
'rAngleOption',
519 'rtheta',
'rphi',
'rbeta',
'rkappa',
'rLogMean',
'rsd',
'rLogMin',
520 'rLogMax',
'rmin',
'rmax',
'ralpha',
'rExpMean',
'rExpMin',
'rExpMax',
521 'rconst',
'userEllipsesOnOff',
'UserEll_Input_File_Path',
522 'userRectanglesOnOff',
'UserRect_Input_File_Path',
523 'EllByCoord_Input_File_Path',
'userEllByCoord',
'userRecByCoord',
524 'userPolygonByCoord',
'RectByCoord_Input_File_Path',
525 'PolygonByCoord_Input_File_Path',
'aperture',
'meanAperture',
526 'stdAperture',
'apertureFromTransmissivity',
'constantAperture',
527 'lengthCorrelatedAperture',
'permOption',
'constantPermeability',
528 'forceLargeFractures',
'radiiListIncrease',
'removeFracturesLessThan'
533 'stopCondition',
'domainSize',
'numOfLayers',
'numOfRegions',
534 'outputAllRadii',
'outputFinalRadiiPerFamily',
535 'outputAcceptedRadiiPerFamily',
'tripleIntersections',
536 'printRejectReasons',
'disableFram',
'visualizationMode',
'seed',
537 'domainSizeIncrease',
'keepOnlyLargestCluster',
538 'keepIsolatedFractures',
'ignoreBoundaryFaces',
'rejectsPerFracture',
539 'famProb',
'insertUserRectanglesFirst',
'nFamEll',
'nFamRect',
540 'userEllipsesOnOff',
'userRectanglesOnOff',
'userEllByCoord',
541 'userRecByCoord',
'userPolygonByCoord',
'aperture',
'permOption',
542 'forceLargeFractures',
'radiiListIncrease',
'removeFracturesLessThan'
545 global noDependancyFlags
546 noDependancyFlags = [
547 'outputAllRadii',
'outputFinalRadiiPerFamily',
548 'outputAcceptedRadiiPerFamily',
'tripleIntersections',
549 'printRejectReasons',
'visualizationMode',
'keepOnlyLargestCluster',
550 'keepIsolatedFractures',
'insertUserRectanglesFirst',
551 'forceLargeFractures'
556 "Float":
"(0.5, 1.6, 4.0, etc.)",
557 "Int":
"(0,1,2,3,etc.)"
581 warningFile = open(
"warningFileDFNGen.txt",
'w')
583 jobname = self.jobname
585 input_helper_methods =
input_helper(params, minFracSize)
593 """ Check the number of families of ellipses."""
596 ellipseFams = input_helper_methods.verify_int(
597 input_helper_methods.value_of(
'nFamEll', params),
601 input_helper_methods.warning(
602 "You have set the number of ellipse families to 0, outside user-defined ellipses, no ellipses will be generated.",
606 """ Check the number of families of rectangles."""
609 rectFams = input_helper_methods.verify_int(
610 input_helper_methods.value_of(
'nFamRect', params),
614 input_helper_methods.warning(
615 "You have set the number of rectangle families to 0, outside user-defined rectangles, no rectangles will be generated.",
618 def stop_condition():
619 """ Check the number of polygons if stopCondition is set to 1, else check the p32 target parameters."""
621 if input_helper_methods.verify_flag(
622 input_helper_methods.value_of(
'stopCondition', params),
623 'stopCondition') == 0:
628 def check_no_dep_flags():
629 """ Check for dependency flags."""
630 for flagName
in noDependancyFlags:
631 input_helper_methods.verify_flag(
632 input_helper_methods.value_of(flagName, params), flagName)
635 """ Check that domainSize has 3 non-zero values to define the
636 size of each dimension (x,y,z) of the domain.
638 errResult = input_helper_methods.verify_list(
639 input_helper_methods.value_of(
'domainSize', params),
641 input_helper_methods.verify_float,
645 if errResult !=
None:
646 input_helper_methods.error(
"\"domainSize\" has defined {} value(s) but there must be 3 non-zero "\
647 "values to represent x, y, and z dimensions".format(-errResult))
649 def domain_size_increase():
650 """ Check the domain size increase parameters.
652 errResult = input_helper_methods.verify_list(
653 input_helper_methods.value_of(
'domainSizeIncrease', params),
654 domain_size_increase,
655 input_helper_methods.verify_float,
657 if errResult !=
None:
658 input_helper_methods.error(
"\"domainSizeIncrease\" has defined {} value(s) but there must be 3 non-zero "\
659 "values to represent extensions in the x, y, and z dimensions".format(-errResult))
661 for i, val
in enumerate(
662 input_helper_methods.value_of(
'domainSizeIncrease', params)):
663 if val >= input_helper_methods.value_of(
'domainSize',
665 input_helper_methods.error(
666 "\"domainSizeIncrease\" contains {} which is more than half of the domain's "
667 "range in that dimension. Cannot change the domain's size by more than half of "
668 "that dimension's value defined in \"domainSize\". This risks collapsing or "
669 "doubling the domain.".format(val))
672 """ Check the number of layers parameter."""
674 numLayers = input_helper_methods.verify_int(
675 input_helper_methods.value_of(
'numOfLayers', params),
679 if numLayers != len(params[
'layers']):
680 input_helper_methods.error(
"\"layers\" has defined {} layers but \"numLayers\" was defined to "\
681 "be {}.".format(len(params[
'layers']), numLayers))
686 """ Check the layer parameters provided. """
687 halfZdomain = params[
'domainSize'][0][
691 for i, layer
in enumerate(params[
'layers']):
692 errResult = input_helper_methods.verify_list(
694 "layer #{}".format(i + 1),
695 input_helper_methods.verify_float,
697 if errResult !=
None:
698 input_helper_methods.error(
"\"layers\" has defined layer #{} to have {} element(s) but each layer must "\
699 "have 2 elements, which define its upper and lower bounds".format(i+1, -errResult))
700 if params[
'layers'].count(layer) > 1:
701 input_helper_methods.error(
702 "\"layers\" has defined the same layer more than once.")
706 input_helper_methods.error(
"\"layers\" has defined layer #{0} where zmin: {1} is "\
707 "greater than zmax {2}".format(i+1,minZ, maxZ))
708 if minZ <= -halfZdomain
and maxZ <= -halfZdomain:
709 input_helper_methods.error(
"\"layers\" has defined layer #{} to have both upper and lower bounds completely "\
710 "below the domain's z-dimensional range ({} to {}). At least one boundary must be within "\
711 "the domain's range. The domain's range is half of 3rd value in \"domainSize\" "\
712 "(z-dimension) in both positive and negative directions.".format(i+1, -halfZdomain, halfZdomain))
713 if minZ >= halfZdomain
and maxZ >= halfZdomain:
714 input_helper_methods.error(
"\"layers\" has defined layer #{} to have both upper and lower bounds completely "\
715 "above the domain's z-dimensional range ({} to {}). At least one boundary must be within "\
716 "the domain's range. The domain's range is half of 3rd value in \"domainSize\" "\
717 "(z-dimension) in both positive and negative directions.".format(i+1, -halfZdomain, halfZdomain))
719 def num_of_regions():
720 """ Check the number of regions parameter."""
722 numRegions = input_helper_methods.verify_int(
723 input_helper_methods.value_of(
'numOfRegions', params),
727 print(
"Number of Regions {0}".format(numRegions))
728 if numRegions != len(params[
'regions']):
729 input_helper_methods.error(
"\"regions\" has defined {} regions but \"numOfRegions\" was defined to "\
730 "be {}.".format(len(params[
'regions']), numRegions))
735 """ Check the regions parameters provided. """
736 half_x_domain = params[
'domainSize'][0][
738 half_y_domain = params[
'domainSize'][0][
740 half_z_domain = params[
'domainSize'][0][
744 for i, region
in enumerate(params[
'regions']):
745 errResult = input_helper_methods.verify_list(
747 "regions #{}".format(i + 1),
748 input_helper_methods.verify_float,
750 if errResult !=
None:
751 input_helper_methods.error(
"\"regions\" has defined layer #{} to have {} element(s) but each region must "\
752 "have 6 elements, which define its upper and lower bounds".format(i+1, -errResult))
753 if params[
'regions'].count(regions) > 1:
754 input_helper_methods.error(
755 "\"regions\" has defined the same region more than once.")
764 if region[1] <= region[0]:
765 input_helper_methods.error(
"\"regions\" has defined region #{0} where xmin: {1} is "\
766 "greater than xmax {2}".format(i+1,region[0], region[1]))
767 if region[0] <= -half_x_domain
and region[1] <= -half_x_domain:
768 input_helper_methods.error(
"\"regions\" has defined layer #{} to have both upper and lower bounds completely "\
769 "below the domain's x-dimensional range ({} to {}). At least one boundary must be within "\
770 "the domain's range. The domain's range is half of 1st value in \"domainSize\" "\
771 "(x-dimension) in both positive and negative directions.".format(i+1, -half_x_domain, half_x_domain))
772 if region[0] >= half_x_domain
and region[1] >= half_x_domain:
773 input_helper_methods.error(
"\"regions\" has defined layer #{} to have both upper and lower bounds completely "\
774 "above the domain's x-dimensional range ({} to {}). At least one boundary must be within "\
775 "the domain's range. The domain's range is half of 1st value in \"domainSize\" "\
776 "(x-dimension) in both positive and negative directions.".format(i+1, -half_x_domain, half_x_domain))
778 if region[3] <= region[2]:
779 input_helper_methods.error(
"\"regions\" has defined region #{0} where ymin: {1} is "\
780 "greater than ymax {2}".format(i+1,region[3], region[2]))
781 if region[2] <= -half_y_domain
and region[3] <= -half_y_domain:
782 input_helper_methods.error(
"\"regions\" has defined layer #{} to have both upper and lower bounds completely "\
783 "below the domain's y-dimensional range ({} to {}). At least one boundary must be within "\
784 "the domain's range. The domain's range is half of 2nd value in \"domainSize\" "\
785 "(y-dimension) in both positive and negative directions.".format(i+1, -half_x_domain, half_x_domain))
786 if region[2] >= half_y_domain
and region[3] >= half_y_domain:
787 input_helper_methods.error(
"\"regions\" has defined layer #{} to have both upper and lower bounds completely "\
788 "above the domain's y-dimensional range ({} to {}). At least one boundary must be within "\
789 "the domain's range. The domain's range is half of 2nd value in \"domainSize\" "\
790 "(y-dimension) in both positive and negative directions.".format(i+1, -half_y_domain, half_y_domain))
793 if region[5] <= region[4]:
794 input_helper_methods.error(
"\"regions\" has defined region #{0} where zmin: {1} is "\
795 "greater than zmax {2}".format(i+1,region[5], region[4]))
796 if region[4] <= -half_z_domain
and region[5] <= -half_z_domain:
797 input_helper_methods.error(
"\"regions\" has defined layer #{} to have both upper and lower bounds completely "\
798 "below the domain's z-dimensional range ({} to {}). At least one boundary must be within "\
799 "the domain's range. The domain's range is half of 3rd value in \"domainSize\" "\
800 "(z-dimension) in both positive and negative directions.".format(i+1, -half_z_domain, half_z_domain))
801 if region[4] >= half_z_domain
and region[5] >= half_z_domain:
802 input_helper_methods.error(
"\"regions\" has defined layer #{} to have both upper and lower bounds completely "\
803 "above the domain's z-dimensional range ({} to {}). At least one boundary must be within "\
804 "the domain's range. The domain's range is half of 3rd value in \"domainSize\" "\
805 "(z-dimension) in both positive and negative directions.".format(i+1, -half_z_domain, half_z_domain))
808 """ Verify the flag that indicates whether if FRAM is disabled.
809 If FRAM is enabled, verify the value of h is valid.
811 if input_helper_methods.verify_flag(
812 input_helper_methods.value_of(
'disableFram', params),
816 input_helper_methods.warning(
"FRAM (feature rejection algorithm for meshing) is disabled. This means that"\
817 "dfnWorks will only run through fracture network generation (the code will stop before meshing)."\
818 "To run the full code change the disableFram option to 1")
821 """ Check the value of the seed used for pseudorandom number generation.
823 val = input_helper_methods.verify_int(input_helper_methods.value_of(
828 input_helper_methods.warning(
"\"seed\" has been set to 0. Random generator will use current wall "\
829 "time so distribution's random selection will not be as repeatable. "\
830 "Use an integer greater than 0 for better repeatability.", params)
831 params[
'seed'][0] = val
833 def ignore_boundary_faces():
834 """ Check the value fo the ignoreBoundaryFaces flag.
836 if input_helper_methods.verify_flag(
837 input_helper_methods.value_of(
'ignoreBoundaryFaces', params),
838 'ignoreBoundaryFaces') == 0:
841 def rejects_per_fracture():
842 """ Check the value of the rejectsPerFracture int.
844 val = input_helper_methods.verify_int(input_helper_methods.value_of(
845 'rejectsPerFracture', params),
846 'rejectsPerFracture',
850 input_helper_methods.warning(
851 "changing \"rejectsPerFracture\" from 0 to 1. Can't ensure 0 rejections.",
854 params[
'rejectsPerFracture'][0] = val
857 """ Check the list of family probabilites (the list of probabilities that a fracture is in each family).
860 errResult = input_helper_methods.verify_list(
861 input_helper_methods.value_of(
'famProb', params),
863 input_helper_methods.verify_float,
864 desiredLength=ellipseFams + rectFams,
868 if errResult !=
None:
870 input_helper_methods.error(
"\"famProb\" must have {} (nFamEll + nFamRect) non-zero elements,"\
871 "one for each family of ellipses and rectangles. {} probabiliies have "\
872 "been defined.".format(ellipseFams + rectFams, -errResult))
875 float(x)
for x
in input_helper_methods.value_of(
'famProb', params)
877 if sum(probList) != 1:
878 input_helper_methods.scale(probList, warningFile)
881 """ Check the parameters for user-defined rectangles and ellipses.
883 userEs =
"userEllipsesOnOff"
884 userRs =
"userRectanglesOnOff"
885 recByCoord =
"userRecByCoord"
886 ellByCoord =
"userEllByCoord"
887 polygonByCoord =
"userPolygonByCoord"
888 ePath =
"UserEll_Input_File_Path"
889 rPath =
"UserRect_Input_File_Path"
890 coordPath =
"RectByCoord_Input_File_Path"
891 ecoordPath =
"EllByCoord_Input_File_Path"
892 polycoordPath =
"PolygonByCoord_Input_File_Path"
893 invalid =
"\"{}\" is not a valid path."
895 if input_helper_methods.verify_flag(
896 input_helper_methods.value_of(ellByCoord, params),
898 if not os.path.isfile(
899 input_helper_methods.value_of(ecoordPath, params)):
900 print(
'THIS PATH IS NOT A VALID FILE PATH: ',
901 input_helper_methods.value_of(ecoordPath, params))
902 input_helper_methods.error(invalid.format(ecoordPath))
904 shutil.copy(input_helper_methods.value_of(ecoordPath, params),
907 if input_helper_methods.verify_flag(
908 input_helper_methods.value_of(userEs, params), userEs) == 1:
909 if not os.path.isfile(input_helper_methods.value_of(ePath,
911 print(
'THIS PATH IS NOT A VALID FILE PATH: ',
912 input_helper_methods.value_of(ePath, params))
913 input_helper_methods.error(invalid.format(ePath))
915 shutil.copy(input_helper_methods.value_of(ePath, params),
918 if input_helper_methods.verify_flag(
919 input_helper_methods.value_of(userRs, params), userRs) == 1:
920 if not os.path.isfile(input_helper_methods.value_of(rPath,
922 print(
'THIS PATH IS NOT A VALID FILE PATH: ',
923 input_helper_methods.value_of(rPath, params))
924 input_helper_methods.error(invalid.format(rPath))
926 shutil.copy(input_helper_methods.value_of(rPath, params),
929 if input_helper_methods.verify_flag(
930 input_helper_methods.value_of(recByCoord, params),
932 if not os.path.isfile(
933 input_helper_methods.value_of(coordPath, params)):
934 print(
'THIS PATH IS NOT A VALID FILE PATH: ',
935 input_helper_methods.value_of(coordPath, params))
936 input_helper_methods.error(invalid.format(coordPath))
938 shutil.copy(input_helper_methods.value_of(coordPath, params),
942 """ Verify the int value used for aperture.
944 apOption = input_helper_methods.verify_int(
945 input_helper_methods.value_of(
'aperture', params),
'aperture')
948 if input_helper_methods.verify_float(input_helper_methods.value_of(
949 'meanAperture', params),
952 input_helper_methods.error(
"\"meanAperture\" cannot be 0.")
953 if input_helper_methods.verify_float(input_helper_methods.value_of(
954 'stdAperture', params),
957 input_helper_methods.error(
"\"stdAperture\" cannot be 0. If you wish to have a standard deviation "\
958 "of 0, use a constant aperture instead.")
961 input_helper_methods.verify_list(input_helper_methods.value_of(
962 'apertureFromTransmissivity', params),
963 'apertureFromTransmissivity',
964 input_helper_methods.verify_float,
967 if input_helper_methods.value_of(
'apertureFromTransmissivity',
969 input_helper_methods.error(
970 "\"apertureFromTransmissivity\"'s first value cannot be 0."
972 if input_helper_methods.value_of(
'apertureFromTransmissivity',
974 input_helper_methods.warning(
975 "\"apertureFromTransmissivity\"'s second value is 0, which will result in a constant aperature.",
979 if input_helper_methods.verify_float(input_helper_methods.value_of(
980 'constantAperture', params),
984 params[
'constantAperture'][0] = 1e-25
985 input_helper_methods.warning(
"\"constantAperture\" was set to 0 and has been changed "\
986 "to 1e-25 so fractures have non-zero thickness.", params)
989 input_helper_methods.verify_list(input_helper_methods.value_of(
990 'lengthCorrelatedAperture', params),
991 'lengthCorrelatedAperture',
992 input_helper_methods.verify_float,
995 if input_helper_methods.value_of(
'lengthCorrelatedAperture',
997 input_helper_methods.error(
998 "\"lengthCorrelatedAperture\"'s first value cannot be 0.")
999 if input_helper_methods.value_of(
'lengthCorrelatedAperture',
1001 input_helper_methods.warning(
1002 "\"lengthCorrelatedAperture\"'s second value is 0, which will result in a constant aperature.",
1006 input_helper_methods.error(
"\"aperture\" must only be option 1 (log-normal), 2 (from transmissivity), "\
1007 "3 (constant), or 4 (length correlated).")
1010 """Verify the float used for permeability, if permOption is set to 1"""
1011 if input_helper_methods.verify_flag(
1012 input_helper_methods.value_of(
'permOption'),
1014 if input_helper_methods.verify_float(
1015 input_helper_methods.value_of(
'constantPermeability',
1017 'constantPermeability') == 0:
1018 params[
'constantPermeability'][0] = 1e-25
1019 input_helper_methods.warning(
"\"constantPermeability\" was set to 0 and has been changed "\
1020 "to 1e-25 so fractures have non-zero permeability.", params)
1027 """Verify the number of polygons integer."""
1028 val = input_helper_methods.verify_int(input_helper_methods.value_of(
1032 if val == 0: input_helper_methods.error(
"\"nPoly\" cannot be zero.")
1033 params[
'nPoly'][0] = val
1036 """Verify the p32 target parameters for ellipses and parameters."""
1037 global ellipseFams, rectFams
1038 errResult =
None if (ellipseFams == 0)
else input_helper_methods.verify_list(input_helper_methods.value_of(
'e_p32Targets', params),
'e_p32Targets', \
1039 input_helper_methods.verify_float, desiredLength = ellipseFams, noNegs=
True, noZeros=
True)
1040 if errResult !=
None:
1041 input_helper_methods.error(
"\"e_p32Targets\" has defined {} p32 values but there is(are) {} ellipse family(ies). "\
1042 "Need one p32 value per ellipse family.".format(-errResult, ellipseFams))
1044 errResult =
None if (rectFams == 0)
else input_helper_methods.verify_list(input_helper_methods.value_of(
'r_p32Targets', params),
"r_p32Targets", \
1045 input_helper_methods.verify_float, desiredLength = rectFams, noNegs=
True, noZeros=
True)
1046 if errResult !=
None:
1047 input_helper_methods.error(
"\"r_p32Targets\" has defined {} p32 value(s) but there is(are) {} rectangle "\
1048 "family(ies). Need one p32 value per rectangle family)".format(-errResult, rectFams))
1050 def f(theta, t, a, b):
1051 """Differential Equation Angle Theta as a function of arc length, see Hyman et al. 2014, SIAM J. Sci. Compu
1053 return 1.0 / np.sqrt((a * np.sin(theta))**2 + (b * np.cos(theta)**2))
1055 def h_shape_check(aspect, minRadius, num_points=4):
1056 """ Check that the arc length discretized ellipse is greater than 3*h """
1066 c = np.pi * (a + b) * (1.0 + (3.0 * ((a - b) / (a + b))**2) /
1067 (10. + np.sqrt(4. - 3. * ((a - b) /
1076 steps = np.linspace(0, c, n + 1)
1078 theta = scipy.integrate.odeint(f, 0, steps, args=(a, b), rtol=10**-10)
1081 x = a * r * np.cos(theta)
1082 y = b * r * np.sin(theta)
1086 for i
in range(1, n):
1087 for j
in range(i, n):
1089 h_current = np.sqrt((x[i] - x[j])**2 + (y[i] - y[j])**2)
1090 if (h_current < h_min):
1095 def compare_pts_v_sh(prefix, hval):
1096 """ Check that the rectangles and ellipses generated will not involve features with length less than 3*h value used in FRAM.
1098 shape =
"ellipse" if prefix ==
'e' else "rectangle"
1099 aspectList = params[prefix +
"aspect"][0]
1100 numPointsList =
None
1102 if shape ==
"ellipse":
1103 numPointsList = params[
'enumPoints'][0]
1112 for distrib
in params[prefix +
'distr'][0]:
1113 if distrib
in [1, 2, 3, 4]:
1115 minRad = params[prefix +
'LogMin'][0][numLog]
1118 minRad = params[prefix +
'min'][0][numTPL]
1121 minRad = params[prefix +
'ExpMin'][0][numEXP]
1124 minRad = params[prefix +
'const'][0][numConst]
1126 if shape ==
"ellipse":
1127 hmin = h_shape_check(float(aspectList[numAspect]),
1129 int(numPointsList[numAspect]))
1131 hmin = h_shape_check(
1132 float(aspectList[numAspect]), float(minRad)
1135 if hmin < (3 * hval):
1136 input_helper_methods.error(shape +
" family #{} has defined a shape with features too small for meshing. Increase the aspect "\
1137 "ratio or minimum radius so that no 2 points of the polygon create a line of length less "\
1138 "than 3h".format(numAspect+1))
1143 """ Check the float value provided for h to be used in FRAM (the feautre rejection algorithm for meshing.
1147 val = input_helper_methods.verify_float(input_helper_methods.value_of(
1152 if val == 0: input_helper_methods.error(
"\"h\" cannot be 0.")
1153 if minFracSize
is None:
1155 if val < minFracSize / 1000.0
and ellipseFams + rectFams > 0:
1162 input_helper_methods.warning(
"\"h\" (length scale) is smaller than one 1000th of the minimum "\
1163 "fracture size ({}). The generated mesh will be extremely fine and will likely be "\
1164 "computationally exhausting to create. Computation may take longer than usual.".format(minFracSize))
1165 if val > minFracSize / 10.0:
1166 input_helper_methods.warning(
"\"h\" (length scale) is greater than one 10th of the minimum "\
1167 "fracture size ({}). The generated mesh will be very coarse and there will likely "\
1168 "be a high rate of fracture rejection.".format(minFracSize))
1170 if val > minFracSize:
1171 input_helper_methods.error(
"\"h\" (length scale) is greater than the minimum fracture size ({}). "\
1172 "Choose a smaller value for h or a greater minimum fracure size."\
1173 " ".format(minFracSize))
1174 compare_pts_v_sh(
'e', val)
1175 compare_pts_v_sh(
'r', val)
1177 params[
'h'][0] = val
1179 def boundary_faces():
1180 """Check that the boundaryFaceis list is a list of flags of length 6, one for each side of the domain
1181 ie {1, 1, 1, 0, 0, 1} represents --> {+x, -x, +y, -y, +z, -z}
1182 DFN only keeps clusters with connections to domain boundaries set to 1.
1184 errResult = input_helper_methods.verify_list(
1185 input_helper_methods.value_of(
'boundaryFaces', params),
1186 'boundaryFaces', input_helper_methods.verify_flag, 6)
1187 if errResult !=
None:
1188 input_helper_methods.error(
"\"boundaryFaces\" must be a list of 6 flags (0 or 1), {} have(has) been defined. Each flag "\
1189 "represents a side of the domain, {{+x, -x, +y, -y, +z, -z}}.".format(-errResult))
1192 """ Check the integer value of enumPoints for each ellipse family."""
1193 errResult = input_helper_methods.verify_list(
1194 input_helper_methods.value_of(
'enumPoints', params),
1196 input_helper_methods.verify_int,
1197 desiredLength=ellipseFams,
1200 if errResult !=
None:
1201 input_helper_methods.error(
"\"enumPoints\" has defined {} value(s) but there is(are) {} families of ellipses. Please "\
1202 "define one enumPoints value greater than 4 for each ellipse family.".format(-errResult, ellipseFams))
1203 for val
in input_helper_methods.value_of(
"enumPoints"):
1205 input_helper_methods.error(
"\"enumPoints\" contains a value less than or equal to 4. If 4 points were intended, "\
1206 "define this family as a rectangle family. No polygons with less than 4 verticies are acceptable.")
1217 """ Check the aspect of the the rectangle or ellipse families. """
1218 shape =
"ellipse" if prefix ==
'e' else "rectangle"
1219 numFamilies = ellipseFams
if prefix ==
'e' else rectFams
1220 paramName = prefix +
"aspect"
1222 errResult = input_helper_methods.verify_list(
1223 input_helper_methods.value_of(paramName),
1225 input_helper_methods.verify_float,
1226 desiredLength=numFamilies,
1229 if errResult !=
None:
1230 input_helper_methods.error(
"\"{}\" has defined {} value(s) but there is(are) {} {} families. Please define one "\
1231 "aspect ratio for each family.".format(paramName, -errResult, numFamilies, shape))
1233 def angle_option(prefix):
1234 """ Check the angle option flag. """
1235 paramName = prefix +
"AngleOption"
1236 input_helper_methods.verify_flag(
1237 input_helper_methods.value_of(paramName), paramName)
1240 """ Check the number of layers. """
1241 shape =
"ellipse" if prefix ==
'e' else "rectangle"
1242 numFamilies = ellipseFams
if prefix ==
'e' else rectFams
1243 paramName = prefix +
"Layer"
1245 errResult = input_helper_methods.verify_list(
1246 input_helper_methods.value_of(paramName),
1248 input_helper_methods.verify_int,
1249 desiredLength=numFamilies)
1250 if errResult !=
None:
1251 input_helper_methods.error(
"\"{}\" has defined {} layer(s) but there is(are) {} {} families. "\
1252 "Need one layer per {} family. Layers are numbered by the order they "\
1253 "are defined in 'layers' parameter. Layer 0 is the whole domain."\
1254 .format(paramName, -errResult, numFamilies, shape, shape))
1256 for layer
in input_helper_methods.value_of(paramName):
1257 if input_helper_methods.is_negative(int(layer)):
1258 input_helper_methods.error(
"\"{}\" contains a negative layer number. Only values from 0 to "\
1259 "{} (numOfLayers) are accepted. Layer 0 corresponds to the entire"\
1260 "domain.".format(paramName, numLayers))
1261 if int(layer) > numLayers:
1262 input_helper_methods.error(
"\"{}\" contains value '{}' but only {} layer(s) is(are) defined. Make sure the "\
1263 "layer numbers referenced here are found in that same position in \"layers\" "\
1264 "parameter.".format(paramName, layer, numLayers))
1267 """ Check the number of region. """
1268 shape =
"ellipse" if prefix ==
'e' else "rectangle"
1269 numFamilies = ellipseFams
if prefix ==
'e' else rectFams
1270 paramName = prefix +
"Region"
1272 errResult = input_helper_methods.verify_list(
1273 input_helper_methods.value_of(paramName),
1275 input_helper_methods.verify_int,
1276 desiredLength=numFamilies)
1277 if errResult !=
None:
1278 input_helper_methods.error(
"\"{}\" has defined {} layer(s) but there is(are) {} {} families. "\
1279 "Need one layer per {} family. Regions are numbered by the order they "\
1280 "are defined in 'region' parameter. Layer 0 is the whole domain."\
1281 .format(paramName, -errResult, numFamilies, shape, shape))
1283 for region
in input_helper_methods.value_of(paramName):
1284 if input_helper_methods.is_negative(int(region)):
1285 input_helper_methods.error(
"\"{}\" contains a negative layer number. Only values from 0 to "\
1286 "{} (numOfRegions) are accepted. Layer 0 corresponds to the entire"\
1287 "domain.".format(paramName, numRegions))
1288 if int(region) > numRegions:
1289 input_helper_methods.error(
"\"{}\" contains value '{}' but only {} region(s) is(are) defined. Make sure the "\
1290 "region numbers referenced here are found in that same position in \"region\" "\
1291 "parameter.".format(paramName, region, numRegions))
1293 def theta_phi_kappa(prefix):
1294 """ Check the angle parameters used for Fisher distributions
1296 shape =
"ellipse" if prefix ==
'e' else "rectangle"
1297 numFamilies = ellipseFams
if prefix ==
'e' else rectFams
1298 paramNames = [prefix + name
for name
in [
"theta",
"phi",
"kappa"]]
1299 errString =
"\"{}\" has defined {} angle(s) but there is(are) {} {} family(ies)."\
1300 "Please defined one angle for each {} family."
1302 for param
in paramNames:
1303 errResult = input_helper_methods.verify_list(
1304 input_helper_methods.value_of(param),
1306 input_helper_methods.verify_float,
1307 desiredLength=numFamilies)
1308 if errResult !=
None:
1309 input_helper_methods.error(
1310 errString.format(param, -errResult, numFamilies, shape,
1336 """ Parse each line of the input file.
1338 for line
in inputIterator:
1339 line = input_helper_methods.extract_parameters(
1340 line, inputIterator)
1341 if (line !=
"" and ":" in line):
1342 input_helper_methods.process_line(line, unfoundKeys,
1343 inputIterator, warningFile)
1344 needed = [unfound
for unfound
in unfoundKeys
if unfound
in mandatory]
1348 errString +=
"\t\"" + key +
"\"\n"
1349 input_helper_methods.error(
1350 "Missing the following mandatory parameters: \n{}".format(
1354 """ Verify all of the parameters in the input file.
1356 distributions = distr_module.distr(params, numEdistribs, numRdistribs,
1359 n_fam_ell, n_fam_rect, stop_condition, domain_size, num_of_layers,
1360 num_of_regions, seed, domain_size_increase, ignore_boundary_faces,
1361 rejects_per_fracture, user_defined,
1362 input_helper_methods.check_fam_count, check_no_dep_flags, fam_prob
1366 layer, region, aspect, angle_option, theta_phi_kappa,
1367 distributions.beta_distribution, distributions.distr
1371 distributions.lognormal_dist, distributions.tpl_dist,
1372 distributions.exponential_dist, distributions.constant_dist
1375 checkLast = [disable_fram, aperture, permeability]
1377 for paramFunc
in firstPriority:
1381 for paramFunc
in generalized:
1386 for paramFunc
in generalized:
1389 for i, paramFunc
in enumerate(distribs):
1390 if numEdistribs[i + 1] > 0:
1394 if numRdistribs[i + 1] > 0:
1398 for paramFunc
in checkLast:
1402 """ Write the parameters from the verbose input file back to a simplified input file.
1404 for param
in params:
1405 if param ==
'layers':
1406 writer.write(param +
': ')
1407 for layer
in params[
'layers']:
1409 input_helper_methods.list_to_curly(str(layer)) +
" ")
1412 elif param ==
'regions':
1413 writer.write(param +
': ')
1414 for region
in params[
'regions']:
1416 input_helper_methods.list_to_curly(str(region)) +
" ")
1419 elif type(input_helper_methods.value_of(param,
1420 writing=
True))
is list:
1421 curl = input_helper_methods.list_to_curly(
1422 str(input_helper_methods.value_of(param, writing=
True)))
1423 writer.write(param +
': ' + curl +
'\n')
1427 str(input_helper_methods.value_of(param, writing=
True)) +
1432 if not os.path.exists(os.getcwd()):
1433 print(
"ERROR: cwd: ", os.getcwd(),
" does not exist")
1434 if not os.path.exists(os.path.abspath(self.dfnGen_file)):
1435 print(
"ERROR: dfnGen input file path: ",
1436 os.path.abspath(self.dfnGen_file),
" does not exist")
1437 print(os.path.abspath(self.dfnGen_file))
1438 shutil.copy(os.path.abspath(self.dfnGen_file), os.getcwd())
1440 sys.exit(
"Unable to copy dfnGen input file\n%s\nExiting" %
1443 ioPaths = {
"input":
"",
"output":
""}
1445 ioPaths[
"input"] = self.dfnGen_file
1447 input_helper_methods.error(
"Please provide an input file path as the first command line argument.\n"\
1448 " $ python3 inputParser.py [inputPath] [outputPath (Optional)]")
1450 ioPaths[
"output"] = self.jobname +
'/' + self.dfnGen_file.split(
1451 '/')[-1][:-4] +
'_clean.dat'
1452 ioPaths[
"output"] = os.path.abspath(ioPaths[
"output"])
1453 print(ioPaths[
"output"])
1455 ioPaths[
"output"] =
"polishedOutput.txt"
1456 input_helper_methods.warning(
"No output path has been provided so output will be written to "\
1457 "\"polishedOutput.txt\" in your current working directory.", params)
1459 reader = open(ioPaths[
"input"],
'r')
1461 input_helper_methods.error(
1462 "Check that the path of your input file is valid")
1464 writer = open(ioPaths[
"output"],
'w')
1466 input_helper_methods.error(
1467 "Check that the path of your output file is valid.")
1469 inputIterator = iter(reader)
1470 print(
'--> Checking input data')
1471 print(
'--> Input Data: ', ioPaths[
"input"])
1472 print(
'--> Output File: ', ioPaths[
"output"])
1477 print(
'--> Checking Input Data Complete')