def flotationMask(s, zF, Q, rhoI=rhoI, rhoW=rhoW): """Using flotation height, create masks for floating and grounded ice. Parameters ---------- zF firedrake interp function Flotation height (m) Q : firedrake function space function space rhoI : [type], optional [description], by default rhoI rhoW : [type], optional [description], by default rhoW Returns ------- floating firedrake interp function ice shelf mask 1 floating, 0 grounded grounded firedrake interp function Grounded mask 1 grounded, 0 floating """ # smooth to avoid isolated points dipping below flotation. zAbove = firedrakeSmooth(icepack.interpolate(s - zF, Q), alpha=100) floating = icepack.interpolate(zAbove < 0, Q) grounded = icepack.interpolate(zAbove > 0, Q) return floating, grounded
def mapPlots(plotData, t, melt, floating, h, hLast, deltaT, Q): ''' Plot melt, floating/grounded, thinning in map view ''' setupMapPlots(plotData) axes = plotData['axesM'].flatten() meltC = icepack.plot.tricontourf(icepack.interpolate(melt, Q), levels=np.linspace(-150, 0, 151), extend='both', axes=axes[0]) floatC = icepack.plot.tricontourf(floating, axes=axes[1], extend='both', levels=np.linspace(0, 1, 106)) thin = icepack.interpolate((h - hLast) / deltaT, Q) thinC = icepack.plot.tricontourf(thin, levels=np.linspace(-8, 8, 161), axes=axes[2], extend='both', cmap=plt.get_cmap('bwr_r')) plotData['figM'].suptitle(f'Simulation Year: {t:0.1f}') if plotData['figMColorbar']: titles = ['melt (m/yr)', 'floating', 'dH/dt (m/yr)'] for ax, cont, title in zip(axes, [meltC, floatC, thinC], titles): myColorBar(plotData['figM'], ax, cont) ax.set_title(title) plotData['figMColorbar'] = False plotData['figM'].tight_layout(rect=[0, 0, 1, 0.95]) if plotData['plotResult']: plotData['figM'].canvas.draw()
def forceFloat(floatingMask, bedElevation, sufaceElevation, Q, headRoom=50): """Adjust bed as needed to force flotation Parameters ---------- floatingMask : firedrake function Mask where 1 indicates ice should be afloati bedElevation : firedrake function Original bed elevation sufaceElevation : firedrake function Surface elevation ice equivalent Q : firedrake function space Domain """ nItMax = 20 newBed = bedElevation.copy(deepcopy=True) dZ = 15 i = 0 print('start') while i < nItMax: zF = flotationHeight(newBed, Q, rhoI=rhoI, rhoW=rhoW) currentFlotation, _ = flotationMask(sufaceElevation, zF, Q, rhoI=rhoI, rhoW=rhoW) notFloating = firedrake.And(currentFlotation < firedrake.Constant(0.5), floatingMask > 0.5) area = firedrake.assemble(notFloating * firedrake.dx) newBed = icepack.interpolate(newBed - dZ * notFloating, Q) print(i, area / 1e6) if area < 1: break i = i + 1 newBed = icepack.interpolate(newBed - headRoom * floatingMask, Q) return newBed
def divMelt(h, floating, meltParams, u, Q): """ Melt function that is a scaled version of the flux divergence h : firedrake function ice thickness u : firedrake vector function surface elevation floating : firedrake function floating mask V : firedrake vector space vector space for velocity meltParams : dict parameters for melt function Returns ------- firedrake function melt rates """ flux = u * h fluxDiv = icepack.interpolate(firedrake.div(flux), Q) fluxDivS = firedrakeSmooth(fluxDiv, alpha=8000) fluxDivS = firedrake.min_value( fluxDivS * floating * meltParams['meltMask'], 0) intFluxDiv = firedrake.assemble(fluxDivS * firedrake.dx) scale = -1.0 * float(meltParams['intMelt']) / float(intFluxDiv) scale = firedrake.Constant(scale) melt = icepack.interpolate( firedrake.min_value(fluxDivS * scale, meltParams['maxMelt']), Q) return melt
def test_ice_shelf(): ice_shelf = icepack.IceShelf({0}) h = icepack.interpolate(discretization, thickness) u0 = icepack.interpolate(discretization, velocity, lambda x: 0.0) theta = icepack.interpolate(discretization, temperature) u = ice_shelf.solve(h, theta, u0)
def piecewiseWithDepth(h, floating, meltParams, Q, *argv, **kwargs): """ Melt function that is described piecewise by set of polynomials Melt is in units of m/yr w.e. Parameters ---------- h : firedrake function ice thickness floating : firedrake function floating mask meltParams : dict parameters for melt function Returns ------- firedrake function melt rates """ # compute depth melt = firedrake.Constant(0) for i in range(1, meltParams['numberOfPolynomials'] + 1): poly = meltParams[f'poly{i}'] tmpMelt = firedrake.Constant(poly['coeff'][0]) for j in range(1, poly['deg'] + 1): tmpMelt = tmpMelt + poly['coeff'][j] * h**j # Apply melt to all shelf ice (ice > 30 m) melt = melt + tmpMelt * \ (h > max(poly['min'], 30.1)) * (h < poly['max']) # Smooth result alpha = 4000 # Default if 'alpha' in meltParams: alpha = meltParams['alpha'] # # if filterWithFloatMask apply float mask before filter, which will shift # melt distribution up in the column. If filter applied afterwards, it # will be concentrated nearer the GL. filterWithFloatMask = False if 'filterWithFloatMask' in meltParams: filterWithFloatMask = meltParams['filterWithFloatMask'] if filterWithFloatMask: # Changed to avoid petsc memory issue on store # melt1 = icepack.interpolate(melt * floating, Q) melt1 = icepack.interpolate(melt, Q) melt1 = icepack.interpolate(floating * melt1, Q) else: melt1 = icepack.interpolate(melt, Q) melt1 = firedrakeSmooth(melt1, alpha=alpha) # 'totalMelt' given renormalize melt to produce this value if 'totalMelt' in meltParams.keys(): trend = 0. if 'trend' in kwargs.keys(): trend = kwargs['trend'] intMelt = firedrake.assemble(melt1 * floating * firedrake.dx) total = float(meltParams['totalMelt']) + trend scale = firedrake.Constant(-1.0 * total / float(intMelt)) else: scale = firedrake.Constant(1.) # melt = icepack.interpolate(melt1 * scale * floating, Q) return melt
def test_algebra(): u = icepack.interpolate(discretization, lambda x: x[0] - x[1]) v = icepack.interpolate(discretization, lambda x: x[0] * x[1]) w = icepack.interpolate(discretization, lambda x: x[0] - x[1] + x[0] * x[1]) assert icepack.dist(u + v, w) / icepack.norm(w) < 1.0e-8 w = icepack.interpolate(discretization, lambda x: x[0] - x[1] - x[0] * x[1]) assert icepack.dist(u - v, w) / icepack.norm(w) < 1.0e-8 w = icepack.interpolate(discretization, lambda x: 2 * x[0] * x[1]) assert icepack.dist(2.0 * v, w) / icepack.norm(w) < 1.0e-8 assert abs(icepack.inner_product(u, v)) < 1.0e-8
def test_interpolating_function(): nx, ny = 32, 32 mesh = firedrake.UnitSquareMesh(nx, ny) x = firedrake.SpatialCoordinate(mesh) Q = firedrake.FunctionSpace(mesh, "CG", 2) q = icepack.interpolate(x[0] ** 2 - x[1] ** 2, Q) assert abs(firedrake.assemble(q * dx)) < 1e-6
def test_interpolating_vector_field(): n = 32 array_vx = np.array([[(i + j) / n for j in range(n + 1)] for i in range(n + 1)]) missing = -9999.0 array_vx[0, 0] = missing array_vx = np.flipud(array_vx) array_vy = np.array([[(j - i) / n for j in range(n + 1)] for i in range(n + 1)]) array_vy[-1, -1] = -9999.0 array_vy = np.flipud(array_vy) vx = make_rio_dataset(array_vx, missing) vy = make_rio_dataset(array_vy, missing) mesh = make_domain(48, 48, xmin=1 / 4, ymin=1 / 4, width=1 / 2, height=1 / 2) x, y = firedrake.SpatialCoordinate(mesh) V = firedrake.VectorFunctionSpace(mesh, family='CG', degree=1) u = firedrake.interpolate(firedrake.as_vector((x + y, x - y)), V) v = icepack.interpolate((vx, vy), V) assert firedrake.norm(u - v) / firedrake.norm(u) < 1e-10
def initSummary(grounded0, floating0, h0, u0, meltModel, meltParams, SMB, Q, mesh, forwardParams, restart=False): ''' Compute initial areas and summary data if restart, try reload restart data. If not go with clean slate, which should be a legacy case (from when intermediates steps were not saved) ''' if forwardParams['restart']: summaryData = reinitSummary(forwardParams) if summaryData is not None: return summaryData # No summary.tmp from a prior run so reset restart forwardParams['restart'] = False # start from scratch area = firedrake.assemble(firedrake.Constant(1) * firedrake.dx(mesh)) gArea0 = firedrake.assemble(grounded0 * firedrake.dx(mesh)) fArea0 = area - gArea0 melt = meltModel(h0, floating0, meltParams, Q, u0) meltTot = firedrake.assemble( icepack.interpolate(floating0 * melt, Q) * firedrake.dx(mesh)) SMBfloating = firedrake.assemble( icepack.interpolate(floating0 * SMB, Q) * firedrake.dx(mesh)) SMBgrounded = firedrake.assemble( icepack.interpolate(grounded0 * SMB, Q) * firedrake.dx(mesh)) summaryData = { 'year': [0], 'DVG': [0, 0], 'DVF': [0, 0], 'deltaVF': [0, 0], 'deltaVG': [0, 0], 'gArea': [float(gArea0)], 'fArea': [float(fArea0)], 'meltTot': [float(meltTot)], 'area': float(area), 'SMBgrounded': [float(SMBgrounded)], 'SMBfloating': [float(SMBfloating)], 'dTsum': 1. } # dTsum fixed - code modes needed to change return summaryData
def computeSummaryData(SD, h, s, u, a, melt, SMB, grounded, floating, year, Q, mesh, deltaT, beginTime): ''' Compute summary results and sort in summaryData Save all as float for yaml output ''' deltaVF, deltaVG = \ volumeChange(grounded, floating, h, u, a, mesh) SD['deltaVF'][-1] += deltaVF * deltaT * iceToWater SD['deltaVG'][-1] += deltaVG * deltaT * iceToWater SD['DVF'][-1] += deltaVF * deltaT * iceToWater SD['DVG'][-1] += deltaVG * deltaT * iceToWater print(f"++{year:0.3f} {SD['deltaVF'][-1]/1e9:0.3f} " f"{SD['deltaVG'][-1]/1e9:0.3f} {SD['DVF'][-1]/1e9:0.3f} " f"{SD['DVG'][-1]/1e9:0.3f}") # # If not with half a time step of year return # Need to update this for different update interval (~=1) dTint = abs(year - round(year, 0)) # difference from int year # if before first year or not with half time step of int year return if year < (1. - deltaT * 0.5) or not (dTint < deltaT * 0.5): return False print(f'year {year} runtime {datetime.now() - beginTime}') # gArea = firedrake.assemble(grounded * firedrake.dx(mesh)) SD['year'].append(float(year)) SD['gArea'].append(gArea) SD['fArea'].append(SD['area'] - gArea) # append a new zero value to start incrementing SD['deltaVF'].append(0) SD['deltaVG'].append(0) # append current value to start incrementing SD['DVF'].append(SD['DVF'][-1]) SD['DVG'].append(SD['DVG'][-1]) # meltTot = firedrake.assemble( icepack.interpolate(floating * melt, Q) * firedrake.dx(mesh)) SD['meltTot'].append(float(meltTot)) SMBfloating = firedrake.assemble( icepack.interpolate(floating * SMB, Q) * firedrake.dx(mesh)) SD['SMBfloating'].append(float(SMBfloating)) SMBgrounded = firedrake.assemble( icepack.interpolate(grounded * SMB, Q) * firedrake.dx(mesh)) SD['SMBgrounded'].append(float(SMBgrounded)) # print(f'{year}: Initial melt {SD["meltTot"][0] / 1e9:.2f} current melt ' f' {meltTot / 1e9:.2f}') return True
def getRateFactor(rateFile, Q): """Read rate factor as B and convert to A Parameters ---------- rateFile : str File with rate factor data Q : firedrake function space function space Returns ------- A : firedrake function A Glenns flow law parameter """ Bras = rasterio.open(rateFile) B = icepack.interpolate(Bras, Q) convFactor = 1e-6 * (86400 * 365.25)**(-1. / 3.) A = icepack.interpolate((B * convFactor)**-3, Q) return A
def test_close_to_edge(): n = 32 array = np.array([[(i + j) / n for j in range(n + 1)] for i in range(n + 1)]) missing = -9999.0 array = np.flipud(array) dataset = make_rio_dataset(array, missing) xmin, ymin = 1 / (2 * n), 3 / (4 * n) mesh = make_domain(48, 48, xmin=xmin, ymin=ymin, width=1 / 2, height=1 / 2) Q = firedrake.FunctionSpace(mesh, "CG", 1) q = icepack.interpolate(dataset, Q)
def piecewiseWithDepth(h, floating, meltParams, Q, *argv): """ Melt function that is described piecewise by set of polynomials Parameters ---------- h : firedrake function ice thickness floating : firedrake function floating mask meltParams : dict parameters for melt function Returns ------- firedrake function melt rates """ # compute depth melt = firedrake.Constant(0) for i in range(1, meltParams['numberOfPolynomials'] + 1): poly = meltParams[f'poly{i}'] tmpMelt = firedrake.Constant(poly['coeff'][0]) for j in range(1, poly['deg'] + 1): tmpMelt = tmpMelt + poly['coeff'][j] * h**j melt = melt + tmpMelt * (h > poly['min']) * (h < poly['max']) # Smooth result # melt1 = icepack.interpolate(melt * floating, Q) alpha = 4000 # Default if 'alpha' in meltParams: alpha = meltParams['alpha'] melt1 = icepack.interpolate(melt, Q) melt1 = firedrakeSmooth(melt1, alpha=alpha) # 'totalMelt' given renormalize melt to produce this value if 'totalMelt' in meltParams.keys(): intMelt = firedrake.assemble(melt1 * floating * firedrake.dx) scale = firedrake.Constant(-1.0 * float(meltParams['totalMelt']) / float(intMelt)) else: scale = firedrake.Constant(1.) # melt = icepack.interpolate(melt1 * scale * floating, Q) return melt
def getModelVelocity(baseName, Q, V, minSigma=5, maxSigma=100): """Read in a tiff velocity data set and return firedrake interpolate functions. Parameters ---------- baseName : str baseName should be of the form pattern.*.abc or pattern The wildcard (*) will be filled with the suffixes (vx, vy.) e.g.,pattern.vx.abc.tif, pattern.vy.abc.tif. Q : firedrake function space function space V : firedrake vector space vector space Returns ------- uObs firedrake interp function on V velocity (m/yr) speed firedrake interp function on Q speed in (m) sigmaX firedrake interp function on Q vx error (m) sigmaY firedrake interp function on Q vy error (m) """ # suffixes for products used suffixes = ['vx', 'vy', 'ex', 'ey'] rasters = {} # prep baseName - baseName.*.xyz.tif or baseName.* if '*' not in baseName: baseName += '.*' if '.tif' not in baseName: baseName += '.tif' # read data for suffix in suffixes: myBand = baseName.replace('*', suffix) if not os.path.exists(myBand): u.myerror(f'Velocity/error file - {myBand} - does not exist') rasters[suffix] = rasterio.open(myBand, 'r') # Firedrake interpolators uObs = icepack.interpolate((rasters['vx'], rasters['vy']), V) # force error to be at least 1 to avoid 0 or negatives. sigmaX = icepack.interpolate(rasters['ex'], Q) sigmaX = icepack.interpolate(firedrake.max_value(sigmaX, minSigma), Q) sigmaX = icepack.interpolate(firedrake.min_value(sigmaX, maxSigma), Q) sigmaY = icepack.interpolate(rasters['ey'], Q) sigmaY = icepack.interpolate(firedrake.max_value(sigmaY, minSigma), Q) sigmaY = icepack.interpolate(firedrake.min_value(sigmaY, maxSigma), Q) speed = icepack.interpolate(firedrake.sqrt(inner(uObs, uObs)), Q) # return results return uObs, speed, sigmaX, sigmaY
def readSMB(SMBfile, Q): ''' Read SMB file an limit values to +/- 6 to avoid no data values Returns water equivalent values. ''' if not os.path.exists: myerror(f'readSMB: SMB file ({SMBfile}) does not exist') SMB = mf.getModelVarFromTiff(SMBfile, Q) # avoid any unreasonably large value SMB = icepack.interpolate( firedrake.max_value(firedrake.min_value(SMB, 6), -6), Q) return SMB
def test_interpolating_scalar_field(): n = 32 array = np.array([[(i + j) / n for j in range(n + 1)] for i in range(n + 1)]) missing = -9999.0 array[0, 0] = missing array = np.flipud(array) dataset = make_rio_dataset(array, missing) mesh = make_domain(48, 48, xmin=1 / 4, ymin=1 / 4, width=1 / 2, height=1 / 2) x, y = firedrake.SpatialCoordinate(mesh) Q = firedrake.FunctionSpace(mesh, "CG", 1) p = firedrake.interpolate(x + y, Q) q = icepack.interpolate(dataset, Q) assert firedrake.norm(p - q) / firedrake.norm(p) < 1e-10
def test_nearest_neighbor_interpolation(): n = 32 array = np.array([[(i + j) / n for j in range(n + 1)] for i in range(n + 1)]) missing = -9999.0 array[0, 0] = missing array = np.flipud(array) dataset = make_rio_dataset(array, missing) mesh = make_domain(48, 48, xmin=1 / 4, ymin=1 / 4, width=1 / 2, height=1 / 2) x, y = firedrake.SpatialCoordinate(mesh) Q = firedrake.FunctionSpace(mesh, "CG", 1) p = firedrake.interpolate(x + y, Q) q = icepack.interpolate(dataset, Q, method="nearest") relative_error = firedrake.norm(p - q) / firedrake.norm(p) assert (relative_error > 1e-10) and (relative_error < 1 / n)
def getModelVarFromTiff(myTiff, Q): """Read a model variable from a tiff file using rasterio Parameters ---------- myTiff : str tiff file with a scalar variable Q : firedrake function space function space Returns ------- firedrake function Data from tiff """ if not os.path.exists(myTiff): myerror(f'Geometry file {myTiff} does not exist') x = rasterio.open(myTiff) return icepack.interpolate(x, Q)
def reduceNearGLBeta(s, sOrig, zF, grounded, Q, thresh, limit=False): """Compute beta reduction in area near grounding line where the height above floation is less than thresh. Parameters ---------- s : firedrake function Current surface. sOrig : firedrake function Original surface. zF : firedrake function Flotation height. grounded : firedrake function grounde mask Q : firedrake function space scaler function space for model thresh : float Threshold to determine where to reduce beta (zAbove < thresh) """ # compute original height above flotation for grounded region sAboveOrig = (sOrig - zF) * grounded # avoid negative/zero values sAboveOrig = firedrake.max_value(sAboveOrig, 0.0) # Current height above flotation with negative values zeroed out. sAbove = firedrake.max_value((s - zF) * grounded, 0) # mask so only areas less than thresh but grounded sMask = (sAbove < thresh) * grounded # print(f'{sAbove.dat.data_ro.min()}, {sAbove.dat.data_ro.max()} {thresh}') # Inverse mask sMaskInv = sMask < 1 # scale = fraction of original height above flotation # Use 5 to avoid potentially large ratio at small values. scaleBeta = sAbove / \ firedrake.max_value(firedrake.min_value(thresh, sAboveOrig), 3) if limit: scaleBeta = firedrake.min_value(3, scaleBeta) scaleBeta = scaleBeta * sMask + sMaskInv # scaleBeta = icepack.interpolate(firedrake.min_value(scaleBeta,1.),Q) # sqrt so tau = scale * beta^2 # scaleBeta = icepack.interpolate(firedrake.sqrt(scaleBeta) * grounded, Q) # Removed grounded above because grounded is always applied in friction scaleBeta = icepack.interpolate(firedrake.sqrt(scaleBeta), Q) # print(f'{scaleBeta.dat.data_ro.min()}, {scaleBeta.dat.data_ro.max()}') return scaleBeta
def betaInit(s, h, speed, V, Q, Q1, grounded, inversionParams): """Compute intitial beta using 0.5 taud. Parameters ---------- s : firedrake function model surface elevation h : firedrake function model thickness speed : firedrake function modelled speed V : firedrake vector function space vector function space Q : firedrake function space scalar function space grounded : firedrake function Mask with 1s for grounded 0 for floating. """ # Use a result from prior inversion checkFile = inversionParams['initFile'] Quse = Q if inversionParams['initWithDeg1']: checkFile = f'{inversionParams["inversionResult"]}.deg1' Quse = Q1 if checkFile is not None: betaTemp = mf.getCheckPointVars(checkFile, 'betaInv', Quse)['betaInv'] beta1 = icepack.interpolate(betaTemp, Q) return beta1 # No prior result, so use fraction of taud tauD = firedrake.project(-rhoI * g * h * grad(s), V) # stress = firedrake.sqrt(firedrake.inner(tauD, tauD)) Print('stress', firedrake.assemble(stress * firedrake.dx)) fraction = firedrake.Constant(0.95) U = max_value(speed, 1) C = fraction * stress / U**(1/m) if inversionParams['friction'] == 'schoof': mExp = 1/m + 1 U0 = firedrake.Constant(inversionParams['uThresh']) C = C * (m/(m+1)) * (U0**mExp + U**mExp)**(1/(m+1)) beta = firedrake.interpolate(firedrake.sqrt(C) * grounded, Q) return beta
def reduceNearGLBeta(s, sOrig, zF, grounded, Q, thresh, limit=False): """Compute beta reduction in area near grounding line where the height above floation is less than thresh. Parameters ---------- s : firedrake function Current surface. sOrig : firedrake function Original surface. zF : firedrake function Flotation height. grounded : firedrake function grounde mask Q : firedrake function space scaler function space for model thresh : float Threshold to determine where to reduce beta (zAbove < thresh) """ # compute original height above flotation for grounded region sAboveOrig = (sOrig - zF) * grounded # avoid negative/zero values sAboveOrig = firedrake.max_value(sAboveOrig, 0.001) # Current height above flotation with negative values zeroed out. sAbove = firedrake.max_value((s - zF) * grounded, 0) # mask so only areas less than thresh but grounded sMask = (sAbove <= thresh) * grounded # Inverse mask sMaskInv = sMask < 1 # scale = fraction of of original height above flotation scaleBeta = sAbove / \ firedrake.max_value(firedrake.min_value(thresh, sAboveOrig), 5) if limit: scaleBeta = firedrake.min_value(3, scaleBeta) scaleBeta = scaleBeta * sMask + sMaskInv # scaleBeta = icepack.interpolate(firedrake.min_value(scaleBeta,1.),Q) # sqrt so tau = scale * beta^2 scaleBeta = icepack.interpolate(firedrake.sqrt(scaleBeta) * grounded, Q) return scaleBeta
def thetaInit(Ainit, Q, Q1, grounded, floating, inversionParams): """Compute intitial theta on the ice shelf (not grounded). Parameters ---------- Ainit : firedrake function A Glens flow law A Q : firedrake function space scalar function space Q1 : firedrake function space 1 deg scalar function space used with 'initWithDeg1' grounded : firedrake function Mask with 1s for grounded 0 for floating. floating : firedrake function Mask with 1s for floating 0 for grounded. Returns ------- theta : firedrake function theta for floating ice """ # Get initial file if there is one to init inversion checkFile = inversionParams['initFile'] Quse = Q # Use degree 1 solution if prompted if inversionParams['initWithDeg1']: checkFile = f'{inversionParams["inversionResult"]}.deg1' Quse = Q1 # Now check if there is a file specificed, and if so, init with that if checkFile is not None: Print(f'Init. with theta: {checkFile}') thetaTemp = mf.getCheckPointVars(checkFile, 'thetaInv', Quse)['thetaInv'] thetaInit = icepack.interpolate(thetaTemp, Q) return thetaInit # No initial theta, so use initial A to init inversion Atheta = mf.firedrakeSmooth(Ainit, alpha=1000) theta = firedrake.ln(Atheta) theta = firedrake.interpolate(theta, Q) return theta
def getModelGeometry(geometryFile, Q, smooth=False, alpha=2e3, zFirn=0., rhoI=rhoI, rhoW=rhoW): """Load geometry data for model and create firedrake interpolators Parameters ---------- geometryFile : str Path to a yaml file with bed, surface, thickness, and floatMask Q : firedrake function space function space smooth: bool, optional apply firedrakeSmooth to the result alpha : float, optional parameter that controls the amount of smoothing, which is approximately the smoothing lengthscale in m, by default 2e3 zFirn : float, optional Correct elevation for firn thickness (m), by default 14 m rhoI : [type], optional [description], by default rhoI rhoW : [type], optional [description], by default rhoW Returns ------- zb firedrake interp function bed elevation (m) s firedrake interp function surface elevation (m) h firedrake interp function ice thickness (m) floatMask firedrake interp function mask with 1 for floating 0 for grounded """ # load geometry files try: with open(geometryFile) as fp: geom = yaml.load(fp, Loader=yaml.FullLoader) except Exception: myerror(f'Could not open geomtery file: {geometryFile}') # Load and convert to firedrake fd = {'bed': None, 'surface': None, 'thickness': None, 'floatMask': None} # Read and process data for myVar in geom: print(myVar, geom[myVar]) fd[myVar] = getModelVarFromTiff(geom[myVar], Q) if smooth and alpha > 1 and myVar != 'floatMask': fd[myVar] = firedrakeSmooth(fd[myVar], alpha=alpha) if myVar == 'surface': fd[myVar] = icepack.interpolate(fd[myVar] - zFirn, Q) # If data are smoothed, regenerate a new mask from smoothed results. if smooth and alpha > 1: zF = flotationHeight(fd['bed'], Q, rhoI=rhoI, rhoW=rhoW) fd['floatMask'], g = flotationMask(fd['surface'], zF, Q, rhoI=rhoI, rhoW=rhoW) else: g = icepack.interpolate(fd['floatMask'] < 1, Q) # Don't allow negative values for myVar in ['surface', 'thickness']: fd[myVar] = icepack.interpolate(firedrake.max_value(10, fd[myVar]), Q) for myVar in geom: print(f'{myVar} min/max {fd[myVar].dat.data_ro.min():10.2f} ' f'{fd[myVar].dat.data_ro.max():10.2f}') return fd['bed'], fd['surface'], fd['thickness'], fd['floatMask'], g
def test_interpolating(): u = icepack.interpolate(discretization, lambda x: x[0] * x[1]) assert u.discretization is discretization assert icepack.max(u) <= 1.0
from icepack import rho_ice, rho_water, gravity # All of this is similar to example 1. import make_mesh length, width = 20.0e3, 20.0e3 make_mesh.main(length, width, "rectangle") mesh = icepack.read_msh("rectangle.msh") mesh.refine_global(3) discretization = icepack.make_discretization(mesh, 1) h0, dh = 500.0, 250.0 def thickness(x): return h0 - dh * x[0] / length h = icepack.interpolate(discretization, thickness) u0, du = 200.0, 200.0 v = icepack.interpolate(discretization, lambda x: u0 + du * x[0] / length, lambda x: 0.0) theta = icepack.interpolate(discretization, lambda x: 254.15) # For a grounded ice stream, we also need to pick the surface elevation (or # equivalently the bed elevation). Make the surface elevation at 50m above # flotation. def surface(x): return (1 - rho_ice / rho_water) * thickness(x) + 20 s = icepack.interpolate(discretization, surface) # We also need to set the friction coefficient, which we'll choose to be a low # value throughout but with a jump in the center of the domain.
def test_interpolating_to_mesh(): # Make the mesh the square `[1/4, 3/4] x [1/4, 3/4]` nx, ny = 32, 32 mesh = firedrake.UnitSquareMesh(nx, ny) x, y = firedrake.SpatialCoordinate(mesh) Vc = mesh.coordinates.function_space() f = firedrake.interpolate( firedrake.as_vector((x / 2 + 1 / 4, y / 2 + 1 / 4)), Vc) mesh.coordinates.assign(f) # Set up the geometry of the gridded data set x0 = (0, 0) n = 32 dx = 1.0 / n transform = rasterio.transform.from_origin(west=0.0, north=1.0, xsize=dx, ysize=dx) # Interpolate a scalar field array = np.array([[dx * (i + j) for j in range(n + 1)] for i in range(n + 1)]) missing = -9999.0 array[0, 0] = missing array = np.flipud(array) memfile = rasterio.MemoryFile(ext='.tif') opts = { 'driver': 'GTiff', 'count': 1, 'width': n, 'height': n, 'transform': transform, 'nodata': -9999 } with memfile.open(**opts) as dataset: dataset.write(array, indexes=1) dataset = memfile.open() Q = firedrake.FunctionSpace(mesh, family='CG', degree=1) p = firedrake.interpolate(x + y, Q) q = icepack.interpolate(dataset, Q) assert firedrake.norm(p - q) / firedrake.norm(p) < 1 / n # Interpolate a vector field array_vx = np.copy(array) array_vy = np.array([[dx * (j - i) for j in range(n + 1)] for i in range(n + 1)]) array_vy[-1, -1] = -9999.0 array_vy = np.flipud(array_vy) memfile_vx, memfile_vy = rasterio.MemoryFile(), rasterio.MemoryFile() with memfile_vx.open(**opts) as vx, memfile_vy.open(**opts) as vy: vx.write(array_vx, indexes=1) vy.write(array_vy, indexes=1) vx, vy = memfile_vx.open(), memfile_vy.open() V = firedrake.VectorFunctionSpace(mesh, family='CG', degree=1) u = firedrake.interpolate(firedrake.as_vector((x + y, x - y)), V) v = icepack.interpolate((vx, vy), V) assert firedrake.norm(u - v) / firedrake.norm(u) < 1 / n
def velocity(x): from icepack import rho_ice, rho_water, gravity, rate_factor u0 = 100.0 rho = rho_ice * (1 - rho_ice / rho_water) A = (rho * gravity * h0 / 4)**3 * rate_factor(temp) q = 1 - (1 - (dh / h0) * (x[0] / length))**4 return u0 + 0.25 * A * q * length * h0 / dh # Read the mesh from a file, refine it a bit, and make a discretization. mesh = icepack.read_msh("rectangle.msh") mesh.refine_global(3) discretization = icepack.make_discretization(mesh, 1) # Interpolate all the exact data to the discretization we just made. h = icepack.interpolate(discretization, thickness) u0 = icepack.interpolate(discretization, velocity, lambda x: 0.0) theta = icepack.interpolate(discretization, temperature) # --- New stuff! --- # The boundary segments of the mesh have different numeric IDs in order to help # set where Dirichlet or Neumann boundary conditions apply. We can check what # they are by plotting the mesh. fig, ax = plt.subplots() ax.set_aspect('equal') icepack.plot.plot_mesh(ax, mesh) plt.show(fig) # From this figure we can see that boundary region 1 is where we want Dirichlet
def main(): ''' Main for foward model simulation ''' forwardParams, inversionParams = parsePigForwardArgs() print(forwardParams) # # Read mesh and setup function spaces mesh, Q, V, meshOpts = \ mf.setupMesh(forwardParams['mesh'], degree=forwardParams['degree'], meshOversample=inversionParams['meshOversample']) beta0, theta0, A0, s0, h0, zb, floating0, grounded0, uInv, uObs = \ mf.getInversionData(forwardParams['inversionResult'], Q, V) # Compute masks for combining A0 with theta0 groundedSmooth, floatingSmooth = setupTaperedMasks(inversionParams, grounded0, floating0) SMB = readSMB(forwardParams['SMB'], Q) # meltModel, meltParams = setupMelt(forwardParams) # Setup ice stream model frictionLaw = setupFriction(forwardParams) # forwardModel = icepack.models.IceStream(friction=frictionLaw, viscosity=taperedViscosity) opts = { 'dirichlet_ids': meshOpts['dirichlet_ids'], 'diagnostic_solver_parameters': { 'max_iterations': 150 } } forwardSolver = icepack.solvers.FlowSolver(forwardModel, **opts) # initial solve uThresh = firedrake.Constant(forwardParams['uThresh']) u0 = forwardSolver.diagnostic_solve(velocity=uObs, thickness=h0, surface=s0, fluidity=A0, beta=beta0, theta=theta0, grounded=grounded0, floating=floating0, groundedSmooth=groundedSmooth, floatingSmooth=floatingSmooth, uThresh=uThresh) # Get fresh or reloaded summary data - reset restart if not data summaryData = initSummary(grounded0, floating0, h0, u0, meltModel, meltParams, SMB, Q, mesh, forwardParams) # chk = setupOutputs(forwardParams, inversionParams, meltParams) deltaT = forwardParams['deltaT'] # copy original state if not forwardParams['restart']: startYear = 0 h, hLast, s, u, zF, grounded, floating = \ initialState(h0, s0, u0, zb, grounded0, floating0, Q) else: # load state to restart startYear, h, hLast, s, u, zF, grounded, floating = \ doRestart(forwardParams, zb, deltaT, chk, Q, V) # Sanity/consistency check print(f'area {summaryData["area"]}') mf.velocityError(u0, uInv, summaryData['area'], 'Difference with uInv') mf.velocityError(u0, uObs, summaryData['area'], 'Difference with uObs') mf.velocityError(uInv, uObs, summaryData['area'], 'Difference with uInv/uObs') # setup plots plotData = { 'plotResult': forwardParams['plotResult'], 'mapPlotLimits': forwardParams['mapPlotLimits'], 'figM': None, 'axesM': None } setupTimesSeriesPlots(plotData, forwardParams) setupProfilePlots(plotData, forwardParams, h) profilePlots(-1e-12, plotData, uObs, s0, h0, zb, zF, None, Q, first=True) # beginTime = datetime.now() betaScale = grounded * 1 beta = beta0 print('Loop') if startYear > forwardParams['nYears']: myerror(f'startYear ({startYear}) is greater than nYears ' f'({forwardParams["nYears"]}). Restart ' f'{forwardParams["nYears"]} so sim may be done') times = np.arange(startYear, forwardParams['nYears'] + deltaT, deltaT) for t in np.around(times, decimals=6): # hLast = h.copy(deepcopy=True) # Save for next summary calc trend = meltTrend(t, forwardParams) melt = meltModel(h, floating, meltParams, Q, u, trend=trend) * \ meltAnomaly(t, forwardParams) a = icepack.interpolate((SMB + melt) * waterToIce, Q) # h = forwardSolver.prognostic_solve(forwardParams['deltaT'], thickness=h, velocity=u, accumulation=a, thickness_inflow=h0) # Don't allow to go too thin. h = checkThickness(h, 30, Q, t, h0) # Compute surface and masks s = computeSurface(h, zb, Q) floating, grounded = mf.flotationMask(s, zF, Q) # Scale beta near gl if t > forwardParams['tBetaScale']: betaScale = mf.reduceNearGLBeta(s, s0, zF, grounded, Q, forwardParams['GLThresh']) beta = icepack.interpolate(beta0 * betaScale, Q) else: beta = beta0 uThresh = firedrake.Constant(forwardParams['uThresh']) u = forwardSolver.diagnostic_solve(velocity=u, thickness=h, surface=s, fluidity=A0, beta=beta, theta=theta0, uThresh=uThresh, floating=floating, grounded=grounded, groundedSmooth=groundedSmooth, floatingSmooth=floatingSmooth) print('.', end='', flush=True) # # Compute summary data and plot if indicated if computeSummaryData(summaryData, h, s, u, a, melt, SMB, grounded, floating, t, Q, mesh, deltaT, beginTime): if plotData['plotResult']: # Only do final plot (below) mapPlots(plotData, t, melt, betaScale, h, hLast, deltaT, Q) # For now ouput fields at same interval as summary data outputTimeStep(t, chk, h=h, s=s, u=u, grounded=grounded, floating=floating) saveSummaryData(forwardParams, summaryData, tmp=True) # timeSeriesPlots(t, plotData, summaryData) profilePlots(t, plotData, u, s, h, zb, zF, melt, Q) # # End Simulation so save results mapPlots(plotData, t, melt, betaScale, h, hLast, deltaT, Q) saveSummaryData(forwardParams, summaryData) savePlots(plotData, forwardParams) # Show final result if requested if plotData['plotResult']: print('Show Plot') plt.show()
def checkThickness(h, thresh, Q, t, h0): ''' Do not let ice get too thin ''' # h = icepack.interpolate(firedrake.max_value(thresh, h), Q) return h
def computeSurface(h, zb, Q): '''Hack of icepack version to uses different rhoI/rhoW ''' s = firedrake.max_value(h + zb, h * (1 - rhoI / rhoW)) return icepack.interpolate(s, Q)
plt.show(fig) # This mesh is too coarse. We can refine all the cells for better resolution. mesh.refine_global(4) print("Number of vertices, cells: {0} {1}" .format(mesh.n_vertices(), mesh.n_active_cells())) # To represent fields defined over this domain, we first need to create a # finite element discretization. The second argument is the polynomial degree. discretization = icepack.make_discretization(mesh, 1) # To test this out, we'll make a random Fourier series. def f(x): k1 = (2.0, 3.0) k2 = (-5.0, 1.0) phi1 = 2 * np.pi * (k1[0] * x[0] / length + k1[1] * x[1] / width) phi2 = 2 * np.pi * (k2[0] * x[0] / length + k2[1] * x[1] / width) return np.sin(phi1) + np.sin(phi2) # We'll be using this function a lot. It takes an analytically defined field # `f` and interpolates it to a finite element discretization. The fields we'll # be interpolating are input data, either defined by hand or from observations. u = icepack.interpolate(discretization, f) # Finally, to make sure it's all working right, we can plot it. import icepack.plot fig, ax = plt.subplots() icepack.plot.plot_field(ax, u) plt.show(fig)
preprocess.main() # Read in the observational data. vx_obs = icepack.read_arc_ascii_grid(open("ross-vx.txt", "r")) vy_obs = icepack.read_arc_ascii_grid(open("ross-vy.txt", "r")) h_obs = icepack.read_arc_ascii_grid(open("ross-h.txt", "r")) mesh = icepack.read_msh("ross.msh") fig, ax = plt.subplots() ax.set_aspect('equal') icepack.plot.plot_mesh(ax, mesh) plt.show(fig) discretization = icepack.make_discretization(mesh, 1) v = icepack.interpolate(discretization, vx_obs, vy_obs) h = icepack.interpolate(discretization, h_obs) # Make a dumb guess for the ice temperature. In "real life", you would want to # use an inverse method that would tune the temperature to fit observations. theta = icepack.interpolate(discretization, lambda x: 253.0) # Solve for the ice velocity, assuming this guess for the temperature. dirichlet_boundary_ids = {2, 3, 4, 5} ice_shelf = icepack.IceShelf(dirichlet_boundary_ids) u = ice_shelf.solve(h, theta, v) print("Misfit between computed and observed velocities: {}" .format(icepack.dist(u, v) / icepack.norm(v))) fig, axes = plt.subplots(ncols=2, sharey=True)
import numpy as np import matplotlib.pyplot as plt import icepack, icepack.plot import preprocess preprocess.main() # All of this is the same as example 3. vx_obs = icepack.read_arc_ascii_grid(open("ross-vx.txt", "r")) vy_obs = icepack.read_arc_ascii_grid(open("ross-vy.txt", "r")) h_obs = icepack.read_arc_ascii_grid(open("ross-h.txt", "r")) mesh = icepack.read_msh("ross.msh") discretization = icepack.make_discretization(mesh, 1) v = icepack.interpolate(discretization, vx_obs, vy_obs) h0 = icepack.interpolate(discretization, h_obs) theta = icepack.interpolate(discretization, lambda x: 253.0) dirichlet_boundary_ids = {2, 3, 4, 5} ice_shelf = icepack.IceShelf(dirichlet_boundary_ids); u0 = ice_shelf.solve(h0, theta, v) # And this should be familiar from example 2. dt = min(1.0, icepack.compute_timestep(0.5, u0)) # Make a steady-state accumulation rate field. a0 = icepack.interpolate(discretization, lambda x: 0.0) a0 = (ice_shelf.solve(dt, h0, a0, u0, h0) - h0) / dt # Really hacky way to compute the average of a field.