def weibullModelAnalysis(isopachs,numberOfRuns,iterationsPerRun,limits): """ Analyses the isopach data under the assumption it follows a Weibull model Model: T(x) = theta*((x/lambda)^(k-2))*exp(-((x/lambda)^k)) IMPORTANT: Works under the assumption x = sqrt(A/pi) rather than x = sqrt(A). Not guaranteed to provide a good fit (although with a suitable number of runs and iterations per run the probability is high). If a poor fit is returned, try rerunning the calculation. Arguments isopachs:list of Isopachs -- list of isopachs to analyse. numberOfRuns:int -- the number of runs that the hill-climbing algorithm performs iterationsPerRun:int -- the number of iterations per run that the hill-climbing algorithm performs limits:list of 2-tuples -- A list of 2 2-tuples, the first 2 tuple representing lower and upper bounds for parameter lambda and the second 2-tuple the bounds for parameter k. Returns A dictionary with the following key-value mapping: dict["estimatedTotalVolume"]:float -- the estimated total volume of the deposit (in km3). dict["thicknessFunction"]:func x->t -- the thickness function, calculates T(x) (in metres). dict["lambda"]:float -- estimated value of parameter lambda dict["k"]:float -- estimated value of parameter k dict["theta"]:float -- estimated value of parameter theta dict["bestScore"]:float -- the score of the parameters returned, the closer to zero it is the better the fit of the curve dict["isopachs"]:list of Isopachs -- list of Isopachs analysed dict["limits"]:list of 2-tuples -- the limits for lambda and k used in calculations dict["mrse"]:float -- the mean relative squared error of the model """ sqrtAreasKM = np.array([isopach.sqrtAreaKM for isopach in isopachs]) thicknessesM = np.array([isopach.thicknessM for isopach in isopachs]) lamb, k, bestScore = _solveWeibullParameters(_logErrorFunction, sqrtAreasKM, thicknessesM, numberOfRuns, iterationsPerRun, *limits) theta = calculateTheta(sqrtAreasKM, thicknessesM, lamb,k) thicknessFunction = _createThicknessFunction(lamb, k, theta) estimatedTotalVolumeKM3 = calculateWeibullVolume(lamb, k, theta) mrse = regression_methods.meanRelativeSquaredError(sqrtAreasKM, thicknessesM, thicknessFunction) return {"estimatedTotalVolume" : estimatedTotalVolumeKM3, "thicknessFunction" : thicknessFunction, "lambda" : lamb, "k" : k, "theta" : theta, "bestScore" : bestScore, "isopachs" : isopachs, "limits" : limits, "mrse" : mrse}
def errorFunction(lamb,k): theta = calculateTheta(xs,ys,lamb,k) def thicknessFunction(x): try: return np.exp(np.log(theta)+(k-2)*np.log(x/lamb)-(x/lamb)**k) except FloatingPointError: return 0 mrse = regression_methods.meanRelativeSquaredError(xs, ys, thicknessFunction) return math.log(mrse)
def _updateWei(self): ########### ## Stats ## ########### fr = self.statsFrame # lambda lamb = self.currentParameters["lambda"] lambdaStr = helper_functions.roundToSF(lamb, NUMBER_OF_SF) fr.weiLambdaE.insertNew(lamb) # k k = self.currentParameters["k"] kStr = helper_functions.roundToSF(k, NUMBER_OF_SF) fr.weiKE.insertNew(k) # theta theta = self.currentParameters["theta"] thetaStr = helper_functions.roundToSF(theta, NUMBER_OF_SF) fr.weiThetaE.insertNew(theta) # Volume volume = calculateWeibullVolume(lamb, k, theta) volumeStr = helper_functions.roundToSF(volume, NUMBER_OF_SF) fr.totalEstimatedVolume_E.insertNew(volumeStr) # Error thicknessFunction = lambda x: theta * ( (x / lamb)**(k - 2)) * math.exp(-((x / lamb)**k)) error = regression_methods.meanRelativeSquaredError( self.sqrtAreaKM, self.thicknessM, thicknessFunction) errorStr = helper_functions.roundToSF(error, NUMBER_OF_SF) fr.relativeSquaredError_E.insertNew(errorStr) # Equation invLambdaStr = helper_functions.roundToSF(1 / lamb, NUMBER_OF_SF) kminus2Str = helper_functions.roundToSF(k - 2, NUMBER_OF_SF) equationStr = "T = " + thetaStr + "((" + invLambdaStr + "x)^" + kminus2Str + ")exp(-(" + invLambdaStr + "x)^" + kStr + ")" fr.equation_E.insertNew(equationStr) ############ ## Graphs ## ############ # Model startX = 0 endX = (self.isopachs[-1].distanceFromVentKM() + 50) * SQRT_PI xs = helper_functions.getStaggeredPoints(startX, endX, MODEL_PLOTTING_PRECISION)[1:] ys = [ theta * ((x / lamb)**(k - 2)) * math.exp(-((x / lamb)**k)) for x in xs ] self.modelGraphFrame.plotFilledLine(xs, ys, colours[0])
def _updateWei(self): ########### ## Stats ## ########### fr = self.statsFrame # lambda lamb = self.currentParameters["lambda"] lambdaStr = helper_functions.roundToSF(lamb, NUMBER_OF_SF) fr.weiLambdaE.insertNew(lamb) # k k = self.currentParameters["k"] kStr = helper_functions.roundToSF(k, NUMBER_OF_SF) fr.weiKE.insertNew(k) # theta theta = self.currentParameters["theta"] thetaStr = helper_functions.roundToSF(theta, NUMBER_OF_SF) fr.weiThetaE.insertNew(theta) # Volume volume = calculateWeibullVolume(lamb, k, theta) volumeStr = helper_functions.roundToSF(volume, NUMBER_OF_SF) fr.totalEstimatedVolume_E.insertNew(volumeStr) # Error thicknessFunction = lambda x : theta*((x/lamb)**(k-2))*math.exp(-((x/lamb)**k)) error = regression_methods.meanRelativeSquaredError(self.sqrtAreaKM, self.thicknessM, thicknessFunction) errorStr = helper_functions.roundToSF(error, NUMBER_OF_SF) fr.relativeSquaredError_E.insertNew(errorStr) # Equation invLambdaStr = helper_functions.roundToSF(1/lamb, NUMBER_OF_SF) kminus2Str = helper_functions.roundToSF(k-2, NUMBER_OF_SF) equationStr = "T = " + thetaStr + "((" + invLambdaStr + "x)^" +kminus2Str + ")exp(-(" + invLambdaStr + "x)^" + kStr + ")" fr.equation_E.insertNew(equationStr) ############ ## Graphs ## ############ # Model startX = 0 endX = (self.isopachs[-1].distanceFromVentKM()+50)*SQRT_PI xs = helper_functions.getStaggeredPoints(startX,endX,MODEL_PLOTTING_PRECISION)[1:] ys = [theta*((x/lamb)**(k-2))*math.exp(-((x/lamb)**k)) for x in xs] self.modelGraphFrame.plotFilledLine(xs, ys, colours[0])
def errorFunction(c,m): thicknessFunction = lambda x : c*(x**(-m)) return math.log(regression_methods.meanRelativeSquaredError(xs, ys, thicknessFunction))
def _updatePow(self): ########### ## Stats ## ########### fr = self.statsFrame # Coefficient c = self.currentParameters["coefficient"] coefficientStr = helper_functions.roundToSF(c, NUMBER_OF_SF) fr.powCoefficient_E.insertNew(c) # Exponent m = self.currentParameters["exponent"] exponentStr = helper_functions.roundToSF(m, NUMBER_OF_SF) fr.powExponent_E.insertNew(m) # Proximal limit proximalLimitKM = self.currentParameters["proximalLimitKM"] proximalLimitStr = helper_functions.roundToSF(proximalLimitKM, NUMBER_OF_SF) fr.powProximalLimit_E.insertNew(proximalLimitKM) # Distal limit distalLimitKM = self.currentParameters["distalLimitKM"] distalLimitStr = helper_functions.roundToSF(distalLimitKM, NUMBER_OF_SF) fr.powDistalLimit_E.insertNew(distalLimitKM) # Volume volume = calculatePowerLawVolume(c, m, proximalLimitKM, distalLimitKM) volumeStr = helper_functions.roundToSF(volume, NUMBER_OF_SF) fr.totalEstimatedVolume_E.insertNew(volumeStr) # Error thicknessFunction = lambda x : c*(x**(-m)) error = regression_methods.meanRelativeSquaredError(self.sqrtAreaKM, self.thicknessM, thicknessFunction) errorStr = helper_functions.roundToSF(error, NUMBER_OF_SF) fr.relativeSquaredError_E.insertNew(errorStr) # Equation equationStr = "T = " + coefficientStr if m > 0: equationStr += "x^-" + exponentStr elif m < 0: equationStr += "x^" + exponentStr[1:] fr.equation_E.insertNew(equationStr) # Suggested proximal limit suggestedProximalLimit = self.currentParameters["suggestedProximalLimit"] suggestedProximalLimitStr = helper_functions.roundToSF(suggestedProximalLimit, NUMBER_OF_SF) fr.powSuggestedProximalLimit_E.insertNew(suggestedProximalLimitStr) ############ ## Graphs ## ############ startX = proximalLimitKM*SQRT_PI endX = distalLimitKM*SQRT_PI # Model xs = helper_functions.getStaggeredPoints(startX, endX, MODEL_PLOTTING_PRECISION) ys = [thicknessFunction(x) for x in xs] self.modelGraphFrame.plotFilledLine(xs, ys, color=colours[0]) # Regression logXs = [np.log(a) for a in self.sqrtAreaKM] logYs = [np.log(t) for t in self.thicknessM] self.regressionGraphFrame.plotScatter(logXs, logYs, False) self.regressionGraphFrame.axes.set_xlabel(r"$\log{\sqrt{Area}}$") lineXs = [np.sqrt(startX), np.sqrt(endX)] lineYs = [np.log(c) - m*x for x in lineXs] self.regressionGraphFrame.plotLine(lineXs, lineYs, colours[0])
def _updateExp(self, comboboxUpdate): n = self.currentParameters["numberOfSegments"] coefficients = self.currentParameters["segmentCoefficients"] exponents = self.currentParameters["segmentExponents"] limits = self.currentParameters["segmentLimits"] ########### ## Stats ## ########### fr = self.statsFrame # Segment start start = limits[self.currentSegment] startStr = helper_functions.roundToSF(start, NUMBER_OF_SF) fr.expSegStartLimit_E.insertNew(start) fr.expSegStartLimit_E.setUserEditable(self.currentSegment != 0) # Segment end end = limits[self.currentSegment+1] endStr = helper_functions.roundToSF(end, NUMBER_OF_SF) fr.expSegEndLimit_E.insertNew(end) fr.expSegEndLimit_E.setUserEditable(self.currentSegment != n-1) # Segment coefficient coefficient = coefficients [self.currentSegment] coefficientStr = helper_functions.roundToSF(coefficient, NUMBER_OF_SF) fr.expSegCoefficent_E.insertNew(coefficient) # Segment exponent exponent = exponents[self.currentSegment] exponentStr = helper_functions.roundToSF(exponent, NUMBER_OF_SF) fr.expSegExponent_E.insertNew(exponent) # Segment volume segmentVolumes = [calculateExponentialSegmentVolume(coefficients[i], exponents[i], limits[i], limits[i+1]) for i in range(n)] segmentVolumeStr = helper_functions.roundToSF(segmentVolumes[self.currentSegment], NUMBER_OF_SF) fr.expSegVolume_E.insertNew(segmentVolumeStr) # Total volume totalVolume = sum(segmentVolumes) estimatedTotalVolumeStr = helper_functions.roundToSF(totalVolume, NUMBER_OF_SF) fr.totalEstimatedVolume_E.insertNew(estimatedTotalVolumeStr) # Error def thicknessFunction(x): for i in range(n): if limits[i] <= x < limits[i+1]: return coefficients[i]*math.exp(-exponents[i]*x) error = regression_methods.meanRelativeSquaredError(self.sqrtAreaKM, self.thicknessM, thicknessFunction) errorStr = helper_functions.roundToSF(error, NUMBER_OF_SF) fr.relativeSquaredError_E.insertNew(errorStr) # Equation equationStr = "T = " + coefficientStr if exponent > 0: equationStr += "exp(-" + exponentStr + "x)" elif exponent < 0: equationStr += "exp(" + exponentStr[1:] + "x)" fr.equation_E.insertNew(equationStr) ############ ## Graphs ## ############ if not comboboxUpdate: # Model endXs = limits[1:-1] + [1.5*max(self.sqrtAreaKM)-0.5*min(self.sqrtAreaKM)] for i in range(n): xs = helper_functions.getStaggeredPoints(limits[i], endXs[i], MODEL_PLOTTING_PRECISION) ys = [coefficients[i]*math.exp(-exponents[i]*x) for x in xs] self.modelGraphFrame.plotFilledLine(xs, ys, color=colours[i]) # Regression logThicknessM = [np.log(t) for t in self.thicknessM] self.regressionGraphFrame.plotScatter(self.sqrtAreaKM, logThicknessM, False) self.regressionGraphFrame.axes.set_xlabel(r"$\sqrt{Area}$") for i in range(n): xs = [limits[i], endXs[i]] ys = [np.log(thicknessFunction(x)) for x in xs] self.regressionGraphFrame.plotLine(xs,ys, color=colours[i])
def weibullModelAnalysis(isopachs, numberOfRuns, iterationsPerRun, limits): """ Analyses the isopach data under the assumption it follows a Weibull model Model: T(x) = theta*((x/lambda)^(k-2))*exp(-((x/lambda)^k)) IMPORTANT: Works under the assumption x = sqrt(A/pi) rather than x = sqrt(A). Not guaranteed to provide a good fit (although with a suitable number of runs and iterations per run the probability is high). If a poor fit is returned, try rerunning the calculation. Arguments isopachs:list of Isopachs -- list of isopachs to analyse. numberOfRuns:int -- the number of runs that the hill-climbing algorithm performs iterationsPerRun:int -- the number of iterations per run that the hill-climbing algorithm performs limits:list of 2-tuples -- A list of 2 2-tuples, the first 2 tuple representing lower and upper bounds for parameter lambda and the second 2-tuple the bounds for parameter k. Returns A dictionary with the following key-value mapping: dict["estimatedTotalVolume"]:float -- the estimated total volume of the deposit (in km3). dict["thicknessFunction"]:func x->t -- the thickness function, calculates T(x) (in metres). dict["lambda"]:float -- estimated value of parameter lambda dict["k"]:float -- estimated value of parameter k dict["theta"]:float -- estimated value of parameter theta dict["bestScore"]:float -- the score of the parameters returned, the closer to zero it is the better the fit of the curve dict["isopachs"]:list of Isopachs -- list of Isopachs analysed dict["limits"]:list of 2-tuples -- the limits for lambda and k used in calculations dict["mrse"]:float -- the mean relative squared error of the model """ sqrtAreasKM = np.array([isopach.sqrtAreaKM for isopach in isopachs]) thicknessesM = np.array([isopach.thicknessM for isopach in isopachs]) lamb, k, bestScore = _solveWeibullParameters(_logErrorFunction, sqrtAreasKM, thicknessesM, numberOfRuns, iterationsPerRun, *limits) theta = calculateTheta(sqrtAreasKM, thicknessesM, lamb, k) thicknessFunction = _createThicknessFunction(lamb, k, theta) estimatedTotalVolumeKM3 = calculateWeibullVolume(lamb, k, theta) mrse = regression_methods.meanRelativeSquaredError(sqrtAreasKM, thicknessesM, thicknessFunction) return { "estimatedTotalVolume": estimatedTotalVolumeKM3, "thicknessFunction": thicknessFunction, "lambda": lamb, "k": k, "theta": theta, "bestScore": bestScore, "isopachs": isopachs, "limits": limits, "mrse": mrse }
def exponentialModelAnalysis(isopachs, n): """ Analyses the isopach data under the assumption it follows a n-segment exponential model Model: T(x) = c*exp(-m*x) IMPORTANT: Works under the assumption x = sqrt(A/pi) rather than x = sqrt(A). Arguments isopachs:list of Isopachs -- list of isopachs to analyse n:int -- the number of exponential segments Returns A dictionary with the following key-value mapping: dict["estimatedTotalVolume"]:float -- the estimated total volume of the deposit. dict["thicknessFunction"]:func x->t -- the thickness function, calculates T(x) (in metres). dict["segmentLimits"]:list of floats -- list of bounds for the segments. Segment i is valid between segmentLimits[i] and segmentLimits[i+1]. dict["segmentVolumes"]:list of floats -- estimated tephra volumes for each segment. dict["segmentCoefficients"]:list of floats -- estimated coefficients for each segment. dict["segmentExponents"]:list of floats -- estimated exponents for each segment. dict["segmentBts"]:list of floats -- estimated half-thicknesses for each segment (i.e. distance across which tephra thickness halves). dict["regressionLines"]:list of Lines -- Line objects representing each segment's least squares regression line used to estimate it's parameters. dict["isopachs"]:list of Isopachs -- list of Isopachs analysed. dict["numberOfSegments"]:int -- number of exponential segments. dict["mrse"]:float -- the mean relative squared error of the model """ thicknessesM = [isopach.thicknessM for isopach in isopachs] logThickness = [np.log(isopach.thicknessM) for isopach in isopachs] sqrtAreasKM = [isopach.sqrtAreaKM for isopach in isopachs] regressionLines, segmentLimits = regression_methods.calculateMultiLineRegression( sqrtAreasKM, logThickness, n) segmentT0s = [np.exp(line.c) for line in regressionLines] segmentKs = [-line.m for line in regressionLines] segmentBts = [np.log(2) / (k * np.sqrt(np.pi)) for k in segmentKs] segmentVolumes = [] segmentLimits[0], segmentLimits[-1] = 0, float("inf") for i in range(n): segmentVolumes.append( calculateExponentialSegmentVolume(segmentT0s[i], segmentKs[i], segmentLimits[i], segmentLimits[i + 1])) estimatedTotalVolume = sum(segmentVolumes) def thicknessFunction(x): for i in range(n): if segmentLimits[i] <= x < segmentLimits[i + 1]: return segmentT0s[i] * np.exp(-segmentKs[i] * x) raise ValueError( "x (" + str(x) + ") is not in the domain of the function (0 to infinity)") mrse = regression_methods.meanRelativeSquaredError(sqrtAreasKM, thicknessesM, thicknessFunction) return { "estimatedTotalVolume": estimatedTotalVolume, "thicknessFunction": thicknessFunction, "segmentLimits": segmentLimits, "segmentVolumes": segmentVolumes, "segmentCoefficients": segmentT0s, "segmentExponents": segmentKs, "segmentBts": segmentBts, "regressionLines": regressionLines, "isopachs": isopachs, "numberOfSegments": n, "mrse": mrse }
def exponentialModelAnalysis(isopachs,n): """ Analyses the isopach data under the assumption it follows a n-segment exponential model Model: T(x) = c*exp(-m*x) IMPORTANT: Works under the assumption x = sqrt(A/pi) rather than x = sqrt(A). Arguments isopachs:list of Isopachs -- list of isopachs to analyse n:int -- the number of exponential segments Returns A dictionary with the following key-value mapping: dict["estimatedTotalVolume"]:float -- the estimated total volume of the deposit. dict["thicknessFunction"]:func x->t -- the thickness function, calculates T(x) (in metres). dict["segmentLimits"]:list of floats -- list of bounds for the segments. Segment i is valid between segmentLimits[i] and segmentLimits[i+1]. dict["segmentVolumes"]:list of floats -- estimated tephra volumes for each segment. dict["segmentCoefficients"]:list of floats -- estimated coefficients for each segment. dict["segmentExponents"]:list of floats -- estimated exponents for each segment. dict["segmentBts"]:list of floats -- estimated half-thicknesses for each segment (i.e. distance across which tephra thickness halves). dict["regressionLines"]:list of Lines -- Line objects representing each segment's least squares regression line used to estimate it's parameters. dict["isopachs"]:list of Isopachs -- list of Isopachs analysed. dict["numberOfSegments"]:int -- number of exponential segments. dict["mrse"]:float -- the mean relative squared error of the model """ thicknessesM = [isopach.thicknessM for isopach in isopachs] logThickness = [np.log(isopach.thicknessM) for isopach in isopachs] sqrtAreasKM = [isopach.sqrtAreaKM for isopach in isopachs] regressionLines, segmentLimits = regression_methods.calculateMultiLineRegression(sqrtAreasKM,logThickness,n) segmentT0s = [np.exp(line.c) for line in regressionLines] segmentKs = [-line.m for line in regressionLines] segmentBts = [np.log(2)/(k*np.sqrt(np.pi)) for k in segmentKs] segmentVolumes = [] segmentLimits[0], segmentLimits[-1] = 0, float("inf") for i in range(n): segmentVolumes.append(calculateExponentialSegmentVolume(segmentT0s[i],segmentKs[i],segmentLimits[i],segmentLimits[i+1])) estimatedTotalVolume = sum(segmentVolumes) def thicknessFunction(x): for i in range(n): if segmentLimits[i] <= x < segmentLimits[i+1]: return segmentT0s[i]*np.exp(-segmentKs[i]*x) raise ValueError("x (" + str(x) + ") is not in the domain of the function (0 to infinity)") mrse = regression_methods.meanRelativeSquaredError(sqrtAreasKM, thicknessesM, thicknessFunction) return {"estimatedTotalVolume" : estimatedTotalVolume, "thicknessFunction" : thicknessFunction, "segmentLimits" : segmentLimits, "segmentVolumes" : segmentVolumes, "segmentCoefficients" : segmentT0s, "segmentExponents" : segmentKs, "segmentBts" : segmentBts, "regressionLines" : regressionLines, "isopachs" : isopachs, "numberOfSegments" : n, "mrse" : mrse}
def powerLawModelAnalysis(isopachs, proximalLimitKM, distalLimitKM): """ Analyses the isopach data under the assumption it follows a power law model Model: T(x) = c*x^(-m) IMPORTANT: Works under the assumption x = sqrt(A/pi) rather than x = sqrt(A). Arguments isopachs:list of Isopachs -- list of isopachs to analyse proximalLimitKM:float -- the proximal limit of integration (in km) distalLimitKM:float -- the distal limit of integration (in km) Returns A dictionary with the following key-value mapping: dict["estimatedTotalVolume"]:float -- the estimated total volume of the deposit. dict["thicknessFunction"]:func x->t -- the thickness function, calculates T(x) (in metres). dict["regressionLine"]:Line -- Line object representing the least squares regression line used to estimate the parameters dict["coefficient"]:float -- estimated coefficient for the power curve dict["exponent"]:float -- estimated exponent for the power curve dict["isopachs"]:list of Isopachs -- list of Isopachs analysed dict["proximalLimitKM"]:list of Isopachs -- the proximal limit of integration used in calculations dict["distalLimitKM"]:list of Isopachs -- the distal limit of integration used in calculations dict["mrse"]:float -- the mean relative squared error of the model """ thicknessesM = [isopach.thicknessM for isopach in isopachs] sqrtAreasKM = [isopach.sqrtAreaKM for isopach in isopachs] logThicknessesM = [np.log(t) for t in thicknessesM] logSqrtAreaKM = [np.log(a) for a in sqrtAreasKM] proximalLimitSqrtAreaKM = proximalLimitKM*np.sqrt(np.pi) distalLimitSqrtAreaKM = distalLimitKM*np.sqrt(np.pi) regressionLine = regression_methods.calculateSingleLineRegression(logSqrtAreaKM, logThicknessesM) m = -regressionLine.m c = np.exp(regressionLine.c) estimatedTotalVolume = calculatePowerLawVolume(c, m, proximalLimitSqrtAreaKM, distalLimitSqrtAreaKM) def thicknessFunction(x): if proximalLimitSqrtAreaKM <= x <= distalLimitSqrtAreaKM: return c*(x**-m) else: raise ValueError("x is out of range of proximal and distal limits of integration") if len(isopachs) > 3: suggestedProximalLimit = calculateProximalLimitEstimate(isopachs, c, m) else: suggestedProximalLimit = "N/A" mrse = regression_methods.meanRelativeSquaredError(sqrtAreasKM, thicknessesM, thicknessFunction) return {"estimatedTotalVolume" : estimatedTotalVolume, "thicknessFunction" : thicknessFunction, "regressionLine" : regressionLine, "coefficient" : c, "exponent" : m, "isopachs" : isopachs, "proximalLimitKM" : proximalLimitKM, "distalLimitKM" : distalLimitKM, "suggestedProximalLimit" : suggestedProximalLimit, "mrse" : mrse}
def powerLawModelAnalysis(isopachs, proximalLimitKM, distalLimitKM): """ Analyses the isopach data under the assumption it follows a power law model Model: T(x) = c*x^(-m) IMPORTANT: Works under the assumption x = sqrt(A/pi) rather than x = sqrt(A). Arguments isopachs:list of Isopachs -- list of isopachs to analyse proximalLimitKM:float -- the proximal limit of integration (in km) distalLimitKM:float -- the distal limit of integration (in km) Returns A dictionary with the following key-value mapping: dict["estimatedTotalVolume"]:float -- the estimated total volume of the deposit. dict["thicknessFunction"]:func x->t -- the thickness function, calculates T(x) (in metres). dict["regressionLine"]:Line -- Line object representing the least squares regression line used to estimate the parameters dict["coefficient"]:float -- estimated coefficient for the power curve dict["exponent"]:float -- estimated exponent for the power curve dict["isopachs"]:list of Isopachs -- list of Isopachs analysed dict["proximalLimitKM"]:list of Isopachs -- the proximal limit of integration used in calculations dict["distalLimitKM"]:list of Isopachs -- the distal limit of integration used in calculations dict["mrse"]:float -- the mean relative squared error of the model """ thicknessesM = [isopach.thicknessM for isopach in isopachs] sqrtAreasKM = [isopach.sqrtAreaKM for isopach in isopachs] logThicknessesM = [np.log(t) for t in thicknessesM] logSqrtAreaKM = [np.log(a) for a in sqrtAreasKM] proximalLimitSqrtAreaKM = proximalLimitKM * np.sqrt(np.pi) distalLimitSqrtAreaKM = distalLimitKM * np.sqrt(np.pi) regressionLine = regression_methods.calculateSingleLineRegression( logSqrtAreaKM, logThicknessesM) m = -regressionLine.m c = np.exp(regressionLine.c) estimatedTotalVolume = calculatePowerLawVolume(c, m, proximalLimitSqrtAreaKM, distalLimitSqrtAreaKM) def thicknessFunction(x): if proximalLimitSqrtAreaKM <= x <= distalLimitSqrtAreaKM: return c * (x**-m) else: raise ValueError( "x is out of range of proximal and distal limits of integration" ) if len(isopachs) > 3: suggestedProximalLimit = calculateProximalLimitEstimate(isopachs, c, m) else: suggestedProximalLimit = "N/A" mrse = regression_methods.meanRelativeSquaredError(sqrtAreasKM, thicknessesM, thicknessFunction) return { "estimatedTotalVolume": estimatedTotalVolume, "thicknessFunction": thicknessFunction, "regressionLine": regressionLine, "coefficient": c, "exponent": m, "isopachs": isopachs, "proximalLimitKM": proximalLimitKM, "distalLimitKM": distalLimitKM, "suggestedProximalLimit": suggestedProximalLimit, "mrse": mrse }