def codeSuccessfullyRun(codeBlockName, folder, rerun): try: success = False xmlFile = getProgressFilenames(folder).xmlFile if rerun: try: # Open file for reading tree = ET.parse(xmlFile) root = tree.getroot() codeBlockNodes = root.findall('CodeBlock') except Exception: removeFile(xmlFile) else: codeBlockNames = [] for codeBlockNode in codeBlockNodes: names = codeBlockNode.findall('Name') for name in names: codeBlockNames.append(name.text) if codeBlockName in codeBlockNames: success = True if success: log.info('Skipping: ' + str(codeBlockName)) return success except Exception: log.warning('Could not check if code block was previously run') log.warning(traceback.format_exc())
def initProgress(folder, rerun): try: xmlFile = getProgressFilenames(folder).xmlFile if not rerun: removeFile(xmlFile) # Create file if it does not exist if not os.path.exists(xmlFile): root = ET.Element("data") tree = ET.ElementTree(root) tree.write(xmlFile, encoding="utf-8", xml_declaration=True) else: # Open file for reading tree = ET.parse(xmlFile) root = tree.getroot() # Write scratch GDB to XML file if not already present scratchGDBNode = root.find('ScratchGDB') if scratchGDBNode is None: scratchGDBNode = createXMLNode(root, 'ScratchGDB') scratchGDBNode.text = str(arcpy.env.scratchGDB) try: # Save the XML file tree.write(xmlFile, encoding='utf-8', xml_declaration=True) except Exception: log.warning("Problem saving XML file " + str(xmlFile)) raise except Exception: log.warning('Could not initialise progress.xml')
def checkNegValue(nameValue, value, nameSoil): # Checks if the output is negative and returns a warning if value < 0.0: warningMsg = str(nameValue) + " is negative for " + str(nameSoil) log.warning(warningMsg) warningMsg2 = "Please check the results for " + str(nameSoil) log.warning(warningMsg2)
def checkNegOutput(array, record): # Checks if the output is negative and returns a warning for output in array: if output < 0.0: warningFlag = 'Soil moisture value is negative for record ' + str( record) log.warning(warningFlag)
def createXMLNode(parent, name): try: newNode = ET.Element(name) parent.append(newNode) return newNode except Exception: log.warning("Could not create node " + name) raise
def checkBatjes(sand, silt, clay, carbon, carbContent, record): warningFlag = '' # log.info('DEBUG: checking for Batjes') if sand < 5.0: warningFlag = 'Sand less than 5' log.warning( 'Batjes (1996) requires sand content to be at least 5 percent, check record: ' + str(record)) if silt < 5.0: warningFlag = 'Silt less than 5' log.warning( 'Batjes (1996) requires silt content to be at least 5 percent, check record: ' + str(record)) if clay < 5.0: warningFlag = 'Clay less than 5' log.warning( 'Batjes (1996) requires clay content to be at least 5 percent, check record: ' + str(record)) if carbon < 0.1: warningFlag = 'Carbon less than 0.1' log.warning( 'Batjes (1996) requires carbon content to be at least 0.1 percent, check record: ' + str(record)) return warningFlag
def getProgressFilenames(folder): try: class Files: ''' Declare filenames here ''' def __init__(self): self.xmlFile = "progress.xml" return common.addPath(Files(), folder) except Exception: log.warning("Error occurred while generating filenames") raise
def checkValue(name, value, record): warningFlag = '' # log.info('DEBUG: checking ' + str(name) + ' is not negative or over 100') if value < 0.0: warningFlag = str(name) + ' is negative' log.warning( str(name) + ' is negative, please check record: ' + str(record)) if value > 100.0: warningFlag = str(name) + ' is over 100' log.warning( str(name) + ' is over 100, please check record: ' + str(record)) return warningFlag
def checkCarbon(carbon, carbContent, record): warningFlag = '' # log.info('DEBUG: checking if carbon is negative or over 100') if carbon < 0.0: warningFlag = 'Carbon negative' if carbContent == 'OC': msg = 'Organic carbon ' field = 'OC' elif carbContent == 'OM': msg = 'Organic matter ' field = 'OM' warningMsg1 = str(msg) + "content (percentage) is negative" log.warning(warningMsg1) warningMsg2 = "Please check the field " + str( field) + " in record " + str(record) log.warning(warningMsg2) ## ADD WARNING for carbon > 40 ## ADD ERROR for carbon > 100 if carbon > 100.0: warningFlag = 'OC or OM over 100' if carbContent == 'OC': msg = 'Organic carbon ' field = 'OC' elif carbContent == 'OM': msg = 'Organic matter ' field = 'OM' warningMsg1 = str( msg) + "content (percentage) is higher than 100 percent" log.warning(warningMsg1) warningMsg2 = "Please check the field " + str( field) + " in record " + str(record) log.warning(warningMsg2) return warningFlag
def pressureFields(outputFolder, inputShp, fieldFC, fieldSIC, fieldPWP): # Check PTF information PTFxml = os.path.join(outputFolder, "ptfinfo.xml") PTFOption = common.readXML(PTFxml, 'VGOption') PTFInfo = PTFdatabase.checkPTF(PTFOption) PTFType = PTFInfo.PTFType PTFPressures = PTFInfo.PTFPressures # Get OIDField OIDField = arcpy.Describe(inputShp).OIDFieldName fcArray = [] sicArray = [] pwpArray = [] # Check the field capacity field if common.CheckField(inputShp, fieldFC): with arcpy.da.SearchCursor(inputShp, [fieldFC, OIDField]) as searchCursor: for row in searchCursor: fc_kPa = row[0] if PTFType == 'vgPTF': if fc_kPa < 6 or fc_kPa > 33: log.warning("Field capacity for soil in row " + str(OIDField) + " should be between 6 to 33 kPa") fcArray.append(fc_kPa) elif PTFType == 'pointPTF': # Check if this pressure point is inside the array if fc_kPa in PTFPressures: fcArray.append(fc_kPa) else: log.error( "Pressure for field capacity NOT present in point-PTF pressures" ) log.error( "Cannot calculate water content at this pressure for field capacity" ) sys.exit() else: log.error("PTF type not recognised: " + str(PTFType)) else: log.error("Field for field capacity not found in input shapefle: " + str(fieldFC)) sys.exit() if fieldSIC is not None: if common.CheckField(inputShp, fieldSIC): with arcpy.da.SearchCursor(inputShp, [fieldSIC, OIDField]) as searchCursor: for row in searchCursor: sic_kPa = row[0] if PTFType == 'vgPTF': ## TODO: Put in a check for the stoma closure pressure ## TODO: Need to know what is a realistic range for the SIC presusre sicArray.append(sic_kPa) elif PTFType == 'pointPTF': # Check if this pressure point is inside the array if sic_kPa in PTFPressures: sicArray.append(sic_kPa) else: log.error( "Pressure for stoma closure due to water stress NOT present in point-PTF pressures" ) log.error( "Cannot calculate water content at this pressure for stoma closure due to water stress" ) sys.exit() else: log.error("PTF type not recognised: " + str(PTFType)) else: log.error( "Field for water stress-induced stomatal closure not found in input shapefle: " + str(fieldFC)) else: log.warning( "Field for water stress-induced stomatal closure not specified") log.warning("Using default value of 100 kPa") defaultSIC = 100.0 # Populate sicArray for i in range(0, len(fcArray)): sicArray.append(defaultSIC) if fieldPWP is not None: if common.CheckField(inputShp, fieldPWP): with arcpy.da.SearchCursor(inputShp, [fieldPWP, OIDField]) as searchCursor: for row in searchCursor: pwp_kPa = row[0] if PTFType == 'vgPTF': if pwp_kPa > 1500: log.warning( "Permanent wilting point for soil in row " + str(OIDField) + " exceeds 1500 kPa") ## ASK B: vg not valid for over 1500 kPa? log.warning( "The van Genuchten equation is not valid for pressures greater than 1500 kPa" ) pwpArray.append(pwp_kPa) elif PTFType == 'pointPTF': # Check if this pressure point is inside the array if pwp_kPa in pwp_kPa: pwpArray.append(pwp_kPa) else: log.error( "Pressure for permanent wilting point NOT present in point-PTF pressures" ) log.error( "Cannot calculate water content at this pressure for permanent wilting point" ) sys.exit() else: log.error("PTF type not recognised: " + str(PTFType)) else: log.error( "Field for permanent wilting point not found in input shapefle: " + str(fieldFC)) else: log.warning("Field for permanent wilting point not specified") log.warning("Using default value of 1500 kPa") defaultPWP = 1500.0 # Populate pwpArray for i in range(0, len(fcArray)): pwpArray.append(defaultPWP) # log.info('DEBUG: fcArray: ' + str(fcArray)) # log.info('DEBUG: sicArray: ' + str(sicArray)) # log.info('DEBUG: pwpArray: ' + str(pwpArray)) return fcArray, sicArray, pwpArray
def SaxtonRawls_2006_BC(outputShp, PTFOption, carbonConFactor, carbContent): log.info("Calculating Brooks-Corey using Saxton and Rawls (2006)") # Arrays to output warningArray = [] WC_resArray = [] WC_satArray = [] lambda_BCArray = [] hb_BCArray = [] K_satArray = [] # Get OID field OIDField = common.getOIDField(outputShp) # Requirements: sand, clay, and OM if carbContent == 'OC': reqFields = [OIDField, "Sand", "Clay", "OC", "LUCIname"] elif carbContent == 'OM': reqFields = [OIDField, "Sand", "Clay", "OM", "LUCIname"] carbonConFactor = 1.0 checks_PTFs.checkInputFields(reqFields, outputShp) # Retrieve info from input record = [] sandPerc = [] clayPerc = [] carbPerc = [] name = [] with arcpy.da.SearchCursor(outputShp, reqFields) as searchCursor: for row in searchCursor: objectID = row[0] sand = row[1] clay = row[2] carbon = row[3] recName = row[4] record.append(objectID) sandPerc.append(sand) clayPerc.append(clay) carbPerc.append(carbon) name.append(recName) for x in range(0, len(record)): # Data checks warningFlag = checks_PTFs.checkValue("Clay", clayPerc[x], record[x]) warningFlag = checks_PTFs.checkValue("Sand", sandPerc[x], record[x]) warningFlag = checks_PTFs.checkValue("Carbon", carbPerc[x], record[x]) warningArray.append(warningFlag) # Calculate values WC_residual = 0 WC_33tkPa = (-0.00251 * sandPerc[x]) + (0.00195 * clayPerc[x]) + (0.00011 * carbPerc[x]*float(carbonConFactor)) + (0.0000006 * sandPerc[x] * carbPerc[x]*float(carbonConFactor)) - (0.0000027 * clayPerc[x] * carbPerc[x]*float(carbonConFactor)) + (0.0000452 * sandPerc[x] * clayPerc[x]) + 0.299 WC_33kPa = (1.283 * (WC_33tkPa)**(2)) + (0.626 * (WC_33tkPa)) - 0.015 WC_sat_33tkPa = (0.00278 * sandPerc[x]) + (0.00034 * clayPerc[x]) + (0.00022 * carbPerc[x]*float(carbonConFactor)) - (0.0000018 * sandPerc[x] * carbPerc[x]*float(carbonConFactor)) - (0.0000027 * clayPerc[x] * carbPerc[x]*float(carbonConFactor)) - (0.0000584 * sandPerc[x] * clayPerc[x]) + 0.078 WC_sat_33kPa = 1.636 * WC_sat_33tkPa - 0.107 ## WC_0kPa is now WC_sat WC_sat = WC_33kPa + WC_sat_33kPa - (0.00097 * sandPerc[x]) + 0.043 WC_1500tkPa = (-0.00024 * sandPerc[x]) + (0.00487 * clayPerc[x]) + (0.00006 * carbPerc[x]*float(carbonConFactor)) + (0.0000005 * sandPerc[x] * carbPerc[x]*float(carbonConFactor)) - (0.0000013 * clayPerc[x] * carbPerc[x]*float(carbonConFactor)) + (0.0000068 * sandPerc[x] * clayPerc[x]) + 0.031 WC_1500kPa = 1.14 * WC_1500tkPa - 0.02 # Need checks on WC_33kPa and WC_1500kPa wcError = False if WC_33kPa < 0.0: log.warning('WARNING: water content at 33kPa is negative for ' + str(name[x])) log.warning('WARNING: Cannot calculate lambda, setting it to -9999 for error catching') wcError = True if WC_1500kPa < 0.0: log.warning('WARNING: Water content at 1500kPa is negative for ' + str(name[x])) log.warning('WARNING: Cannot calculate lambda, setting it to -9999 for error catching') wcError = True if wcError == True: lambda_BC = -9999 hb_BC = -9999 else: B_SR = (math.log(1500.0) - math.log(33.0)) / (math.log(WC_33kPa) - math.log(WC_1500kPa)) lambda_BC = 1.0 / float(B_SR) hbt_BC = - (0.2167 * sandPerc[x]) - (0.2793 * clayPerc[x]) - (81.97 * WC_sat_33kPa) + (0.7112 * sandPerc[x] * WC_sat_33kPa) + (0.0829 * clayPerc[x] * WC_sat_33kPa) + (0.001405 * sandPerc[x] * clayPerc[x]) + 27.16 hb_BC = hbt_BC + (0.02 * hbt_BC ** 2) - (0.113 * hbt_BC) - 0.7 # If there is a valid lambda value if lambda_BC != -9999: K_sat = 1930.0 * ((WC_sat - WC_33kPa)**(3 - lambda_BC)) else: # If not valid, set K_sat to -9999 K_sat = -9999 WC_resArray.append(WC_residual) WC_satArray.append(WC_sat) lambda_BCArray.append(lambda_BC) hb_BCArray.append(hb_BC) K_satArray.append(K_sat) # Write K_sat to the output shapefile arcpy.AddField_management(outputShp, "K_sat", "DOUBLE", 10, 6) recordNum = 0 with arcpy.da.UpdateCursor(outputShp, "K_sat") as cursor: for row in cursor: row[0] = K_satArray[recordNum] cursor.updateRow(row) recordNum += 1 return warningArray, WC_resArray, WC_satArray, lambda_BCArray, hb_BCArray
def plotPTF(outputFolder, outputShp, PTFOption, nameArray, results): # For plotting point PTFs import matplotlib.pyplot as plt import numpy as np PTFInfo = PTFdatabase.checkPTF(PTFOption) PTFPressures = PTFInfo.PTFPressures PTFUnit = PTFInfo.PTFUnit # Remove warning results.pop(0) waterContents = [] # Rearrange arrays for j in range(0, len(nameArray)): WC = [] for i in range(0, len(PTFPressures)): water = results[i][j] WC.append(water) waterContents.append(WC) # log.info('DEBUG: waterContents: ') # log.info(waterContents) PTFInfo = PTFdatabase.checkPTF(PTFOption) WCheadings = PTFInfo.PTFFields WCheadings.pop(0) # remove warning for j in range(0, len(waterContents)): WC = waterContents[j] firstWCName = WCheadings[0] firstWCVal = WC[0] for i in range(1, len(WC)): if WC[i] > firstWCVal: log.warning('Water content in field ' + str(WCheadings[i]) + ' is higher than pressure at lowest water content (' + str(firstWCName) + ')') log.warning('Check this soil: ' + str(nameArray[j])) # Get units for plot unitPlots = common.getInputValue(outputFolder, "Pressure_units_plot") # Get critical thresholds fcValue = common.getInputValue(outputFolder, "FieldCapacity") sicValue = common.getInputValue(outputFolder, "SIC") pwpValue = common.getInputValue(outputFolder, "PWP") # Set up pressure vector psiArray = np.array(PTFPressures) psi_kPa = psiArray.astype(np.float) if unitPlots == 'kPa': psi_plot = psi_kPa fc_plot = float(fcValue) * -1.0 sic_plot = float(sicValue) * -1.0 pwp_plot = float(pwpValue) * -1.0 xLimits = [-1600.0, 0.1] elif unitPlots == 'cm': psi_plot = 10.0 * psi_kPa fc_plot = float(fcValue) * -10.0 sic_plot = float(sicValue) * -10.0 pwp_plot = float(pwpValue) * -10.0 xLimits = [-16000.0, 0.1] elif unitPlots == 'm': psi_plot = 0.1 * psi_kPa fc_plot = float(fcValue) * -0.1 sic_plot = float(sicValue) * -0.1 pwp_plot = float(pwpValue) * -0.1 xLimits = [-160.0, 0.1] # Convert psi_plot to negative for plotting purposes psi_neg = -1.0 * psi_plot for i in range(0, len(nameArray)): outName = 'pointPTF_' + str(nameArray[i]) + '.png' outPath = os.path.join(outputFolder, outName) title = 'Point-PTF plot for ' + str(nameArray[i]) plt.scatter(psi_neg, waterContents[i], label=str(nameArray[i]), c='b') plt.xscale('symlog') plt.title(title) plt.xlabel('log Pressure (' + str(unitPlots) + ')') plt.ylabel('Volumetric water content') plt.xlim(xLimits) plt.axvline(x=fc_plot, color='g', linestyle='dashed', label='FC') plt.axvline(x=sic_plot, color='m', linestyle='dashed', label='SIC') plt.axvline(x=pwp_plot, color='r', linestyle='dashed', label='PWP') plt.legend(loc="upper left") plt.savefig(outPath, transparent=False) plt.close() log.info('Plot created for soil ' + str(nameArray[i]))
def checkSSC(sand, silt, clay, record): warningFlag = '' # log.info('DEBUG: checking sand, silt, clay (negative and sum)') if sand < 0.0: warningFlag = 'Sand is negative' log.warning('Sand content is negative') log.warning('Please check record: ' + str(record)) if silt < 0.0: warningFlag = 'Silt is negative' log.warning('Silt content is negative') log.warning('Please check record: ' + str(record)) if clay < 0.0: warningFlag = 'Clay is negative' log.warning('Clay content is negative') log.warning('Please check record: ' + str(record)) SSC = sand + silt + clay # log.info('DEBUG: SSC: ' + str(SSC)) if SSC < 99.0: warningFlag = 'SSC less than 99' log.warning('Sand, silt, clay sum up to less than 99 percent') log.warning('Please check record: ' + str(record)) if SSC > 101.0: warningFlag = 'SSC more than 101' log.warning('Sand, silt, clay sum up to more than 100') log.warning('Please check record: ' + str(record)) return warningFlag
def function(params): try: pText = common.paramsAsText(params) # Get inputs runSystemChecks = common.strToBool(pText[1]) outputFolder = pText[2] inputShapefile = pText[3] PTFChoice = pText[4] BCPressures = pText[5] fcVal = pText[6] sicVal = pText[7] pwpVal = pText[8] carbonContent = pText[9] carbonConFactor = pText[10] unitsPlot = pText[11] axisChoice = pText[12] # Create output folder if not os.path.exists(outputFolder): os.mkdir(outputFolder) # System checks and setup if runSystemChecks: common.runSystemChecks(outputFolder) # Set up logging output to file log.setupLogging(outputFolder) # Write input params to XML common.writeParamsToXML(params, outputFolder) if PTFChoice == 'Cosby et al. (1984) - Sand and Clay': PTFOption = 'Cosby_1984_SandC_BC' elif PTFChoice == 'Cosby et al. (1984) - Sand, Silt and Clay': PTFOption = 'Cosby_1984_SSC_BC' elif PTFChoice == 'Rawls and Brakensiek (1985)': PTFOption = 'RawlsBrakensiek_1985_BC' log.warning("Rawls and Brakensiek (1985) requires water content at saturation") log.warning("Please ensure the WC_sat field is present in the shapefile") elif PTFChoice == 'Campbell and Shiozawa (1992)': PTFOption = 'CampbellShiozawa_1992_BC' log.warning("Campbell and Shiozava (1992) requires water content at saturation") log.warning("Please ensure the WC_sat field is present in the shapefile") elif PTFChoice == 'Saxton et al. (1986)': PTFOption = 'Saxton_1986_BC' elif PTFChoice == 'Saxton and Rawls (2006)': PTFOption = 'SaxtonRawls_2006_BC' else: log.error('Choice for Brooks-Corey calculation not recognised') sys.exit() # Set carbon content choice if carbonContent == 'Organic carbon': carbContent = 'OC' elif carbonContent == 'Organic matter': carbContent = 'OM' else: log.error('Invalid carbon content option') sys.exit() # Unpack 'BC pressure heads' parameter if BCPressures is None: BCPressArray = [] else: BCPressArray = BCPressures.split(' ') # Pull out PTFinfo PTFInfo = PTFdatabase.checkPTF(PTFOption) PTFType = PTFInfo.PTFType PTFUnit = PTFInfo.PTFUnit PTFOut = [("BCOption", PTFOption), ("PTFType", PTFType), ("UserUnitPlot", unitsPlot), ("carbContent", carbContent)] # Write to XML file PTFXML = os.path.join(outputFolder, "ptfinfo.xml") common.writeXML(PTFXML, PTFOut) # Call Brooks-Corey function brooks_corey.function(outputFolder, inputShapefile, PTFOption, BCPressArray, fcVal, sicVal, pwpVal, carbContent, carbonConFactor) # Set output filename for display BCOut = os.path.join(outputFolder, "BrooksCorey.shp") arcpy.SetParameter(13, BCOut) log.info("Brooks-Corey operations completed successfully") except Exception: log.exception("Brooks-Corey tool failed") raise
def plotBrooksCorey(outputFolder, WC_resArray, WC_satArray, hbArray, lambdaArray, nameArray, fcValue, sicValue, pwpValue): # Create Brooks-Corey plots import matplotlib.pyplot as plt import numpy as np # Check what unit the user wants to output PTFUnit = common.getInputValue(outputFolder, 'Pressure_units_plot') # Check what axis was chosen AxisChoice = common.getInputValue(outputFolder, 'Plot_axis') # Check for any soils that we were not able to calculate BC parameters for errors = [] for i in range(0, len(lambdaArray)): if lambdaArray[i] == -9999: log.warning('Invalid lambda found for ' + str(nameArray[i])) errors.append(i) # Define output folder for CSVs outFolder = os.path.join(outputFolder, 'BC_waterContents') if not os.path.exists(outFolder): os.mkdir(outFolder) ################################ ### Plot 0: individual plots ### ################################ # Plot 0: pressure on the y-axis and water content on the x-axis for i in [x for x in range(0, len(nameArray)) if x not in errors]: outName = 'bc_' + str(nameArray[i]) + '.png' outPath = os.path.join(outputFolder, outName) title = 'Brooks-Corey plot for ' + str(nameArray[i]) # Set pressure vector psi_kPa = np.linspace(0.0, 1500.0, 1501) # Calculate WC over that pressure vector bc_WC = calcBrooksCoreyFXN(psi_kPa, hbArray[i], WC_resArray[i], WC_satArray[i], lambdaArray[i]) common.writeWCCSV(outFolder, nameArray[i], psi_kPa, bc_WC, 'Pressures_kPa', 'WaterContents') ## Figure out what to do about multipliers if PTFUnit == 'kPa': pressureUnit = 'kPa' psi_plot = psi_kPa fc_plot = float(fcValue) * -1.0 sic_plot = float(sicValue) * -1.0 pwp_plot = float(pwpValue) * -1.0 elif PTFUnit == 'cm': pressureUnit = 'cm' psi_plot = 10.0 * psi_kPa fc_plot = float(fcValue) * -10.0 sic_plot = float(sicValue) * -10.0 pwp_plot = float(pwpValue) * -10.0 elif PTFUnit == 'm': pressureUnit = 'm' psi_plot = 0.1 * psi_kPa fc_plot = float(fcValue) * -0.1 sic_plot = float(sicValue) * -0.1 pwp_plot = float(pwpValue) * -0.1 # Convert psi_plot to negative for plotting psi_neg = -1.0 * psi_plot if AxisChoice == 'Y-axis': plt.plot(psi_neg, bc_WC, label=str(nameArray[i])) plt.xscale('symlog') plt.axvline(x=fc_plot, color='g', linestyle='dashed', label='FC') plt.axvline(x=sic_plot, color='m', linestyle='dashed', label='SIC') plt.axvline(x=pwp_plot, color='r', linestyle='dashed', label='PWP') plt.legend(loc="best") plt.title(title) plt.xlabel('Pressure (' + str(pressureUnit) + ')') plt.ylabel('Volumetric water content') plt.savefig(outPath, transparent=False) plt.close() log.info('Plot created for soil ' + str(nameArray[i])) elif AxisChoice == 'X-axis': plt.plot(bc_WC, psi_neg, label=str(nameArray[i])) plt.yscale('symlog') plt.axhline(y=fc_plot, color='g', linestyle='dashed', label='FC') plt.axhline(y=sic_plot, color='m', linestyle='dashed', label='SIC') plt.axhline(y=pwp_plot, color='r', linestyle='dashed', label='PWP') plt.legend(loc="best") plt.title(title) plt.ylabel('Pressure (' + str(pressureUnit) + ')') plt.xlabel('Volumetric water content') plt.savefig(outPath, transparent=False) plt.close() log.info('Plot created for soil ' + str(nameArray[i])) else: log.error( 'Invalid choice for axis plotting, please select Y-axis or X-axis' ) sys.exit() ######################### ### Plot 1: all soils ### ######################### outPath = os.path.join(outputFolder, 'plotBC_logPressure.png') title = 'Brooks-Corey plots of ' + str( len(nameArray)) + ' soils (log scale)' # Define pressure vector psi_kPa = np.linspace(0.0, 1500.0, 1501) for i in [x for x in range(0, len(nameArray)) if x not in errors]: # Calculate WC over pressure vector bc_WC = calcBrooksCoreyFXN(psi_kPa, hbArray[i], WC_resArray[i], WC_satArray[i], lambdaArray[i]) if PTFUnit == 'kPa': pressureUnit = 'kPa' psi_plot = psi_kPa elif PTFUnit == 'cm': pressureUnit = 'cm' psi_plot = 10.0 * psi_kPa elif PTFUnit == 'm': pressureUnit = 'm' psi_plot = 0.1 * psi_kPa # Convert psi to negative for plotting purposes psi_neg = -1.0 * psi_plot plt.plot(psi_neg, bc_WC, label=str(nameArray[i])) if AxisChoice == 'Y-axis': plt.xscale('symlog') plt.title(title) plt.axvline(x=fc_plot, color='g', linestyle='dashed', label='FC') plt.axvline(x=sic_plot, color='m', linestyle='dashed', label='SIC') plt.axvline(x=pwp_plot, color='r', linestyle='dashed', label='PWP') plt.ylabel('Water content') plt.xlabel('Pressure (' + str(pressureUnit) + ')') plt.legend(ncol=2, fontsize=12, loc="best") plt.savefig(outPath, transparent=False) plt.close() log.info('Plot created with water content on the y-axis') elif AxisChoice == 'X-axis': plt.yscale('symlog') plt.title(title) plt.axhline(y=fc_plot, color='g', linestyle='dashed', label='FC') plt.axhline(y=sic_plot, color='m', linestyle='dashed', label='SIC') plt.axhline(y=pwp_plot, color='r', linestyle='dashed', label='PWP') plt.xlabel('Water content') plt.ylabel('Pressure (' + str(pressureUnit) + ')') plt.legend(ncol=2, fontsize=12, loc="best") plt.savefig(outPath, transparent=False) plt.close() log.info('Plot created with water content on the y-axis') else: log.error( 'Invalid choice for axis plotting, please select Y-axis or X-axis') sys.exit()
def function(outputFolder, inputShp, PTFOption, fcVal, sicVal, pwpVal, carbContent, carbonConFactor): try: # Set temporary variables prefix = os.path.join(arcpy.env.scratchGDB, "soil_") # Set output filename outputShp = os.path.join(outputFolder, "soil_point_ptf.shp") # Copy the input shapefile to the output folder arcpy.CopyFeatures_management(inputShp, outputShp) #################################### ### Calculate the water contents ### #################################### # Get the nameArray nameArray = [] with arcpy.da.SearchCursor(outputShp, "LUCIname") as searchCursor: for row in searchCursor: name = row[0] nameArray.append(name) # Get PTF fields PTFxml = os.path.join(outputFolder, "ptfinfo.xml") PTFFields = common.readXML(PTFxml, 'PTFFields') PTFPressures = common.readXML(PTFxml, 'PTFPressures') PTFUnit = common.readXML(PTFxml, 'PTFUnit') # Call point-PTF here depending on PTFOption if PTFOption == "Nguyen_2014": results = point_PTFs.Nguyen_2014(outputFolder, outputShp, carbonConFactor, carbContent) elif PTFOption == "Adhikary_2008": results = point_PTFs.Adhikary_2008(outputFolder, outputShp) elif PTFOption == "Rawls_1982": results = point_PTFs.Rawls_1982(outputFolder, outputShp, carbonConFactor, carbContent) elif PTFOption == "Hall_1977_top": results = point_PTFs.Hall_1977_top(outputFolder, outputShp, carbonConFactor, carbContent) elif PTFOption == "Hall_1977_sub": results = point_PTFs.Hall_1977_sub(outputFolder, outputShp, carbonConFactor, carbContent) elif PTFOption == "GuptaLarson_1979": results = point_PTFs.GuptaLarson_1979(outputFolder, outputShp, carbonConFactor, carbContent) elif PTFOption == "Batjes_1996": results = point_PTFs.Batjes_1996(outputFolder, outputShp, carbonConFactor, carbContent) elif PTFOption == "SaxtonRawls_2006": results = point_PTFs.SaxtonRawls_2006(outputFolder, outputShp, carbonConFactor, carbContent) elif PTFOption == "Pidgeon_1972": results = point_PTFs.Pidgeon_1972(outputFolder, outputShp, carbonConFactor, carbContent) elif str(PTFOption[0:8]) == "Lal_1978": results = point_PTFs.Lal_1978(outputFolder, outputShp, PTFOption) elif PTFOption == "AinaPeriaswamy_1985": results = point_PTFs.AinaPeriaswamy_1985(outputFolder, outputShp) elif PTFOption == "ManriqueJones_1991": results = point_PTFs.ManriqueJones_1991(outputFolder, outputShp) elif PTFOption == "vanDenBerg_1997": results = point_PTFs.vanDenBerg_1997(outputFolder, outputShp, carbonConFactor, carbContent) elif PTFOption == "TomasellaHodnett_1998": results = point_PTFs.TomasellaHodnett_1998(outputFolder, outputShp, carbonConFactor, carbContent) elif PTFOption == "Reichert_2009_OM": results = point_PTFs.Reichert_2009_OM(outputFolder, outputShp, carbonConFactor, carbContent) elif PTFOption == "Reichert_2009": results = point_PTFs.Reichert_2009(outputFolder, outputShp) elif PTFOption == "Botula_2013": results = point_PTFs.Botula_2013(outputFolder, outputShp) elif PTFOption == "ShwethaVarija_2013": results = point_PTFs.ShwethaVarija_2013(outputFolder, outputShp) elif PTFOption == "Dashtaki_2010_point": results = point_PTFs.Dashtaki_2010(outputFolder, outputShp) elif PTFOption == "Santra_2018_OC": results = point_PTFs.Santra_2018_OC(outputFolder, outputShp, carbonConFactor, carbContent) elif PTFOption == "Santra_2018": results = point_PTFs.Santra_2018(outputFolder, outputShp) else: log.error("PTF option not recognised") sys.exit() # Plots plots.plotPTF(outputFolder, outputShp, PTFOption, nameArray, results) ###################################################### ### Calculate water content at critical thresholds ### ###################################################### satStatus = False fcStatus = False sicStatus = False pwpStatus = False wc_satCalc = [] wc_fcCalc = [] wc_sicCalc = [] wc_pwpCalc = [] if PTFOption == "Reichert_2009_OM": log.info( 'For Reichert et al. (2009) - Sand, silt, clay, OM, BD saturation is at 6kPa' ) satField = "WC_6kPa" else: satField = "WC_0" + str(PTFUnit) fcField = "WC_" + str(int(fcVal)) + str(PTFUnit) sicField = "WC_" + str(int(sicVal)) + str(PTFUnit) pwpField = "WC_" + str(int(pwpVal)) + str(PTFUnit) wcFields = [] wcArrays = [] if satField in PTFFields: # Saturation set to 0kPa # PTFs with 0kPa: ## Saxton_1986, Batjes_1996, SaxtonRawls_2006 ## Lal_1978_Group1, Lal_1978_Group2 ## TomasellaHodnett_1998 log.info('Field with WC at saturation found!') with arcpy.da.SearchCursor(outputShp, satField) as searchCursor: for row in searchCursor: wc_sat = row[0] if wc_sat > 1.0: log.warning('Water content at saturation over 1.0') wc_satCalc.append(wc_sat) satStatus = True wcFields.append("wc_satCalc") wcArrays.append(wc_satCalc) # Add sat field to output shapefile arcpy.AddField_management(outputShp, "wc_satCalc", "DOUBLE", 10, 6) recordNum = 0 with arcpy.da.UpdateCursor(outputShp, "wc_satCalc") as cursor: for row in cursor: row[0] = wc_satCalc[recordNum] cursor.updateRow(row) recordNum += 1 else: log.warning('Field with WC at saturation not found') satStatus = False if fcField in PTFFields: log.info('Field with WC at field capacity found!') with arcpy.da.SearchCursor(outputShp, fcField) as searchCursor: for row in searchCursor: wc_fc = row[0] wc_fcCalc.append(wc_fc) fcStatus = True wcFields.append("wc_fcCalc") wcArrays.append(wc_fcCalc) # Add FC field to output shapefile arcpy.AddField_management(outputShp, "wc_fcCalc", "DOUBLE", 10, 6) recordNum = 0 with arcpy.da.UpdateCursor(outputShp, "wc_fcCalc") as cursor: for row in cursor: row[0] = wc_fcCalc[recordNum] cursor.updateRow(row) recordNum += 1 else: log.warning('Field with WC at field capacity not found') fcStatus = False if sicField in PTFFields: log.info( 'Field with WC at water stress-induced stomatal closure found!' ) with arcpy.da.SearchCursor(outputShp, sicField) as searchCursor: for row in searchCursor: wc_sic = row[0] wc_sicCalc.append(wc_sic) sicStatus = True wcFields.append("wc_sicCalc") wcArrays.append(wc_sicCalc) # Add sic field to output shapefile arcpy.AddField_management(outputShp, "wc_sicCalc", "DOUBLE", 10, 6) recordNum = 0 with arcpy.da.UpdateCursor(outputShp, "wc_sicCalc") as cursor: for row in cursor: row[0] = wc_sicCalc[recordNum] cursor.updateRow(row) recordNum += 1 else: log.warning( 'Field with WC at water stress-induced stomatal closure not found' ) sicStatus = False if pwpField in PTFFields: log.info('Field with WC at permanent wilting point found!') with arcpy.da.SearchCursor(outputShp, pwpField) as searchCursor: for row in searchCursor: wc_pwp = row[0] if wc_pwp < 0.01: log.warning( 'WARNING: Water content at PWP is below 0.01') elif wc_pwp < 0.05: log.warning('Water content at PWP is below 0.05') wc_pwpCalc.append(wc_pwp) pwpStatus = True wcFields.append("wc_pwpCalc") wcArrays.append(wc_pwpCalc) # Add pwp field to output shapefile arcpy.AddField_management(outputShp, "wc_pwpCalc", "DOUBLE", 10, 6) recordNum = 0 with arcpy.da.UpdateCursor(outputShp, "wc_pwpCalc") as cursor: for row in cursor: row[0] = wc_pwpCalc[recordNum] cursor.updateRow(row) recordNum += 1 else: log.warning('Field with WC at permanent wilting point not found') pwpStatus = False drainWater = [] PAW = [] RAW = [] NRAW = [] if satStatus == True and fcStatus == True: # drainWater = wc_sat - wc_fc drainWater = point_PTFs.calcWaterContent(wc_satCalc, wc_fcCalc, 'drainable water', nameArray) log.info('Drainable water calculated') wcFields.append("wc_DW") wcArrays.append(drainWater) # Add DW field to output shapefile arcpy.AddField_management(outputShp, "wc_DW", "DOUBLE", 10, 6) recordNum = 0 with arcpy.da.UpdateCursor(outputShp, "wc_DW") as cursor: for row in cursor: row[0] = drainWater[recordNum] cursor.updateRow(row) recordNum += 1 if fcStatus == True and pwpStatus == True: # PAW = wc_fc - wc_pwp PAW = point_PTFs.calcWaterContent(wc_fcCalc, wc_pwpCalc, 'plant available water', nameArray) log.info('Plant available water calculated') wcFields.append("wc_PAW") wcArrays.append(PAW) # Add PAW field to output shapefile arcpy.AddField_management(outputShp, "wc_PAW", "DOUBLE", 10, 6) recordNum = 0 with arcpy.da.UpdateCursor(outputShp, "wc_PAW") as cursor: for row in cursor: row[0] = PAW[recordNum] cursor.updateRow(row) recordNum += 1 pawStatus = True if fcStatus == True and sicStatus == True: # readilyAvailWater = wc_fc - wc_sic RAW = point_PTFs.calcWaterContent(wc_fcCalc, wc_sicCalc, 'readily available water', nameArray) log.info('Readily available water calculated') wcFields.append("wc_RAW") wcArrays.append(RAW) # Add wc_RAW field to output shapefile arcpy.AddField_management(outputShp, "wc_RAW", "DOUBLE", 10, 6) recordNum = 0 with arcpy.da.UpdateCursor(outputShp, "wc_RAW") as cursor: for row in cursor: row[0] = RAW[recordNum] cursor.updateRow(row) recordNum += 1 elif pawStatus == True: # If PAW exists, get RAW = 0.5 * RAW PAW = point_PTFs.calcWaterContent(wc_fcCalc, wc_pwpCalc, 'plant available water', nameArray) RAW = [(float(i) * 0.5) for i in PAW] log.info('Readily available water calculated based on PAW') wcFields.append("wc_RAW") wcArrays.append(RAW) # Add wc_RAW field to output shapefile arcpy.AddField_management(outputShp, "wc_RAW", "DOUBLE", 10, 6) recordNum = 0 with arcpy.da.UpdateCursor(outputShp, "wc_RAW") as cursor: for row in cursor: row[0] = RAW[recordNum] cursor.updateRow(row) recordNum += 1 else: log.info('Readily available water not calculated') if sicStatus == True and pwpStatus == True: # notRAW = wc_sic - wc_pwp NRAW = point_PTFs.calcWaterContent(wc_sicCalc, wc_pwpCalc, 'not readily available water', nameArray) log.info('Not readily available water calculated') wcFields.append("wc_NRAW") wcArrays.append(NRAW) # Add sat field to output shapefile arcpy.AddField_management(outputShp, "wc_NRAW", "DOUBLE", 10, 6) recordNum = 0 with arcpy.da.UpdateCursor(outputShp, "wc_NRAW") as cursor: for row in cursor: row[0] = NRAW[recordNum] cursor.updateRow(row) recordNum += 1 log.info( 'Water contents at critical thresholds written to output shapefile' ) except Exception: arcpy.AddError("Point-PTFs function failed") raise finally: # Remove feature layers from memory try: for lyr in common.listFeatureLayers(locals()): arcpy.Delete_management(locals()[lyr]) exec(lyr + ' = None') in locals() except Exception: pass
def function(outputFolder, inputShp, PTFOption, BCPressArray, fcVal, sicVal, pwpVal, carbContent, carbonConFactor): try: # Set temporary variables prefix = os.path.join(arcpy.env.scratchGDB, "bc_") tempSoils = prefix + "tempSoils" # Set output filename outputShp = os.path.join(outputFolder, "BrooksCorey.shp") # Copy the input shapefile to the output folder arcpy.CopyFeatures_management(inputShp, outputShp) # Get the nameArray nameArray = [] with arcpy.da.SearchCursor(outputShp, "LUCIname") as searchCursor: for row in searchCursor: name = row[0] nameArray.append(name) # PTFs should return: WC_res, WC_sat, lambda_BC, hb_BC if PTFOption == "Cosby_1984_SandC_BC": warning, WC_res, WC_sat, lambda_BC, hb_BC = bc_PTFs.Cosby_1984_SandC_BC( outputShp, PTFOption) elif PTFOption == "Cosby_1984_SSC_BC": warning, WC_res, WC_sat, lambda_BC, hb_BC = bc_PTFs.Cosby_1984_SSC_BC( outputShp, PTFOption) elif PTFOption == "RawlsBrakensiek_1985_BC": warning, WC_res, WC_sat, lambda_BC, hb_BC = bc_PTFs.RawlsBrakensiek_1985_BC( outputShp, PTFOption) elif PTFOption == "CampbellShiozawa_1992_BC": warning, WC_res, WC_sat, lambda_BC, hb_BC = bc_PTFs.CampbellShiozawa_1992_BC( outputShp, PTFOption) elif PTFOption == "Saxton_1986_BC": warning, WC_res, WC_sat, lambda_BC, hb_BC = bc_PTFs.Saxton_1986_BC( outputShp, PTFOption) elif PTFOption == "SaxtonRawls_2006_BC": warning, WC_res, WC_sat, lambda_BC, hb_BC = bc_PTFs.SaxtonRawls_2006_BC( outputShp, PTFOption, carbonConFactor, carbContent) else: log.error("Brooks-Corey option not recognised: " + str(PTFOption)) sys.exit() # Write to shapefile brooksCorey.writeBCParams(outputShp, warning, WC_res, WC_sat, lambda_BC, hb_BC) log.info("Brooks-Corey parameters written to output shapefile") # Create plots brooksCorey.plotBrooksCorey(outputFolder, WC_res, WC_sat, hb_BC, lambda_BC, nameArray, fcVal, sicVal, pwpVal) ############################################### ### Calculate water content using BC params ### ############################################### # Check for any soils that we were not able to calculate BC parameters for # lambda_BC[i] == -9999 errors = [] for i in range(0, len(lambda_BC)): if lambda_BC[i] == -9999: log.warning('Invalid lambda found for ' + str(nameArray[i])) errors.append(i) # Calculate water content at default pressures WC_1kPaArray = [] WC_3kPaArray = [] WC_10kPaArray = [] WC_33kPaArray = [] WC_100kPaArray = [] WC_200kPaArray = [] WC_1000kPaArray = [] WC_1500kPaArray = [] for i in range(0, len(nameArray)): pressures = [1.0, 3.0, 10.0, 33.0, 100.0, 200.0, 1000.0, 1500.0] if lambda_BC[i] != -9999: bc_WC = brooksCorey.calcBrooksCoreyFXN(pressures, hb_BC[i], WC_res[i], WC_sat[i], lambda_BC[i]) else: bc_WC = [-9999] * len(pressures) WC_1kPaArray.append(bc_WC[0]) WC_3kPaArray.append(bc_WC[1]) WC_10kPaArray.append(bc_WC[2]) WC_33kPaArray.append(bc_WC[3]) WC_100kPaArray.append(bc_WC[4]) WC_200kPaArray.append(bc_WC[5]) WC_1000kPaArray.append(bc_WC[6]) WC_1500kPaArray.append(bc_WC[7]) common.writeOutputWC(outputShp, WC_1kPaArray, WC_3kPaArray, WC_10kPaArray, WC_33kPaArray, WC_100kPaArray, WC_200kPaArray, WC_1000kPaArray, WC_1500kPaArray) # Write water content at user-input pressures # Initialise the pressure head array x = np.array(BCPressArray) bcPressures = x.astype(np.float) # For the headings headings = ['Name'] for pressure in bcPressures: headName = 'WC_' + str(pressure) + "kPa" headings.append(headName) wcHeadings = headings[1:] wcArrays = [] # Calculate soil moisture content at custom VG pressures for i in range(0, len(nameArray)): if lambda_BC[i] != -9999: wcValues = brooksCorey.calcBrooksCoreyFXN( bcPressures, hb_BC[i], WC_res[i], WC_sat[i], lambda_BC[i]) else: wcValues = [-9999] * len(bcPressures) wcValues.insert(0, nameArray[i]) wcArrays.append(wcValues) # Write to output CSV outCSV = os.path.join(outputFolder, 'WaterContent.csv') with open(outCSV, 'wb') as csv_file: writer = csv.writer(csv_file) writer.writerow(headings) for i in range(0, len(nameArray)): row = wcArrays[i] writer.writerow(row) msg = 'Output CSV with water content saved to: ' + str(outCSV) log.info(msg) csv_file.close() ################################################## ### Calculate water content at critical points ### ################################################## # Initialise water content arrays wc_satCalc = [] wc_fcCalc = [] wc_sicCalc = [] wc_pwpCalc = [] wc_DW = [] wc_RAW = [] wc_NRAW = [] wc_PAW = [] wcCriticalPressures = [0.0, fcVal, sicVal, pwpVal] for x in range(0, len(nameArray)): if lambda_BC[x] != -9999: wcCriticals = brooksCorey.calcBrooksCoreyFXN( wcCriticalPressures, hb_BC[x], WC_res[x], WC_sat[x], lambda_BC[x]) wc_sat = wcCriticals[0] wc_fc = wcCriticals[1] wc_sic = wcCriticals[2] wc_pwp = wcCriticals[3] drainWater = wc_sat - wc_fc readilyAvailWater = wc_fc - wc_sic notRAW = wc_sic - wc_pwp PAW = wc_fc - wc_pwp checks_PTFs.checkNegValue("Drainable water", drainWater, nameArray[i]) checks_PTFs.checkNegValue("Readily available water", readilyAvailWater, nameArray[i]) checks_PTFs.checkNegValue("Not readily available water", notRAW, nameArray[i]) checks_PTFs.checkNegValue("Not readily available water", PAW, nameArray[i]) else: wc_sat = -9999 wc_fc = -9999 wc_sic = -9999 wc_pwp = -9999 drainWater = -9999 readilyAvailWater = -9999 notRAW = -9999 PAW = -9999 wc_satCalc.append(wc_sat) wc_fcCalc.append(wc_fc) wc_sicCalc.append(wc_sic) wc_pwpCalc.append(wc_pwp) wc_DW.append(drainWater) wc_RAW.append(readilyAvailWater) wc_NRAW.append(notRAW) wc_PAW.append(PAW) common.writeOutputCriticalWC(outputShp, wc_satCalc, wc_fcCalc, wc_sicCalc, wc_pwpCalc, wc_DW, wc_RAW, wc_NRAW, wc_PAW) except Exception: arcpy.AddError("Brooks-Corey function failed") raise finally: # Remove feature layers from memory try: for lyr in common.listFeatureLayers(locals()): arcpy.Delete_management(locals()[lyr]) exec(lyr + ' = None') in locals() except Exception: pass
def runSystemChecks(folder=None, rerun=False): import LUCI_PTFs.lib.progress as progress # Set overwrite output arcpy.env.overwriteOutput = True # Check spatial analyst licence is available if arcpy.CheckExtension("Spatial") == "Available": arcpy.CheckOutExtension("Spatial") else: raise RuntimeError( "Spatial Analyst license not present or could not be checked out") ### Set workspaces so that temporary files are written to the LUCI scratch geodatabase ### if arcpy.ProductInfo() == "ArcServer": log.info('arcpy.env.scratchWorkspace on server: ' + str(arcpy.env.scratchWorkspace)) # Set current workspace arcpy.env.workspace = arcpy.env.scratchGDB else: # If rerunning a tool, check if scratch workspace has been set. If it has, use it as it is (with temporary rasters and feature classes from the previous run). scratchGDB = None if rerun: xmlFile = progress.getProgressFilenames(folder).xmlFile if os.path.exists(xmlFile): scratchGDB = readXML(xmlFile, 'ScratchGDB') if not arcpy.Exists(scratchGDB): log.error('Previous scratch GDB ' + str(scratchGDB) + ' does not exist. Tool cannot be rerun.') log.error('Exiting tool') sys.exit() if scratchGDB is None: # Set scratch path from values in user settings file if values present scratchPath = None try: if os.path.exists(configuration.userSettingsFile): tree = ET.parse(configuration.userSettingsFile) root = tree.getroot() scratchPath = root.find("scratchPath").text except Exception: pass # If any errors occur, ignore them. Just use the default scratch path. # Set scratch path if needed if scratchPath is None: scratchPath = configuration.scratchPath # Create scratch path folder if not os.path.exists(scratchPath): os.makedirs(scratchPath) # Remove old date/time stamped scratch folders if they exist and if they do not contain lock ArcGIS lock files. for root, dirs, files in os.walk(scratchPath): for dir in dirs: # Try to rename folder. If this is possible then no locks are held on it and it can then be removed. try: fullDirPath = os.path.join(scratchPath, dir) renamedDir = os.path.join(scratchPath, 'ready_for_deletion') os.rename(fullDirPath, renamedDir) except Exception: # import traceback # log.warning(traceback.format_exc()) pass else: try: shutil.rmtree(renamedDir) except Exception: # import traceback # log.warning(traceback.format_exc()) pass # Create new date/time stamped scratch folder for the scratch GDB to live in dateTimeStamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") scratchGDBFolder = os.path.join(scratchPath, 'scratch_' + dateTimeStamp) if not os.path.exists(scratchGDBFolder): os.mkdir(scratchGDBFolder) # Create scratch GDB scratchGDB = os.path.join(scratchGDBFolder, 'scratch.gdb') if not os.path.exists(scratchGDB): arcpy.CreateFileGDB_management(os.path.dirname(scratchGDB), os.path.basename(scratchGDB)) # Try to remove old scratch path if still exists try: shutil.rmtree(configuration.oldScratchPath, ignore_errors=True) except Exception: pass # Set scratch and current workspaces arcpy.env.scratchWorkspace = scratchGDB arcpy.env.workspace = scratchGDB # Scratch folder scratchFolder = arcpy.env.scratchFolder if not os.path.exists(scratchFolder): os.mkdir(scratchFolder) # Remove all in_memory data sets arcpy.Delete_management("in_memory") # Check disk space for disk with scratch workspace freeSpaceGb = 3 if getFreeDiskSpaceGb(arcpy.env.scratchWorkspace) < freeSpaceGb: log.warning("Disk containing scratch workspace has less than " + str(freeSpaceGb) + "Gb free space. This may cause this tool to fail.")
def function(outputFolder, inputShp, fieldFC, fieldPWP, fieldSat, RAWoption, RAWfrac, fieldCrit, RDchoice, rootingDepth): try: # Set temporary variables prefix = os.path.join(arcpy.env.scratchGDB, "moist_") tempSoils = prefix + "tempSoils" # Set output filename outputShp = os.path.join(outputFolder, "soilMoisture.shp") # Check fields are present in the input shapefile reqFields = ['OBJECTID', fieldFC, fieldPWP, fieldSat] if RAWoption == 'CriticalPoint': reqFields.append(fieldCrit) checkInputFields(reqFields, inputShp) # Copy the input shapefile to the output folder arcpy.CopyFeatures_management(inputShp, tempSoils) # Retrieve information from input shapefile record = [] volFC = [] volPWP = [] volSat = [] volCrit = [] with arcpy.da.SearchCursor(tempSoils, reqFields) as searchCursor: for row in searchCursor: objectID = row[0] WC_FC = row[1] WC_PWP = row[2] WC_Sat = row[3] record.append(objectID) volFC.append(WC_FC) volPWP.append(WC_PWP) volSat.append(WC_Sat) if RAWoption == 'CriticalPoint': WC_Crit = row[4] volCrit.append(WC_Crit) volPAW = [] volDW = [] volTWC = [] volRAW = [] mmPAW = [] mmDW = [] mmTWC = [] mmRAW = [] for x in range(0, len(record)): # Calculate PAW PAW = volFC[x] - volPWP[x] if PAW < 0: log.warning('Negative PAW (vol) calculated for record ' + str(record[x])) volPAW.append(PAW) # Calculate drainable water DW = volSat[x] - volFC[x] if DW < 0: log.warning( 'Negative drainable water (vol) calculated for record ' + str(record[x])) volDW.append(DW) # Calculate total water content TWC = volSat[x] - volPWP[x] if TWC < 0: log.warning( 'Negative total water content (vol) calculated for record ' + str(record[x])) volTWC.append(TWC) if RAWoption == 'CriticalPoint': RAW = volFC[x] - volCrit[x] RAWfrac = RAW / float(PAW) if frac <= 0.2 or frac >= 0.8: log.warning('Fraction of RAW to PAW is below 0.2 or 0.8') volRAW.append(RAW) elif RAWoption == 'Fraction': RAW = float(RAWfrac) * float(PAW) volRAW.append(RAW) if RDchoice == True: mm_PAW = PAW * float(rootingDepth) mm_DW = DW * float(rootingDepth) mm_TWC = TWC * float(rootingDepth) mm_RAW = RAW * float(rootingDepth) mmPAW.append(mm_PAW) mmDW.append(mm_DW) mmTWC.append(mm_TWC) mmRAW.append(mm_RAW) outFields = ['volPAW', 'volDW', 'volTWC', 'volRAW'] # Write outputs to shapefile arcpy.AddField_management(tempSoils, "volPAW", "DOUBLE", 10, 6) arcpy.AddField_management(tempSoils, "volDW", "DOUBLE", 10, 6) arcpy.AddField_management(tempSoils, "volTWC", "DOUBLE", 10, 6) arcpy.AddField_management(tempSoils, "volRAW", "DOUBLE", 10, 6) if RDchoice == True: arcpy.AddField_management(tempSoils, "mmPAW", "DOUBLE", 10, 6) arcpy.AddField_management(tempSoils, "mmDW", "DOUBLE", 10, 6) arcpy.AddField_management(tempSoils, "mmTWC", "DOUBLE", 10, 6) arcpy.AddField_management(tempSoils, "mmRAW", "DOUBLE", 10, 6) outFields.append('mmPAW') outFields.append('mmDW') outFields.append('mmTWC') outFields.append('mmRAW') recordNum = 0 with arcpy.da.UpdateCursor(tempSoils, outFields) as cursor: for row in cursor: row[0] = volPAW[recordNum] row[1] = volDW[recordNum] row[2] = volTWC[recordNum] row[3] = volRAW[recordNum] if RDchoice == True: row[4] = mmPAW[recordNum] row[5] = mmDW[recordNum] row[6] = mmTWC[recordNum] row[7] = mmRAW[recordNum] cursor.updateRow(row) recordNum += 1 # Clean fields outFields.append('OBJECTID') common.CleanFields(tempSoils, outFields) arcpy.CopyFeatures_management(tempSoils, outputShp) except Exception: arcpy.AddError("Soil moisture function failed") raise finally: # Remove feature layers from memory try: for lyr in common.listFeatureLayers(locals()): arcpy.Delete_management(locals()[lyr]) exec(lyr + ' = None') in locals() except Exception: pass