Source code for pydfnworks.dfnGen.generation.gen_input

import os
import sys
import shutil
import numpy as np
import scipy.integrate
import re

#pydfnworks modules
from pydfnworks.dfnGen.generation import gen_distributions as distr_module


class input_helper():
    """ Functions to help parse the input file and check input parameters.
        
        Attributes:
            * params (list): list of parameters specified in the input file.
            * minFracSize (float): the minimum fracture size.
    """
    def __init__(self, params, minFracSize):
        self.params = params
        self.minFracSize = minFracSize

    ## ====================================================================== ##
    ##                              Helper Functions                          ##
    ## ====================================================================== ##

    def curly_to_list(self, curlyList):
        """ '{1,2,3}' --> [1,2,3]
        """
        return re.sub("{|}", "", curlyList).strip().split(",")

    def list_to_curly(self, strList):
        """ [1,2,3] --> '{1,2,3}'   for writing output
        """
        curl = re.sub(r'\[', '{', strList)
        curl = re.sub(r'\]', '}', curl)
        curl = re.sub(r"\'", '', curl)
        return curl

    def has_curlys(self, line, key):
        """ Checks to see that every { has a matching }.
        """
        if '{' in line and '}' in line: return True
        elif '{' in line or '}' in line:
            self.error(
                "Line defining \"{}\" contains a single curly brace.".format(
                    key))
        return False

    def value_of(self, key, writing=False):
        """ Use to get key's value in params. writing always false  
        """
        if (not writing) and (len(self.params[key]) > 1):
            self.error(
                "\"{}\" can only correspond to 1 list. {} lists have been defined."
                .format(key, len(self.params[key])))
        try:
            val = self.params[key][0]
            if val == '' or val == []:
                self.error("\"{}\" does not have a value.".format(key))
            return val
        except IndexError:
            self.error("\"{}\" has not been defined.".format(
                key))  ## Include assumptions (ie no Angleoption -> degrees?)

    def get_groups(self, line, valList, key):
        """ extract values between { and } 
        """
        curlyGroup = re.compile('({.*?})')
        groups = re.findall(curlyGroup, line)
        for group in groups:
            line = line.replace(group, '', 1)  ## only delete first occurrence
            valList.append(self.curly_to_list(group))

        if line.strip() != "":
            self.error(
                "Unexpected character found while parsing \"{}\".".format(key))

    def val_helper(self, line, valList, key):
        """ pulls values from culry brackets 
        """
        if self.has_curlys(line, key):
            self.get_groups(line, valList, key)
        else:
            valList.append(line)

    def error(self, errString):
        """ print an error
        
        Args:
            errString (str): a string describing the error
        """
        error = "\nERROR --- " + errString + "\n----Program terminated while parsing input----\n"
        sys.stderr.write(error)
        sys.exit(1)

    def warning(self, warnString):
        """ print warning
        
        Args:
            warnStinrg (str): a string with the warning
        """
        print("WARNING --- " + warnString)

    def warning(self, warnString, warningFile=''):
        """ print a warning to a file (currently does not work)"""
        #global warningFile
        print("WARNING --- " + warnString)
        #warningFile.write("WARNING --- " + warnString + "\n")

    def is_negative(self, num):
        """"returns True if num is negative, false otherwise
        """
        return True if num < 0 else False

    def check_fam_count(self):
        """Makes sure at least one polygon family has been defined in nFamRect or nFamEll
        OR that there is a user input file for polygons.
        """
        userDefExists = (self.value_of('userEllipsesOnOff') == '1') |\
                   (self.value_of('userRectanglesOnOff') == '1') |\
                   (self.value_of('userRecByCoord') == '1') |\
                   (self.value_of('userEllByCoord') == '1')

        ellipseFams = len(self.value_of('nFamRect'))
        rectFams = len(self.value_of('nFamEll'))

        if ellipseFams + rectFams <= 0 and not userDefExists:
            self.error("Zero polygon families have been defined. Please create at least one family "\
                  "of ellipses/rectagnles, or provide a user-defined-polygon input file path in "\
                  "\"UserEll_Input_File_Path\", \"UserRect_Input_File_Path\", \"UserEll_Input_File_Path\", or "\
                  "\"RectByCoord_Input_File_Path\" and set the corresponding flag to '1'.")

    def scale(self, probList, warningFile):
        """ scales list of probabilities (famProb) that doesn't add up to 1
        ie [.2, .2, .4] --> [0.25, 0.25, 0.5] 
        """
        total = sum(probList)
        scaled = [float("{:.6}".format(x / total)) for x in probList]
        self.warning("'famProb' probabilities did not add to 1 and have been scaled accordingly "\
            "for their current sum, {:.6}. Scaled {} to {}".format(total, probList, scaled), warningFile)
        return [x / total for x in probList]

    def zero_in_std_devs(self, valList):
        """ returns True is there is a zero in valList of standard deviations
        """
        for val in valList:
            if float(val) == 0: return True

    def check_min_max(self, minParam, maxParam, shape):
        """ Checks that the minimum parameter for a family is not greater or equal to the maximum parameter.
        """
        for minV, maxV in zip(self.value_of(minParam),
                              self.value_of(maxParam)):
            if minV == maxV:
                self.error("\"{}\" and \"{}\" contain equal values for the same {} family. "\
                      "If {} and {} were intended to be the same, use the constant distribution "\
                      "(4) instead.".format(minParam, maxParam, shape, minParam, maxParam))
            if minV > maxV:
                self.error(
                    "\"{}\" is greater than \"{}\" in a(n) {} family.".format(
                        minParam, maxParam, shape))
                sys.exit(1)

    def check_mean(self, minParam, maxParam, meanParam, warningFile=''):
        """ Warns the user if the minimum value of a parameter is greater than the family's mean value, or if the
        maximum value of the parameter is less than the family's mean value.
        """
        for minV, meanV in zip(self.value_of(minParam),
                               self.value_of(meanParam)):
            if minV > meanV:
                self.warning("\"{}\" contains a min value greater than its family's mean value in "\
                       "\"{}\". This could drastically increase computation time due to increased "\
                       "rejection rate of the most common fracture sizes.".format(minParam, meanParam), warningFile)
        for maxV, meanV in zip(self.value_of(maxParam),
                               self.value_of(meanParam)):
            if maxV < meanV:
                self.warning("\"{}\" contains a max value less than its family's mean value in "\
                       "\"{}\". This could drastically increase computation time due to increased "\
                       "rejection rate of the most common fracture sizes.".format(maxParam, meanParam), warningFile)

    def check_min_frac_size(self, valList):
        """ Corrects the minimum fracture size if necessary, by looking at the values in valList.
        """
        global minFracSize
        for val in valList:
            if minFracSize == None:
                minFracSize = val
            elif val < minFracSize:
                minFracSize = val

    ## ====================================================================== ##
    ##                              Parsing Functions                         ##
    ## ====================================================================== ##
    def extract_parameters(self, line, inputIterator):
        """Returns line without comments or white space.
        """
        if "/*" in line:
            comment = line
            line = line[:line.index(
                "/*")]  ## only process text before '/*' comment
            while "*/" not in comment:
                comment = next(
                    inputIterator)  ## just moves iterator past comment

        elif "//" in line:
            line = line[:line.index(
                "//")]  ## only process text before '//' comment

        return line.strip()

    def find_val(self, line, key, inputIterator, unfoundKeys, warningFile):
        """ Extract the value for key from line. 
        """
        valList = []
        line = line[line.index(":") + 1:].strip()
        if line != "": self.val_helper(line, valList, key)

        line = self.extract_parameters(next(inputIterator), inputIterator)
        while ':' not in line:
            line = line.strip()
            if line != "":
                self.val_helper(line, valList, key)
            try:
                line = self.extract_parameters(next(inputIterator),
                                               inputIterator)
            except StopIteration:
                break

        if valList == [] and key in mandatory:
            self.error(
                "\"{}\" is a mandatory parameter and must be defined.".format(
                    key))
        if key is not None:
            self.params[key] = valList if valList != [] else [
                ""
            ]  ## allows nothing to be entered for unused params
        if line != "":
            self.process_line(line, unfoundKeys, inputIterator, warningFile)

    def find_key(self, line, unfoundKeys, warningFile):
        """ Input: line containing a parameter (key) preceding a ":" 
           
        Returns: 
            * key -- if it has not been defined yet and is valid
            * None -- if key does not exist
            * exits -- if the key has already been defined to prevent duplicate confusion        
        """
        key = line[:line.index(":")].strip()
        if key in unfoundKeys:
            unfoundKeys.remove(key)
            return key
        try:
            self.params[key]
            self.error("\"{}\" has been defined more than once.".format(key))
        except KeyError:
            self.warning(
                "\"" + key + "\" is not one of the valid parameter names.",
                warningFile)

    def process_line(self, line, unfoundKeys, inputIterator, warningFile):
        """ Find the key in a line, and the value for that key.
        """
        if line.strip != "":
            key = self.find_key(line, unfoundKeys, warningFile)
            if key != None:
                self.find_val(line, key, inputIterator, unfoundKeys,
                              warningFile)

    ## ====================================================================== ##
    ##                              Verification                              ##
    ## ====================================================================== ##
    ## Note: Always provide EITHER a key (ie "stopCondition")
    ##         OR inList = True/False (boolean indicating val being checked is inside a list)

    ## Input: value - value being checked
    ##        key - parameter the value belongs to
    ##        inList - (Optional)
    def verify_flag(self, value, key="", inList=False):
        """ Verify that value is either a 0 or a 1.
        """
        if value is '0' or value is '1':
            return int(value)
        elif inList:
            return None
        else:
            self.error("\"{}\" must be either '0' or '1'".format(key))

    def verify_float(self, value, key="", inList=False, noNeg=False):
        """ Verify that value is a positive float.
        """
        if type(value) is list:
            self.error(
                "\"{}\" contains curly braces {{}} but should not be a list value."
                .format(key))
        try:
            if noNeg and float(value) < 0:
                self.error("\"{}\" cannot be a negative number.".format(key))
            return float(value)
        except ValueError:
            if inList: return None
            else:
                self.error("\"{}\" contains an unexpected character. Must be a single "\
                      "floating point value (0.5, 1.6, 4.0, etc.)".format(key))

    def verify_int(self, value, key="", inList=False, noNeg=False):
        """ Verify that value is a positive integer.
        """
        if type(value) is list:
            self.error(
                "\"{}\" contains curly braces {{}} but should not be a list value."
                .format(key))
        try:
            if noNeg and int(re.sub(r'\.0*$', '', value)) < 0:
                self.error("\"{}\" cannot be a negative number.".format(key))
            return int(re.sub(r'\.0*$', '',
                              value))  ## regex for removing .0* (ie 4.00 -> 4)
        except ValueError:
            if inList: return None
            else:
                self.error("\"{}\" contains an unexpected character. Must be a single "\
                      "integer value (0,1,2,3,etc.)".format(key))

    def verify_list(self,
                    valList,
                    key,
                    verificationFn,
                    desiredLength,
                    noZeros=False,
                    noNegs=False):
        """verifies input list that come in format {0, 1, 2, 3}
       
        Input: 
            * valList - list of values (flags, floats, or ints) corresponding to a parameter
            * key - the name of the parameter whose list is being verified
            * verificationFn - (either verifyflag, verifyfloat or verifyint) checks each list element 
            * desiredLength - how many elements are supposed to be in the list
            * noZeros - (optional) True for lists than cannot contain 0's, false if 0's are ok  
            * noNegs - (optional) True for lists than cannot contain negative numbers, false otherwise
        Output:
            * returns negative value of list length to indicate incorrect length and provide meaningful error message
            * prints error and exits if a value of the wrong type is found in the list
            * returns None if successful"""

        if valList == ['']: return 0
        if type(valList) is not list:
            self.error(
                "\"{}\"'s value must be a list enclosed in curly brackets {{}}."
                .format(key))
        if desiredLength != 0 and int(len(valList)) != int(desiredLength):
            print('list desired length is ', desiredLength, 'but valList is ',
                  valList, 'with length ', len(valList))
            return -len(valList)
        for i, value in enumerate(valList):
            value = value.strip()
            verifiedVal = verificationFn(value, inList=True)
            if verifiedVal == None:
                listType = re.sub(
                    'integer', 'int',
                    re.sub(
                        r'verify', '',
                        verificationFn.__name__))  ## 'verifyint' --> 'integer'
                self.error("\"{}\" must be a list of {}s {}. non-{} found in "\
                      "list".format(key, listType, examples[listType], listType))
            if noZeros and verifiedVal == 0:
                self.error("\"{}\" list cannot contain any zeros.".format(key))
            if noNegs and self.is_negative(float(verifiedVal)):
                self.error(
                    "\"{}\" list cannot contain negative values.".format(key))
            valList[i] = verifiedVal

    ## def verifynumvalsis(length, key):f
    ##  if len(self.params[key]) != length:
    ##     self.error("error: ", "\"" + param + "\"", "should have", length, "value(s) but", len(self.params[key]), "are defined.")
    ##    sys.exit()


[docs]def check_input(self, input_file='', output_file=''): """Check input file for DFNGen to make sure all necessary parameters are defined Input Format Requirements: * Each parameter must be defined on its own line (separate by newline) * A parameter (key) MUST be separated from its value by a colon ':' (ie. --> key: value) * Values may also be placed on lines after the 'key' * Comment Format: On a line containing // or / ``*``, nothing after ``*`` / or // will be processed but text before a comment will be processed Parameters ---------- input_file : string name of dfnGen input file output_file : string Name of stripped down input file for DFNGen (input_file_clean.dat) Returns ------- None Notes ----- 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. """ global params ## BIG TODO s ----- ## ==== Problems ==== ## ## 11. Multiple keys on one line ## 15. check # values (famprob: {.5,.5} {.3, .3., .4}) params = { 'esd': [], 'insertUserRectanglesFirst': [], 'keepOnlyLargestCluster': [], 'keepIsolatedFractures': [], 'rmin': [], 'rAngleOption': [], 'boundaryFaces': [], 'userRectanglesOnOff': [], 'printRejectReasons': [], 'numOfLayers': [], 'numOfRegions': [], 'RectByCoord_Input_File_Path': [], 'eLogMean': [], 'rExpMin': [], 'lengthCorrelatedAperture': [], 'ebetaDistribution': [], 'tripleIntersections': [], 'layers': [], 'regions': [], 'stdAperture': [], 'ealpha': [], 'constantPermeability': [], 'rLogMax': [], 'rLogMean': [], 'nFamRect': [], 'etheta': [], 'eLogMax': [], 'rphi': [], 'outputAllRadii': [], 'r_p32Targets': [], 'permOption': [], 'userRecByCoord': [], 'RectByCoord_Input_File_Path': [], 'userRectanglesOnOff': [], 'UserRect_Input_File_Path': [], 'userEllByCoord': [], 'EllByCoord_Input_File_Path': [], 'userEllipsesOnOff': [], 'UserEll_Input_File_Path': [], 'userPolygonByCoord': [], 'PolygonByCoord_Input_File_Path': [], 'rExpMean': [], 'rbetaDistribution': [], 'aperture': [], 'emax': [], 'eExpMean': [], 'e_p32Targets': [], 'eLayer': [], 'eRegion': [], 'domainSizeIncrease': [], 'h': [], 'outputFinalRadiiPerFamily': [], 'rbeta': [], 'rLogMin': [], 'edistr': [], 'domainSize': [], 'eExpMin': [], 'ekappa': [], 'rLayer': [], 'rRegion': [], 'seed': [], 'constantAperture': [], 'stopCondition': [], 'enumPoints': [], 'meanAperture': [], 'eLogMin': [], 'easpect': [], 'rtheta': [], 'rdistr': [], 'rconst': [], 'rExpMax': [], 'ignoreBoundaryFaces': [], 'visualizationMode': [], 'outputAcceptedRadiiPerFamily': [], 'apertureFromTransmissivity': [], 'rsd': [], 'ebeta': [], 'nFamEll': [], 'econst': [], 'raspect': [], 'eAngleOption': [], 'emin': [], 'ephi': [], 'rmax': [], 'famProb': [], 'disableFram': [], 'ralpha': [], 'nPoly': [], 'rejectsPerFracture': [], 'rkappa': [], 'eExpMax': [], 'forceLargeFractures': [], 'radiiListIncrease': [], 'removeFracturesLessThan': [] } global unfoundKeys unfoundKeys = { 'stopCondition', 'nPoly', 'outputAllRadii', 'outputAllRadii', 'outputFinalRadiiPerFamily', 'outputAcceptedRadiiPerFamily', 'domainSize', 'numOfLayers', 'layers', 'numOfRegions', 'regions', 'h', 'tripleIntersections', 'printRejectReasons', 'disableFram', 'visualizationMode', 'seed', 'domainSizeIncrease', 'keepOnlyLargestCluster', 'keepIsolatedFractures', 'ignoreBoundaryFaces', 'boundaryFaces', 'rejectsPerFracture', 'famProb', 'insertUserRectanglesFirst', 'nFamEll', 'eLayer', 'eRegion', 'edistr', 'ebetaDistribution', 'e_p32Targets', 'easpect', 'enumPoints', 'eAngleOption', 'etheta', 'ephi', 'ebeta', 'ekappa', 'eLogMean', 'esd', 'eLogMin', 'eLogMax', 'eExpMean', 'eExpMin', 'eExpMax', 'econst', 'emin', 'emax', 'ealpha', 'nFamRect', 'rLayer', 'rRegion', 'rdistr', 'rbetaDistribution', 'r_p32Targets', 'raspect', 'rAngleOption', 'rtheta', 'rphi', 'rbeta', 'rkappa', 'rLogMean', 'rsd', 'rLogMin', 'rLogMax', 'rmin', 'rmax', 'ralpha', 'rExpMean', 'rExpMin', 'rExpMax', 'rconst', 'userEllipsesOnOff', 'UserEll_Input_File_Path', 'userRectanglesOnOff', 'UserRect_Input_File_Path', 'EllByCoord_Input_File_Path', 'userEllByCoord', 'userRecByCoord', 'userPolygonByCoord', 'RectByCoord_Input_File_Path', 'PolygonByCoord_Input_File_Path', 'aperture', 'meanAperture', 'stdAperture', 'apertureFromTransmissivity', 'constantAperture', 'lengthCorrelatedAperture', 'permOption', 'constantPermeability', 'forceLargeFractures', 'radiiListIncrease', 'removeFracturesLessThan' } global mandatory mandatory = { 'stopCondition', 'domainSize', 'numOfLayers', 'numOfRegions', 'outputAllRadii', 'outputFinalRadiiPerFamily', 'outputAcceptedRadiiPerFamily', 'tripleIntersections', 'printRejectReasons', 'disableFram', 'visualizationMode', 'seed', 'domainSizeIncrease', 'keepOnlyLargestCluster', 'keepIsolatedFractures', 'ignoreBoundaryFaces', 'rejectsPerFracture', 'famProb', 'insertUserRectanglesFirst', 'nFamEll', 'nFamRect', 'userEllipsesOnOff', 'userRectanglesOnOff', 'userEllByCoord', 'userRecByCoord', 'userPolygonByCoord', 'aperture', 'permOption', 'forceLargeFractures', 'radiiListIncrease', 'removeFracturesLessThan' } global noDependancyFlags noDependancyFlags = [ 'outputAllRadii', 'outputFinalRadiiPerFamily', 'outputAcceptedRadiiPerFamily', 'tripleIntersections', 'printRejectReasons', 'visualizationMode', 'keepOnlyLargestCluster', 'keepIsolatedFractures', 'insertUserRectanglesFirst', 'forceLargeFractures' ] global examples examples = { "Flag": "(0 or 1)", "Float": "(0.5, 1.6, 4.0, etc.)", "Int": "(0,1,2,3,etc.)" } global ellipseFams ellipseFams = 0 global rectFams rectFams = 0 global numLayers numLayers = 0 global numRegions numRegions = 0 global minFracSize minFracSize = None ## WARNING: Index[0] for the following lists should never be used. See edistr() and rdistr() for clarity. global numEdistribs numEdistribs = [ -1, 0, 0, 0, 0 ] ## [0 = no-op, 1 = # log-normal's, 2 = # Truncated Power Law's, 3 = # Exp's, # constant's] global numRdistribs numRdistribs = [ -1, 0, 0, 0, 0 ] ## [0 = no-op, 1 = # log-normal's, 2 = # Truncated Power Law's, 3 = # Exp's, # constant's] global warningFile warningFile = open("warningFileDFNGen.txt", 'w') global jobname jobname = self.jobname input_helper_methods = input_helper(params, minFracSize) ## ===================================================================== ## ## Mandatory Parameters ## ## ===================================================================== ## ## Each of these should be called in the order they are defined in to accomadate for dependecies def n_fam_ell(): """ Check the number of families of ellipses.""" global ellipseFams ## input_helper_methods.verifyNumValsIs(1, 'nFamEll') ellipseFams = input_helper_methods.verify_int( input_helper_methods.value_of('nFamEll', params), 'nFamEll', noNeg=True) if ellipseFams == 0: input_helper_methods.warning( "You have set the number of ellipse families to 0, outside user-defined ellipses, no ellipses will be generated.", params) def n_fam_rect(): """ Check the number of families of rectangles.""" global rectFams ## input_helper_methods.verifyNumValsIs(1, 'nFamRect') rectFams = input_helper_methods.verify_int( input_helper_methods.value_of('nFamRect', params), 'nFamRect', noNeg=True) if rectFams == 0: input_helper_methods.warning( "You have set the number of rectangle families to 0, outside user-defined rectangles, no rectangles will be generated.", params) def stop_condition(): """ Check the number of polygons if stopCondition is set to 1, else check the p32 target parameters.""" ## input_helper_methods.verifyNumValsIs(1, 'stopCondition') if input_helper_methods.verify_flag( input_helper_methods.value_of('stopCondition', params), 'stopCondition') == 0: n_poly() else: p32_targets() def check_no_dep_flags(): """ Check for dependency flags.""" for flagName in noDependancyFlags: input_helper_methods.verify_flag( input_helper_methods.value_of(flagName, params), flagName) def domain_size(): """ Check that domainSize has 3 non-zero values to define the size of each dimension (x,y,z) of the domain. """ errResult = input_helper_methods.verify_list( input_helper_methods.value_of('domainSize', params), 'domainSize', input_helper_methods.verify_float, desiredLength=3, noZeros=True, noNegs=True) if errResult != None: input_helper_methods.error("\"domainSize\" has defined {} value(s) but there must be 3 non-zero "\ "values to represent x, y, and z dimensions".format(-errResult)) def domain_size_increase(): """ Check the domain size increase parameters. """ errResult = input_helper_methods.verify_list( input_helper_methods.value_of('domainSizeIncrease', params), domain_size_increase, input_helper_methods.verify_float, desiredLength=3) if errResult != None: input_helper_methods.error("\"domainSizeIncrease\" has defined {} value(s) but there must be 3 non-zero "\ "values to represent extensions in the x, y, and z dimensions".format(-errResult)) for i, val in enumerate( input_helper_methods.value_of('domainSizeIncrease', params)): if val >= input_helper_methods.value_of('domainSize', params)[i] / 2: input_helper_methods.error( "\"domainSizeIncrease\" contains {} which is more than half of the domain's " "range in that dimension. Cannot change the domain's size by more than half of " "that dimension's value defined in \"domainSize\". This risks collapsing or " "doubling the domain.".format(val)) def num_of_layers(): """ Check the number of layers parameter.""" global numLayers numLayers = input_helper_methods.verify_int( input_helper_methods.value_of('numOfLayers', params), 'numOfLayers', noNeg=True) if numLayers > 0: if numLayers != len(params['layers']): input_helper_methods.error("\"layers\" has defined {} layers but \"numLayers\" was defined to "\ "be {}.".format(len(params['layers']), numLayers)) else: layers() def layers(): """ Check the layer parameters provided. """ halfZdomain = params['domainSize'][0][ 2] / 2.0 ## -index[2] becaue domainSize = [x,y,z] ## -center of z-domain at z = 0 so ## whole Zdomain is -zDomainSize to +zDomainSize for i, layer in enumerate(params['layers']): errResult = input_helper_methods.verify_list( layer, "layer #{}".format(i + 1), input_helper_methods.verify_float, desiredLength=2) if errResult != None: input_helper_methods.error("\"layers\" has defined layer #{} to have {} element(s) but each layer must "\ "have 2 elements, which define its upper and lower bounds".format(i+1, -errResult)) if params['layers'].count(layer) > 1: input_helper_methods.error( "\"layers\" has defined the same layer more than once.") minZ = layer[0] maxZ = layer[1] if maxZ <= minZ: input_helper_methods.error("\"layers\" has defined layer #{0} where zmin: {1} is "\ "greater than zmax {2}".format(i+1,minZ, maxZ)) if minZ <= -halfZdomain and maxZ <= -halfZdomain: input_helper_methods.error("\"layers\" has defined layer #{} to have both upper and lower bounds completely "\ "below the domain's z-dimensional range ({} to {}). At least one boundary must be within "\ "the domain's range. The domain's range is half of 3rd value in \"domainSize\" "\ "(z-dimension) in both positive and negative directions.".format(i+1, -halfZdomain, halfZdomain)) if minZ >= halfZdomain and maxZ >= halfZdomain: input_helper_methods.error("\"layers\" has defined layer #{} to have both upper and lower bounds completely "\ "above the domain's z-dimensional range ({} to {}). At least one boundary must be within "\ "the domain's range. The domain's range is half of 3rd value in \"domainSize\" "\ "(z-dimension) in both positive and negative directions.".format(i+1, -halfZdomain, halfZdomain)) def num_of_regions(): """ Check the number of regions parameter.""" global numRegions numRegions = input_helper_methods.verify_int( input_helper_methods.value_of('numOfRegions', params), 'numOfRegions', noNeg=True) if numRegions > 0: print("Number of Regions {0}".format(numRegions)) if numRegions != len(params['regions']): input_helper_methods.error("\"regions\" has defined {} regions but \"numOfRegions\" was defined to "\ "be {}.".format(len(params['regions']), numRegions)) else: regions() def regions(): """ Check the regions parameters provided. """ half_x_domain = params['domainSize'][0][ 0] / 2.0 ## -index[1] because domainSize = [x,y,z] half_y_domain = params['domainSize'][0][ 1] / 2.0 ## -index[1] because domainSize = [x,y,z] half_z_domain = params['domainSize'][0][ 2] / 2.0 ## -index[2] because domainSize = [x,y,z] ## -center of z-domain at z = 0 so ## whole Zdomain is -zDomainSize to +zDomainSize for i, region in enumerate(params['regions']): errResult = input_helper_methods.verify_list( region, "regions #{}".format(i + 1), input_helper_methods.verify_float, desiredLength=6) if errResult != None: input_helper_methods.error("\"regions\" has defined layer #{} to have {} element(s) but each region must "\ "have 6 elements, which define its upper and lower bounds".format(i+1, -errResult)) if params['regions'].count(regions) > 1: input_helper_methods.error( "\"regions\" has defined the same region more than once.") # min_x = regions[0] # max_x = regions[1] # min_y = regions[2] # max_y = regions[3] # min_z = regions[4] # max_z = regions[5] # X direction if region[1] <= region[0]: input_helper_methods.error("\"regions\" has defined region #{0} where xmin: {1} is "\ "greater than xmax {2}".format(i+1,region[0], region[1])) if region[0] <= -half_x_domain and region[1] <= -half_x_domain: input_helper_methods.error("\"regions\" has defined layer #{} to have both upper and lower bounds completely "\ "below the domain's x-dimensional range ({} to {}). At least one boundary must be within "\ "the domain's range. The domain's range is half of 1st value in \"domainSize\" "\ "(x-dimension) in both positive and negative directions.".format(i+1, -half_x_domain, half_x_domain)) if region[0] >= half_x_domain and region[1] >= half_x_domain: input_helper_methods.error("\"regions\" has defined layer #{} to have both upper and lower bounds completely "\ "above the domain's x-dimensional range ({} to {}). At least one boundary must be within "\ "the domain's range. The domain's range is half of 1st value in \"domainSize\" "\ "(x-dimension) in both positive and negative directions.".format(i+1, -half_x_domain, half_x_domain)) # Y direction if region[3] <= region[2]: input_helper_methods.error("\"regions\" has defined region #{0} where ymin: {1} is "\ "greater than ymax {2}".format(i+1,region[3], region[2])) if region[2] <= -half_y_domain and region[3] <= -half_y_domain: input_helper_methods.error("\"regions\" has defined layer #{} to have both upper and lower bounds completely "\ "below the domain's y-dimensional range ({} to {}). At least one boundary must be within "\ "the domain's range. The domain's range is half of 2nd value in \"domainSize\" "\ "(y-dimension) in both positive and negative directions.".format(i+1, -half_x_domain, half_x_domain)) if region[2] >= half_y_domain and region[3] >= half_y_domain: input_helper_methods.error("\"regions\" has defined layer #{} to have both upper and lower bounds completely "\ "above the domain's y-dimensional range ({} to {}). At least one boundary must be within "\ "the domain's range. The domain's range is half of 2nd value in \"domainSize\" "\ "(y-dimension) in both positive and negative directions.".format(i+1, -half_y_domain, half_y_domain)) # Z direction if region[5] <= region[4]: input_helper_methods.error("\"regions\" has defined region #{0} where zmin: {1} is "\ "greater than zmax {2}".format(i+1,region[5], region[4])) if region[4] <= -half_z_domain and region[5] <= -half_z_domain: input_helper_methods.error("\"regions\" has defined layer #{} to have both upper and lower bounds completely "\ "below the domain's z-dimensional range ({} to {}). At least one boundary must be within "\ "the domain's range. The domain's range is half of 3rd value in \"domainSize\" "\ "(z-dimension) in both positive and negative directions.".format(i+1, -half_z_domain, half_z_domain)) if region[4] >= half_z_domain and region[5] >= half_z_domain: input_helper_methods.error("\"regions\" has defined layer #{} to have both upper and lower bounds completely "\ "above the domain's z-dimensional range ({} to {}). At least one boundary must be within "\ "the domain's range. The domain's range is half of 3rd value in \"domainSize\" "\ "(z-dimension) in both positive and negative directions.".format(i+1, -half_z_domain, half_z_domain)) def disable_fram(): """ Verify the flag that indicates whether if FRAM is disabled. If FRAM is enabled, verify the value of h is valid. """ if input_helper_methods.verify_flag( input_helper_methods.value_of('disableFram', params), 'disableFram') == 0: h() else: input_helper_methods.warning("FRAM (feature rejection algorithm for meshing) is disabled. This means that"\ "dfnWorks will only run through fracture network generation (the code will stop before meshing)."\ "To run the full code change the disableFram option to 1") def seed(): """ Check the value of the seed used for pseudorandom number generation. """ val = input_helper_methods.verify_int(input_helper_methods.value_of( 'seed', params), 'seed', noNeg=True) if val == 0: input_helper_methods.warning("\"seed\" has been set to 0. Random generator will use current wall "\ "time so distribution's random selection will not be as repeatable. "\ "Use an integer greater than 0 for better repeatability.", params) params['seed'][0] = val def ignore_boundary_faces(): """ Check the value fo the ignoreBoundaryFaces flag. """ if input_helper_methods.verify_flag( input_helper_methods.value_of('ignoreBoundaryFaces', params), 'ignoreBoundaryFaces') == 0: boundary_faces() def rejects_per_fracture(): """ Check the value of the rejectsPerFracture int. """ val = input_helper_methods.verify_int(input_helper_methods.value_of( 'rejectsPerFracture', params), 'rejectsPerFracture', noNeg=True) if val == 0: val = 1 input_helper_methods.warning( "changing \"rejectsPerFracture\" from 0 to 1. Can't ensure 0 rejections.", params) params['rejectsPerFracture'][0] = val def fam_prob(): """ Check the list of family probabilites (the list of probabilities that a fracture is in each family). """ errResult = input_helper_methods.verify_list( input_helper_methods.value_of('famProb', params), 'famProb', input_helper_methods.verify_float, desiredLength=ellipseFams + rectFams, noZeros=True, noNegs=True) if errResult != None: print(errResult) input_helper_methods.error("\"famProb\" must have {} (nFamEll + nFamRect) non-zero elements,"\ "one for each family of ellipses and rectangles. {} probabiliies have "\ "been defined.".format(ellipseFams + rectFams, -errResult)) probList = [ float(x) for x in input_helper_methods.value_of('famProb', params) ] if sum(probList) != 1: input_helper_methods.scale(probList, warningFile) def user_defined(): """ Check the parameters for user-defined rectangles and ellipses. """ userEs = "userEllipsesOnOff" userRs = "userRectanglesOnOff" recByCoord = "userRecByCoord" ellByCoord = "userEllByCoord" polygonByCoord = "userPolygonByCoord" ePath = "UserEll_Input_File_Path" rPath = "UserRect_Input_File_Path" coordPath = "RectByCoord_Input_File_Path" ecoordPath = "EllByCoord_Input_File_Path" polycoordPath = "PolygonByCoord_Input_File_Path" invalid = "\"{}\" is not a valid path." if input_helper_methods.verify_flag( input_helper_methods.value_of(ellByCoord, params), ellByCoord) == 1: if not os.path.isfile( input_helper_methods.value_of(ecoordPath, params)): print('THIS PATH IS NOT A VALID FILE PATH: ', input_helper_methods.value_of(ecoordPath, params)) input_helper_methods.error(invalid.format(ecoordPath)) else: shutil.copy(input_helper_methods.value_of(ecoordPath, params), self.jobname) if input_helper_methods.verify_flag( input_helper_methods.value_of(userEs, params), userEs) == 1: if not os.path.isfile(input_helper_methods.value_of(ePath, params)): print('THIS PATH IS NOT A VALID FILE PATH: ', input_helper_methods.value_of(ePath, params)) input_helper_methods.error(invalid.format(ePath)) else: shutil.copy(input_helper_methods.value_of(ePath, params), self.jobname) if input_helper_methods.verify_flag( input_helper_methods.value_of(userRs, params), userRs) == 1: if not os.path.isfile(input_helper_methods.value_of(rPath, params)): print('THIS PATH IS NOT A VALID FILE PATH: ', input_helper_methods.value_of(rPath, params)) input_helper_methods.error(invalid.format(rPath)) else: shutil.copy(input_helper_methods.value_of(rPath, params), self.jobname) if input_helper_methods.verify_flag( input_helper_methods.value_of(recByCoord, params), recByCoord) == 1: if not os.path.isfile( input_helper_methods.value_of(coordPath, params)): print('THIS PATH IS NOT A VALID FILE PATH: ', input_helper_methods.value_of(coordPath, params)) input_helper_methods.error(invalid.format(coordPath)) else: shutil.copy(input_helper_methods.value_of(coordPath, params), self.jobname) def aperture(): """ Verify the int value used for aperture. """ apOption = input_helper_methods.verify_int( input_helper_methods.value_of('aperture', params), 'aperture') if apOption == 1: if input_helper_methods.verify_float(input_helper_methods.value_of( 'meanAperture', params), 'meanAperture', noNeg=True) == 0: input_helper_methods.error("\"meanAperture\" cannot be 0.") if input_helper_methods.verify_float(input_helper_methods.value_of( 'stdAperture', params), 'stdAperture', noNeg=True) == 0: input_helper_methods.error("\"stdAperture\" cannot be 0. If you wish to have a standard deviation "\ "of 0, use a constant aperture instead.") elif apOption == 2: input_helper_methods.verify_list(input_helper_methods.value_of( 'apertureFromTransmissivity', params), 'apertureFromTransmissivity', input_helper_methods.verify_float, desiredLength=2, noNegs=True) if input_helper_methods.value_of('apertureFromTransmissivity', params)[0] == 0: input_helper_methods.error( "\"apertureFromTransmissivity\"'s first value cannot be 0." ) if input_helper_methods.value_of('apertureFromTransmissivity', params)[1] == 0: input_helper_methods.warning( "\"apertureFromTransmissivity\"'s second value is 0, which will result in a constant aperature.", params) elif apOption == 3: if input_helper_methods.verify_float(input_helper_methods.value_of( 'constantAperture', params), 'constantAperture', noNeg=True) == 0: params['constantAperture'][0] = 1e-25 input_helper_methods.warning("\"constantAperture\" was set to 0 and has been changed "\ "to 1e-25 so fractures have non-zero thickness.", params) elif apOption == 4: input_helper_methods.verify_list(input_helper_methods.value_of( 'lengthCorrelatedAperture', params), 'lengthCorrelatedAperture', input_helper_methods.verify_float, desiredLength=2, noNegs=True) if input_helper_methods.value_of('lengthCorrelatedAperture', params)[0] == 0: input_helper_methods.error( "\"lengthCorrelatedAperture\"'s first value cannot be 0.") if input_helper_methods.value_of('lengthCorrelatedAperture', params)[1] == 0: input_helper_methods.warning( "\"lengthCorrelatedAperture\"'s second value is 0, which will result in a constant aperature.", params) else: input_helper_methods.error("\"aperture\" must only be option 1 (log-normal), 2 (from transmissivity), "\ "3 (constant), or 4 (length correlated).") def permeability(): """Verify the float used for permeability, if permOption is set to 1""" if input_helper_methods.verify_flag( input_helper_methods.value_of('permOption'), 'permOption') == 1: if input_helper_methods.verify_float( input_helper_methods.value_of('constantPermeability', params), 'constantPermeability') == 0: params['constantPermeability'][0] = 1e-25 input_helper_methods.warning("\"constantPermeability\" was set to 0 and has been changed "\ "to 1e-25 so fractures have non-zero permeability.", params) ## ========================================================================= ## ## Non-Mandatory Parameters ## ## ========================================================================= ## def n_poly(): """Verify the number of polygons integer.""" val = input_helper_methods.verify_int(input_helper_methods.value_of( 'nPoly', params), 'nPoly', noNeg=True) if val == 0: input_helper_methods.error("\"nPoly\" cannot be zero.") params['nPoly'][0] = val def p32_targets(): """Verify the p32 target parameters for ellipses and parameters.""" global ellipseFams, rectFams errResult = None if (ellipseFams == 0) else input_helper_methods.verify_list(input_helper_methods.value_of('e_p32Targets', params), 'e_p32Targets', \ input_helper_methods.verify_float, desiredLength = ellipseFams, noNegs=True, noZeros=True) if errResult != None: input_helper_methods.error("\"e_p32Targets\" has defined {} p32 values but there is(are) {} ellipse family(ies). "\ "Need one p32 value per ellipse family.".format(-errResult, ellipseFams)) errResult = None if (rectFams == 0) else input_helper_methods.verify_list(input_helper_methods.value_of('r_p32Targets', params), "r_p32Targets", \ input_helper_methods.verify_float, desiredLength = rectFams, noNegs=True, noZeros=True) if errResult != None: input_helper_methods.error("\"r_p32Targets\" has defined {} p32 value(s) but there is(are) {} rectangle "\ "family(ies). Need one p32 value per rectangle family)".format(-errResult, rectFams)) def f(theta, t, a, b): """Differential Equation Angle Theta as a function of arc length, see Hyman et al. 2014, SIAM J. Sci. Compu Equation 3.3""" return 1.0 / np.sqrt((a * np.sin(theta))**2 + (b * np.cos(theta)**2)) def h_shape_check(aspect, minRadius, num_points=4): """ Check that the arc length discretized ellipse is greater than 3*h """ # Major and Minor Axis of Ellipse ## aspect = 1.0 ## param r = minRadius ## TODO check > 3h a = aspect b = 1.0 # approximation of total arclength c = np.pi * (a + b) * (1.0 + (3.0 * ((a - b) / (a + b))**2) / (10. + np.sqrt(4. - 3. * ((a - b) / (a + b))**2))) #number of points n = num_points # expected arclength ds = c / n # array of steps steps = np.linspace(0, c, n + 1) # Numerically integrate arclength ODE theta = scipy.integrate.odeint(f, 0, steps, args=(a, b), rtol=10**-10) # Convert theta to x and y x = a * r * np.cos(theta) y = b * r * np.sin(theta) # Check Euclidean Distance between consecutive points h_min = 99999999999 for i in range(1, n): for j in range(i, n): if (i != j): h_current = np.sqrt((x[i] - x[j])**2 + (y[i] - y[j])**2) if (h_current < h_min): h_min = h_current return h_min def compare_pts_v_sh(prefix, hval): """ Check that the rectangles and ellipses generated will not involve features with length less than 3*h value used in FRAM. """ shape = "ellipse" if prefix == 'e' else "rectangle" aspectList = params[prefix + "aspect"][0] numPointsList = None if shape == "ellipse": numPointsList = params['enumPoints'][0] ## indicies for each list as we check for ellipse generating features less than h numLog = 0 numTPL = 0 numEXP = 0 numConst = 0 numAspect = 0 for distrib in params[prefix + 'distr'][0]: if distrib in [1, 2, 3, 4]: if distrib == 1: minRad = params[prefix + 'LogMin'][0][numLog] numLog += 1 elif distrib == 2: minRad = params[prefix + 'min'][0][numTPL] numTPL += 1 elif distrib == 3: minRad = params[prefix + 'ExpMin'][0][numEXP] numEXP += 1 elif distrib == 4: minRad = params[prefix + 'const'][0][numConst] numConst += 1 if shape == "ellipse": hmin = h_shape_check(float(aspectList[numAspect]), float(minRad), int(numPointsList[numAspect])) else: hmin = h_shape_check( float(aspectList[numAspect]), float(minRad) ) ## dont need numPoints for rectangle, default 4 if hmin < (3 * hval): input_helper_methods.error(shape + " family #{} has defined a shape with features too small for meshing. Increase the aspect "\ "ratio or minimum radius so that no 2 points of the polygon create a line of length less "\ "than 3h".format(numAspect+1)) numAspect += 1 ## this counts the family number def h(): """ Check the float value provided for h to be used in FRAM (the feautre rejection algorithm for meshing. """ global minFracSize val = input_helper_methods.verify_float(input_helper_methods.value_of( 'h', params), 'h', noNeg=True) if val == 0: input_helper_methods.error("\"h\" cannot be 0.") if minFracSize is None: minFracSize = 1 if val < minFracSize / 1000.0 and ellipseFams + rectFams > 0: ####### NOTE ----- future developers TODO, delete the ## "and ellipseFams + rectFams > 0" once you are also ## checking the userInput Files for minimums that could be ## "minFracSize". "minFracSize" is initialized to 99999999 so if no ## ellipse/rect fams are defined and the only polygons come from user ## Input, the warning message says the min Frac size is 99999999 ## since it never gets reset by one of the distribution minima. input_helper_methods.warning("\"h\" (length scale) is smaller than one 1000th of the minimum "\ "fracture size ({}). The generated mesh will be extremely fine and will likely be "\ "computationally exhausting to create. Computation may take longer than usual.".format(minFracSize)) if val > minFracSize / 10.0: input_helper_methods.warning("\"h\" (length scale) is greater than one 10th of the minimum "\ "fracture size ({}). The generated mesh will be very coarse and there will likely "\ "be a high rate of fracture rejection.".format(minFracSize)) if val > minFracSize: input_helper_methods.error("\"h\" (length scale) is greater than the minimum fracture size ({}). "\ "Choose a smaller value for h or a greater minimum fracure size."\ " ".format(minFracSize)) compare_pts_v_sh('e', val) compare_pts_v_sh('r', val) params['h'][0] = val def boundary_faces(): """Check that the boundaryFaceis list is a list of flags of length 6, one for each side of the domain ie {1, 1, 1, 0, 0, 1} represents --> {+x, -x, +y, -y, +z, -z} DFN only keeps clusters with connections to domain boundaries set to 1. """ errResult = input_helper_methods.verify_list( input_helper_methods.value_of('boundaryFaces', params), 'boundaryFaces', input_helper_methods.verify_flag, 6) if errResult != None: input_helper_methods.error("\"boundaryFaces\" must be a list of 6 flags (0 or 1), {} have(has) been defined. Each flag "\ "represents a side of the domain, {{+x, -x, +y, -y, +z, -z}}.".format(-errResult)) def enum_points(): """ Check the integer value of enumPoints for each ellipse family.""" errResult = input_helper_methods.verify_list( input_helper_methods.value_of('enumPoints', params), 'enumPoints', input_helper_methods.verify_int, desiredLength=ellipseFams, noZeros=True, noNegs=True) if errResult != None: input_helper_methods.error("\"enumPoints\" has defined {} value(s) but there is(are) {} families of ellipses. Please "\ "define one enumPoints value greater than 4 for each ellipse family.".format(-errResult, ellipseFams)) for val in input_helper_methods.value_of("enumPoints"): if val <= 4: input_helper_methods.error("\"enumPoints\" contains a value less than or equal to 4. If 4 points were intended, "\ "define this family as a rectangle family. No polygons with less than 4 verticies are acceptable.") ## ========================================================================= ## ## Generalized Mandatory Params ## ## ========================================================================= ## ### ### ### Prefix MUST be either 'e' or 'r' ### ### ### ### ============================================== ### def aspect(prefix): """ Check the aspect of the the rectangle or ellipse families. """ shape = "ellipse" if prefix == 'e' else "rectangle" numFamilies = ellipseFams if prefix == 'e' else rectFams paramName = prefix + "aspect" errResult = input_helper_methods.verify_list( input_helper_methods.value_of(paramName), paramName, input_helper_methods.verify_float, desiredLength=numFamilies, noZeros=True, noNegs=True) if errResult != None: input_helper_methods.error("\"{}\" has defined {} value(s) but there is(are) {} {} families. Please define one "\ "aspect ratio for each family.".format(paramName, -errResult, numFamilies, shape)) def angle_option(prefix): """ Check the angle option flag. """ paramName = prefix + "AngleOption" input_helper_methods.verify_flag( input_helper_methods.value_of(paramName), paramName) def layer(prefix): """ Check the number of layers. """ shape = "ellipse" if prefix == 'e' else "rectangle" numFamilies = ellipseFams if prefix == 'e' else rectFams paramName = prefix + "Layer" errResult = input_helper_methods.verify_list( input_helper_methods.value_of(paramName), paramName, input_helper_methods.verify_int, desiredLength=numFamilies) if errResult != None: input_helper_methods.error("\"{}\" has defined {} layer(s) but there is(are) {} {} families. "\ "Need one layer per {} family. Layers are numbered by the order they "\ "are defined in 'layers' parameter. Layer 0 is the whole domain."\ .format(paramName, -errResult, numFamilies, shape, shape)) for layer in input_helper_methods.value_of(paramName): if input_helper_methods.is_negative(int(layer)): input_helper_methods.error("\"{}\" contains a negative layer number. Only values from 0 to "\ "{} (numOfLayers) are accepted. Layer 0 corresponds to the entire"\ "domain.".format(paramName, numLayers)) if int(layer) > numLayers: input_helper_methods.error("\"{}\" contains value '{}' but only {} layer(s) is(are) defined. Make sure the "\ "layer numbers referenced here are found in that same position in \"layers\" "\ "parameter.".format(paramName, layer, numLayers)) def region(prefix): """ Check the number of region. """ shape = "ellipse" if prefix == 'e' else "rectangle" numFamilies = ellipseFams if prefix == 'e' else rectFams paramName = prefix + "Region" errResult = input_helper_methods.verify_list( input_helper_methods.value_of(paramName), paramName, input_helper_methods.verify_int, desiredLength=numFamilies) if errResult != None: input_helper_methods.error("\"{}\" has defined {} layer(s) but there is(are) {} {} families. "\ "Need one layer per {} family. Regions are numbered by the order they "\ "are defined in 'region' parameter. Layer 0 is the whole domain."\ .format(paramName, -errResult, numFamilies, shape, shape)) for region in input_helper_methods.value_of(paramName): if input_helper_methods.is_negative(int(region)): input_helper_methods.error("\"{}\" contains a negative layer number. Only values from 0 to "\ "{} (numOfRegions) are accepted. Layer 0 corresponds to the entire"\ "domain.".format(paramName, numRegions)) if int(region) > numRegions: input_helper_methods.error("\"{}\" contains value '{}' but only {} region(s) is(are) defined. Make sure the "\ "region numbers referenced here are found in that same position in \"region\" "\ "parameter.".format(paramName, region, numRegions)) def theta_phi_kappa(prefix): """ Check the angle parameters used for Fisher distributions """ shape = "ellipse" if prefix == 'e' else "rectangle" numFamilies = ellipseFams if prefix == 'e' else rectFams paramNames = [prefix + name for name in ["theta", "phi", "kappa"]] errString = "\"{}\" has defined {} angle(s) but there is(are) {} {} family(ies)."\ "Please defined one angle for each {} family." for param in paramNames: errResult = input_helper_methods.verify_list( input_helper_methods.value_of(param), param, input_helper_methods.verify_float, desiredLength=numFamilies) if errResult != None: input_helper_methods.error( errString.format(param, -errResult, numFamilies, shape, shape)) # ## ========================================================================= ## ## Main for I/O Checkin and Writing ## ## ========================================================================= ## ### # def check_i_oargs(ioPaths): # try: # ioPaths["input"] = sys.argv[1] # except IndexError: # input_helper_methods.error("Please provide an input file path as the first command line argument.\n"\ # " $ python3 inputParser.py [inputPath] [outputPath (Optional)]") # # try: # ioPaths["output"] = sys.argv[2] # except IndexError: # ioPaths["output"] = "polishedOutput.txt" # input_helper_methods.warning("No output path has been provided so output will be written to "\ # "\"polishedOutput.txt\" in your current working directory.", params) # def parse_input(): """ Parse each line of the input file. """ for line in inputIterator: line = input_helper_methods.extract_parameters( line, inputIterator) ## this strips comments if (line != "" and ":" in line): input_helper_methods.process_line(line, unfoundKeys, inputIterator, warningFile) needed = [unfound for unfound in unfoundKeys if unfound in mandatory] if needed != []: errString = "" for key in needed: errString += "\t\"" + key + "\"\n" input_helper_methods.error( "Missing the following mandatory parameters: \n{}".format( errString)) def verify_params(): """ Verify all of the parameters in the input file. """ distributions = distr_module.distr(params, numEdistribs, numRdistribs, minFracSize) firstPriority = [ n_fam_ell, n_fam_rect, stop_condition, domain_size, num_of_layers, num_of_regions, seed, domain_size_increase, ignore_boundary_faces, rejects_per_fracture, user_defined, input_helper_methods.check_fam_count, check_no_dep_flags, fam_prob ] generalized = [ layer, region, aspect, angle_option, theta_phi_kappa, distributions.beta_distribution, distributions.distr ] distribs = [ distributions.lognormal_dist, distributions.tpl_dist, distributions.exponential_dist, distributions.constant_dist ] checkLast = [disable_fram, aperture, permeability] for paramFunc in firstPriority: paramFunc() if rectFams > 0: for paramFunc in generalized: paramFunc('r') if ellipseFams > 0: enum_points() for paramFunc in generalized: paramFunc('e') for i, paramFunc in enumerate(distribs): if numEdistribs[i + 1] > 0: paramFunc( 'e' ) ## only call if there have been 1+ of a distrib defined if numRdistribs[i + 1] > 0: paramFunc( 'r') ## +1 for reason stated in list instantiation above for paramFunc in checkLast: paramFunc() def write_back(): """ Write the parameters from the verbose input file back to a simplified input file. """ for param in params: if param == 'layers': writer.write(param + ': ') for layer in params['layers']: writer.write( input_helper_methods.list_to_curly(str(layer)) + " ") writer.write('\n') elif param == 'regions': writer.write(param + ': ') for region in params['regions']: writer.write( input_helper_methods.list_to_curly(str(region)) + " ") writer.write('\n') elif type(input_helper_methods.value_of(param, writing=True)) is list: curl = input_helper_methods.list_to_curly( str(input_helper_methods.value_of(param, writing=True))) writer.write(param + ': ' + curl + '\n') else: writer.write( param + ': ' + str(input_helper_methods.value_of(param, writing=True)) + '\n') #print "--> Checking input files" try: if not os.path.exists(os.getcwd()): print("ERROR: cwd: ", os.getcwd(), " does not exist") if not os.path.exists(os.path.abspath(self.dfnGen_file)): print("ERROR: dfnGen input file path: ", os.path.abspath(self.dfnGen_file), " does not exist") print(os.path.abspath(self.dfnGen_file)) shutil.copy(os.path.abspath(self.dfnGen_file), os.getcwd()) except: sys.exit("Unable to copy dfnGen input file\n%s\nExiting" % self.dfnGen_file) ioPaths = {"input": "", "output": ""} try: ioPaths["input"] = self.dfnGen_file except IndexError: input_helper_methods.error("Please provide an input file path as the first command line argument.\n"\ " $ python3 inputParser.py [inputPath] [outputPath (Optional)]") try: ioPaths["output"] = self.jobname + '/' + self.dfnGen_file.split( '/')[-1][:-4] + '_clean.dat' ioPaths["output"] = os.path.abspath(ioPaths["output"]) print(ioPaths["output"]) except IndexError: ioPaths["output"] = "polishedOutput.txt" input_helper_methods.warning("No output path has been provided so output will be written to "\ "\"polishedOutput.txt\" in your current working directory.", params) try: reader = open(ioPaths["input"], 'r') except: input_helper_methods.error( "Check that the path of your input file is valid") try: writer = open(ioPaths["output"], 'w') except: input_helper_methods.error( "Check that the path of your output file is valid.") inputIterator = iter(reader) print('--> Checking input data') print('--> Input Data: ', ioPaths["input"]) print('--> Output File: ', ioPaths["output"]) parse_input() verify_params() write_back() print('--> Checking Input Data Complete')