def constrain(self, upper=None, lower=None): # Constraint can be set to none if we don't want to change constraints = (new if new is not None else old for new, old in zip((upper, lower), self.constraints)) faces = (self.mesh.facesLeft, self.mesh.facesRight) indexes = [0, -1] self._exterior_flux = 0 for val, index, face in zip(constraints, indexes, faces): if val is None: continue try: self.var.constrain(val.into("K"), face) ## Constrain as temperature except DimensionalityError: # Fixed flux boundary condition # flux density v = val.into("W/m^2") flux = F.FaceVariable(self.mesh, 'exterior_flux', value=v) self._exterior_flux += (self.mesh.exteriorFaces[index] * flux).divergence except AttributeError: pass
def createLinearMesh(self, sections, Acs, As): """ Define mesh """ # Cross sectional area self.AcsMult = Acs # Area multiplier for face area self.areaMult = Acs # Surface area per unit length self.As = As # Create the meshes and emissivity calculator for each segment self.emissivityCalculator = sm.SectionCalculator() dx = [] i = 0 for sec in sections: numElements = np.ceil(sec['length'] / sec['meshSize']) self.emissivityCalculator.addSection( (i, i + numElements - 1), sm.Interpolator1D([sec['temperature1'], sec['temperature2']], [sec['emissivity1'], sec['emissivity2']]) ) dx += [sec['length'] / numElements] * numElements i += numElements self.radiation['emissivity'] = np.zeros((len(dx))) self.mesh = fp.Grid1D(dx = dx) self.meshType = 'Linear' # Define variable self.T = fp.CellVariable(name = "temperature", mesh = self.mesh, value = 0.) self.thermCond = fp.FaceVariable(mesh = self.mesh) self.emissivity = fp.CellVariable(mesh = self.mesh)
def forwardEvo(phi, tStart, tEnd, path, saveResults): # Initialize step counter and evolution time step = 0 tEvo = tStart # Extract cell centers and faces from mesh X, P = Mesh.cellCenters() xFace, pFace = Mesh.faceCenters() # Create plot for starting distribution target = 'forwardEvo-Step-' + '%05d' % step + '.png' genPlots(phi, tEvo, saveResults, target, path) # Continue to evolve distribution until specified end time while (tEvo < tEnd): # Find current spring constant and location of minimum kT = springConstant(tEvo) zT = potentialMinimum(tEvo) # Calculate time step size timeStepDuration = (1. / (SubDiv * Nx)) * (1. / numpy.sqrt(1. / Tau**2 + kT)) if (tEvo + timeStepDuration > tEnd): timeStepDuration = tEnd - tEvo # Create Diffusion Term gxx = numpy.zeros(X.shape) gxp = numpy.zeros(X.shape) gpx = numpy.zeros(X.shape) gpp = numpy.ones(X.shape) * (2. / Tau) dTerm = fipy.DiffusionTerm( fipy.CellVariable(mesh=Mesh, value=[[gxx, gxp], [gpx, gpp]])) del gxx, gxp, gpx, gpp # Create convection term uX = pFace uP = -kT * (xFace - zT) - (2. / Tau) * pFace cCoeff = fipy.FaceVariable(mesh=Mesh, value=[uX, uP]) cTerm = fipy.ExponentialConvectionTerm(cCoeff) del uX, uP, cCoeff # Create evolution equation eq = fipy.TransientTerm() + cTerm == dTerm # Specify solver solver = fipy.solvers.pysparse.LinearLUSolver(tolerance=10**-15, \ iterations=1000, precon=None) # Evolve system eq.solve(var=phi, dt=timeStepDuration, solver=solver) tEvo = tEvo + timeStepDuration step = step + 1 # Check normalization for possible errors norm = fipy.numerix.sum(phi.value, axis=0) * Dx * Dp if (abs(norm - 1) > 10**(-13)): s1 = 'Distribution is no longer normalized.\n' s2 = 'Abs(1-Norm) = ' s3 = repr(norm - 1.) raise RuntimeError(s1 + s2 + s3) del kT, zT, timeStepDuration, cTerm, eq, norm # Create plot of current distribution target = 'forwardEvo-Step-' + '%05d' % step + '.png' genPlots(phi, tEvo, saveResults, target, path) del tEvo, X, P, xFace, pFace return phi
def T_value(phi, ele, cmask, peat_bottom_arr): # Some inputs are in fipy CellVariable type gwt = (phi.value * cmask.value - ele).reshape(ny, nx) # T(h) - T(bottom) T = transmissivity(gwt, t0, t1, t2, t_sapric_coef) - transmissivity( peat_bottom_arr, t0, t1, t2, t_sapric_coef) # d <0 means tra_to_cut is greater than the other transmissivity, which in turn means that # phi is below the impermeable bottom. We allow phi to have those values, but # the transmissivity is in those points is equal to zero (as if phi was exactly at the impermeable bottom). T[T < 0] = 1e-3 # Small non-zero value not to wreck the computation Tcell = fp.CellVariable(mesh=mesh, value=T.flatten( )) # diffusion coefficient, transmissivity. As a cell variable. Tface = fp.FaceVariable(mesh=mesh, value=Tcell.arithmeticFaceValue.value ) # THe correct Face variable. return Tface.value
def D_value(phi, ele, tra_to_cut, cmask, drmask_not): # Some inputs are in fipy CellVariable type gwt = phi.value * cmask.value - ele d = hydro_utils.peat_map_h_to_tra( soil_type_mask=peat_type_mask, gwt=gwt, h_to_tra_and_C_dict=httd) - tra_to_cut # d <0 means tra_to_cut is greater than the other transmissivity, which in turn means that # phi is below the impermeable bottom. We allow phi to have those values, but # the transmissivity is in those points is equal to zero (as if phi was exactly at the impermeable bottom). d[d < 0] = 1e-3 # Small non-zero value not to wreck the computation dcell = fp.CellVariable( mesh=mesh, value=d ) # diffusion coefficient, transmissivity. As a cell variable. dface = fp.FaceVariable(mesh=mesh, value=dcell.arithmeticFaceValue.value ) # THe correct Face variable. return dface.value
def get_vars(params, set_eta, mesh): """Get the variables Args: params: the parameter dict set_eta: function to set the initial value of the phase field mesh: the FiPy mesh Returns: a dictionary of the variables (eta, d2f) """ return pipe( dict( eta=fp.CellVariable(mesh=mesh, hasOld=True, value=params["eta0"], name="eta"), d2f=fp.FaceVariable(mesh=mesh, name="d2f"), ), do(set_eta(params, mesh)), )
def test_steady_state(): """ Test that we can create a steady-state model using FiPy that is similar to that given by our 'naive' solver """ continental_crust.heat_generation = u(1, 'mW/m^3') _ = continental_crust.to_layer(u(3000, 'm')) section = Section([_]) heatflow = u(15, 'mW/m^2') surface_temperature = u(0, 'degC') m = continental_crust q = heatflow k = m.conductivity a = m.heat_generation Cp = m.specific_heat rho = m.density def simple_heat_flow(x): # Density and heat capacity matter don't matter in steady-state T0 = surface_temperature return T0.to('K') + q * x / k + a / (2 * k) * x**2 p = simple_heat_flow(section.cell_centers) dx = section.cell_sizes[0] test_profile = p.into('degC') grad = (-q / k).into('K/m') # Divergence div = (-a / k).into('K/m^2') a_ = -N.gradient(N.gradient(test_profile, dx), dx) assert all(a_ < 0) assert N.allclose(a_.min(), div) res = steady_state(section, heatflow, surface_temperature) profile = res.profile.into('degC') assert N.allclose(test_profile, profile) solver = FiniteSolver(_) solver.constrain(surface_temperature, None) solver.constrain(heatflow, None) res2 = solver.steady_state() # Make sure it's nonlinear and has constant 2nd derivative arr = solver.var.faceGrad.divergence.value assert N.allclose(sum(arr - arr[0]), 0) assert N.allclose(arr.mean(), div) # Test simple finite element model mesh = F.Grid1D(nx=section.n_cells, dx=section.cell_sizes[0].into('m')) T = F.CellVariable(name="Temperature", mesh=mesh) T.constrain(surface_temperature.into("K"), mesh.facesLeft) A = F.FaceVariable(mesh=mesh, value=m.diffusivity.into("m**2/s")) rad_heat = F.CellVariable(mesh=mesh, value=(a / Cp / rho).into("K/s")) D = F.DiffusionTerm(coeff=A) + F.ImplicitSourceTerm(coeff=rad_heat) sol = D.solve(var=T) val = T.faceGrad.divergence.value arr = T.value - 273.15 #assert N.allclose(val.mean(), div) assert N.allclose(test_profile, arr) P = res2.profile.into('degC') assert N.allclose(test_profile, P)
pressureRelaxation = 0.8 velocityRelaxation = 0.5 meshmodule = import_module("mesh{}".format(params["problem"])) (mesh, inlet, outlet, walls, top_right) = meshmodule.mesh_and_boundaries(params) volumes = fp.CellVariable(mesh=mesh, value=mesh.cellVolumes) pressure = fp.CellVariable(mesh=mesh, name="$p$") pressureCorrection = fp.CellVariable(mesh=mesh, name="$p'$") xVelocity = fp.CellVariable(mesh=mesh, name="$u_x$") yVelocity = fp.CellVariable(mesh=mesh, name="$u_y$") velocity = fp.FaceVariable(mesh=mesh, name=r"$\vec{u}$", rank=1) xVelocityEq = fp.DiffusionTerm(coeff=viscosity) - pressure.grad.dot( [[1.], [0.]]) + density * gravity[0] yVelocityEq = fp.DiffusionTerm(coeff=viscosity) - pressure.grad.dot( [[0.], [1.]]) + density * gravity[1] ap = fp.CellVariable(mesh=mesh, value=1.) coeff = 1. / ap.arithmeticFaceValue * mesh._faceAreas * mesh._cellDistances x, y = mesh.cellCenters top_right_cell = fp.CellVariable(mesh=mesh, value=(x > max(x) - params["cellSize"]) & (y > max(y) - params["cellSize"])) large_value = 1e10
def hydrology(solve_mode, nx, ny, dx, dy, days, ele, phi_initial, catchment_mask, wt_canal_arr, boundary_arr, peat_type_mask, httd, tra_to_cut, sto_to_cut, diri_bc=0.0, neumann_bc=None, plotOpt=False, remove_ponding_water=True, P=0.0, ET=0.0, dt=1.0): """ INPUT: - ele: (nx,ny) sized NumPy array. Elevation in m above c.r.p. - Hinitial: (nx,ny) sized NumPy array. Initial water table in m above c.r.p. - catchment mask: (nx,ny) sized NumPy array. Boolean array = True where the node is inside the computation area. False if outside. - wt_can_arr: (nx,ny) sized NumPy array. Zero everywhere except in nodes that contain canals, where = wl of the canal. - value_for_masked: DEPRECATED. IT IS NOW THE SAME AS diri_bc. - diri_bc: None or float. If None, Dirichlet BC will not be implemented. If float, this number will be the BC. - neumann_bc: None or float. If None, Neumann BC will not be implemented. If float, this is the value of grad phi. - P: Float. Constant precipitation. mm/day. - ET: Float. Constant evapotranspiration. mm/day. """ # dneg = [] track_WT_drained_area = (239, 166) track_WT_notdrained_area = (522, 190) ele[~catchment_mask] = 0. ele = ele.flatten() phi_initial = (phi_initial + 0.0 * np.zeros((ny, nx))) * catchment_mask # phi_initial = phi_initial * catchment_mask phi_initial = phi_initial.flatten() if len(ele) != nx * ny or len(phi_initial) != nx * ny: raise ValueError("ele or Hinitial are not of dim nx*ny") mesh = fp.Grid2D(dx=dx, dy=dy, nx=nx, ny=ny) phi = fp.CellVariable( name='computed H', mesh=mesh, value=phi_initial, hasOld=True) #response variable H in meters above reference level if diri_bc != None and neumann_bc == None: phi.constrain(diri_bc, mesh.exteriorFaces) elif diri_bc == None and neumann_bc != None: phi.faceGrad.constrain(neumann_bc * mesh.faceNormals, where=mesh.exteriorFaces) else: raise ValueError( "Cannot apply Dirichlet and Neumann boundary values at the same time. Contradictory values." ) #*******omit areas outside the catchment. c is input cmask = fp.CellVariable(mesh=mesh, value=np.ravel(catchment_mask)) cmask_not = fp.CellVariable(mesh=mesh, value=np.array(~cmask.value, dtype=int)) # *** drain mask or canal mask dr = np.array(wt_canal_arr, dtype=bool) dr[np.array(wt_canal_arr, dtype=bool) * np.array( boundary_arr, dtype=bool )] = False # Pixels cannot be canals and boundaries at the same time. Everytime a conflict appears, boundaries win. This overwrites any canal water level info if the canal is in the boundary. drmask = fp.CellVariable(mesh=mesh, value=np.ravel(dr)) drmask_not = fp.CellVariable( mesh=mesh, value=np.array(~drmask.value, dtype=int) ) # Complementary of the drains mask, but with ints {0,1} # mask away unnecesary stuff # phi.setValue(np.ravel(H)*cmask.value) # ele = ele * cmask.value source = fp.CellVariable(mesh=mesh, value=0.) # cell variable for source/sink # CC=fp.CellVariable(mesh=mesh, value=C(phi.value-ele)) # differential water capacity def D_value(phi, ele, tra_to_cut, cmask, drmask_not): # Some inputs are in fipy CellVariable type gwt = phi.value * cmask.value - ele d = hydro_utils.peat_map_h_to_tra( soil_type_mask=peat_type_mask, gwt=gwt, h_to_tra_and_C_dict=httd) - tra_to_cut # d <0 means tra_to_cut is greater than the other transmissivity, which in turn means that # phi is below the impermeable bottom. We allow phi to have those values, but # the transmissivity is in those points is equal to zero (as if phi was exactly at the impermeable bottom). d[d < 0] = 1e-3 # Small non-zero value not to wreck the computation dcell = fp.CellVariable( mesh=mesh, value=d ) # diffusion coefficient, transmissivity. As a cell variable. dface = fp.FaceVariable(mesh=mesh, value=dcell.arithmeticFaceValue.value ) # THe correct Face variable. return dface.value def C_value(phi, ele, sto_to_cut, cmask, drmask_not): # Some inputs are in fipy CellVariable type gwt = phi.value * cmask.value - ele c = hydro_utils.peat_map_h_to_sto( soil_type_mask=peat_type_mask, gwt=gwt, h_to_tra_and_C_dict=httd) - sto_to_cut c[c < 0] = 1e-3 # Same reasons as for D ccell = fp.CellVariable( mesh=mesh, value=c ) # diffusion coefficient, transmissivity. As a cell variable. return ccell.value D = fp.FaceVariable( mesh=mesh, value=D_value(phi, ele, tra_to_cut, cmask, drmask_not)) # THe correct Face variable. C = fp.CellVariable( mesh=mesh, value=C_value(phi, ele, sto_to_cut, cmask, drmask_not)) # differential water capacity largeValue = 1e20 # value needed in implicit source term to apply internal boundaries if plotOpt: big_4_raster_plot( title='Before the computation', raster1=( (hydro_utils.peat_map_h_to_tra(soil_type_mask=peat_type_mask, gwt=(phi.value - ele), h_to_tra_and_C_dict=httd) - tra_to_cut) * cmask.value * drmask_not.value).reshape(ny, nx), raster2=(ele.reshape(ny, nx) - wt_canal_arr) * dr * catchment_mask, raster3=ele.reshape(ny, nx), raster4=(ele - phi.value).reshape(ny, nx)) # for later cross-section plots y_value = 270 # print "first cross-section plot" # ele_with_can = copy.copy(ele).reshape(ny,nx) # ele_with_can = ele_with_can * catchment_mask # ele_with_can[wt_canal_arr > 0] = wt_canal_arr[wt_canal_arr > 0] # plot_line_of_peat(ele_with_can, y_value=y_value, title="cross-section", color='green', nx=nx, ny=ny, label="ele") # ********************************** PDE, STEADY STATE ********************************** if solve_mode == 'steadystate': if diri_bc != None: # diri_boundary = fp.CellVariable(mesh=mesh, value= np.ravel(diri_boundary_value(boundary_mask, ele2d, diri_bc))) eq = 0. == ( fp.DiffusionTerm(coeff=D) + source * cmask * drmask_not - fp.ImplicitSourceTerm(cmask_not * largeValue) + cmask_not * largeValue * np.ravel(boundary_arr) - fp.ImplicitSourceTerm(drmask * largeValue) + drmask * largeValue * (np.ravel(wt_canal_arr)) # - fp.ImplicitSourceTerm(bmask_not*largeValue) + bmask_not*largeValue*(boundary_arr) ) elif neumann_bc != None: raise NotImplementedError("Neumann BC not implemented yet!") cmask_face = fp.FaceVariable(mesh=mesh, value=np.array( cmask.arithmeticFaceValue.value, dtype=bool)) D[cmask_face.value] = 0. eq = 0. == ( fp.DiffusionTerm(coeff=D) + source * cmask * drmask_not - fp.ImplicitSourceTerm(cmask_not * largeValue) + cmask_not * largeValue * (diri_bc) - fp.ImplicitSourceTerm(drmask * largeValue) + drmask * largeValue * (np.ravel(wt_canal_arr)) # + fp.DiffusionTerm(coeff=largeValue * bmask_face) # - fp.ImplicitSourceTerm((bmask_face * largeValue *neumann_bc * mesh.faceNormals).divergence) ) elif solve_mode == 'transient': if diri_bc != None: # diri_boundary = fp.CellVariable(mesh=mesh, value= np.ravel(diri_boundary_value(boundary_mask, ele2d, diri_bc))) eq = fp.TransientTerm(coeff=C) == ( fp.DiffusionTerm(coeff=D) + source * cmask * drmask_not - fp.ImplicitSourceTerm(cmask_not * largeValue) + cmask_not * largeValue * np.ravel(boundary_arr) - fp.ImplicitSourceTerm(drmask * largeValue) + drmask * largeValue * (np.ravel(wt_canal_arr)) # - fp.ImplicitSourceTerm(bmask_not*largeValue) + bmask_not*largeValue*(boundary_arr) ) elif neumann_bc != None: raise NotImplementedError("Neumann BC not implemented yet!") #******************************************************** max_sweeps = 10 # inner loop. avg_wt = [] wt_track_drained = [] wt_track_notdrained = [] cumulative_Vdp = 0. #********Finite volume computation****************** for d in range(days): source.setValue( (P[d] - ET[d]) * .001 * np.ones(ny * nx) ) # source/sink. P and ET are in mm/day. The factor of 10^-3 converst to m/day. It does not matter that there are 100 x 100 m^2 in one pixel print("(d, P - ET) = ", (d, (P[d] - ET[d]))) if plotOpt and d != 0: # print "one more cross-section plot" plot_line_of_peat(phi.value.reshape(ny, nx), y_value=y_value, title="cross-section", color='cornflowerblue', nx=nx, ny=ny, label=d) res = 0.0 phi.updateOld() D.setValue(D_value(phi, ele, tra_to_cut, cmask, drmask_not)) C.setValue(C_value(phi, ele, tra_to_cut, cmask, drmask_not)) for r in range(max_sweeps): resOld = res res = eq.sweep(var=phi, dt=dt) # solve linearization of PDE #print "sum of Ds: ", np.sum(D.value)/1e8 #print "average wt: ", np.average(phi.value-ele) #print 'residue diference: ', res - resOld if abs(res - resOld) < 1e-7: break # it has reached to the solution of the linear system if solve_mode == 'transient': #solving in steadystate will remove water only at the very end if remove_ponding_water: s = np.where( phi.value > ele, ele, phi.value ) # remove the surface water. This also removes phi in those masked values (for the plot only) phi.setValue(s) # set new values for water table if (D.value < 0.).any(): print("Some value in D is negative!") # For some plots avg_wt.append(np.average(phi.value - ele)) wt_track_drained.append( (phi.value - ele).reshape(ny, nx)[track_WT_drained_area]) wt_track_notdrained.append( (phi.value - ele).reshape(ny, nx)[track_WT_notdrained_area]) """ Volume of dry peat calc.""" not_peat = np.ones(shape=peat_type_mask.shape) # Not all soil is peat! not_peat[peat_type_mask == 4] = 0 # NotPeat not_peat[peat_type_mask == 5] = 0 # OpenWater peat_vol_weights = utilities.PeatV_weight_calc( np.array(~dr * catchment_mask * not_peat, dtype=int)) dry_peat_volume = utilities.PeatVolume(peat_vol_weights, (ele - phi.value).reshape( ny, nx)) cumulative_Vdp = cumulative_Vdp + dry_peat_volume print("avg_wt = ", np.average(phi.value - ele)) # print "wt drained = ", (phi.value - ele).reshape(ny,nx)[track_WT_drained_area] # print "wt not drained = ", (phi.value - ele).reshape(ny,nx)[track_WT_notdrained_area] print("Cumulative vdp = ", cumulative_Vdp) if solve_mode == 'steadystate': #solving in steadystate we remove water only at the very end if remove_ponding_water: s = np.where( phi.value > ele, ele, phi.value ) # remove the surface water. This also removes phi in those masked values (for the plot only) phi.setValue(s) # set new values for water table # Areas with WT <-1.0; areas with WT >0 # plot_raster_by_value((ele-phi.value).reshape(ny,nx), title="ele-phi in the end, colour keys", bottom_value=0.5, top_value=0.01) if plotOpt: big_4_raster_plot( title='After the computation', raster1=( (hydro_utils.peat_map_h_to_tra(soil_type_mask=peat_type_mask, gwt=(phi.value - ele), h_to_tra_and_C_dict=httd) - tra_to_cut) * cmask.value * drmask_not.value).reshape(ny, nx), raster2=(ele.reshape(ny, nx) - wt_canal_arr) * dr * catchment_mask, raster3=ele.reshape(ny, nx), raster4=(ele - phi.value).reshape(ny, nx)) fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(12, 9), dpi=80) x = np.arange(-21, 1, 0.1) axes[0, 0].plot(httd[1]['hToTra'](x), x) axes[0, 0].set(title='hToTra', ylabel='depth') axes[0, 1].plot(httd[1]['C'](x), x) axes[0, 1].set(title='C') axes[1, 0].plot() axes[1, 0].set(title="Nothing") axes[1, 1].plot(avg_wt) axes[1, 1].set(title="avg_wt_over_time") # plot surface in cross-section ele_with_can = copy.copy(ele).reshape(ny, nx) ele_with_can = ele_with_can * catchment_mask ele_with_can[wt_canal_arr > 0] = wt_canal_arr[wt_canal_arr > 0] plot_line_of_peat(ele_with_can, y_value=y_value, title="cross-section", nx=nx, ny=ny, label="surface", color='peru', linewidth=2.0) plt.show() # change_in_canals = (ele-phi.value).reshape(ny,nx)*(drmask.value.reshape(ny,nx)) - ((ele-H)*drmask.value).reshape(ny,nx) # resulting_phi = phi.value.reshape(ny,nx) phi.updateOld() return (phi.value - ele).reshape(ny, nx)
def QAx(self): return fp.FaceVariable(mesh = self.mesh, value = - self.thermCond * self.areaMult * self.mesh.scaledFaceAreas * self.T.faceGrad)
def model_heat_transport(Lx, Ly, alpha, beta, Lxmin, cellsize_wedge_top, cellsize_wedge_bottom, cellsize_footwall, vd, vc, vxa, vya, v_downgoing, sea_lvl_temp, lapse_rate, lab_temp, K, rho, c, H0, e_folding_depth): """ model heat transport using Fipy """ # create mesh mesh = create_rectangle_mesh_with_fault(Lx, Ly, alpha, beta, Lxmin, cellsize_wedge_top, cellsize_wedge_bottom, cellsize_footwall) # get mesh coordinates xyc = mesh.cellCenters() xyf = mesh.faceCenters() # calculate elevation and depth surface_elev = xyc[0] * alpha surface_elev_f = xyf[0] * alpha a = surface_elev < 0 surface_elev[a] = 0.0 b = surface_elev_f < 0 surface_elev_f[b] = 0.0 depth = surface_elev - xyc[1] depthf = surface_elev_f - xyf[1] wedge = xyc[1] >= (xyc[0] * beta) non_wedge = xyc[1] < (xyc[0] * beta) below_wedge = non_wedge * xyc[0] >= 0 outside_wedge = xyc[0] < 0 wedge_f = xyf[1] >= (xyf[0] * beta) non_wedge_f = wedge_f == False below_wedge_f = non_wedge_f * (xyf[0] >= 0) outside_wedge_f = xyf[0] < 0 # calculate heat production hp_exp = 1.0 / e_folding_depth HP = H0 * np.exp(-hp_exp * depth) # set up variable for advection q = fipy.FaceVariable(mesh=mesh, rank=1) # calcuate advection rates in wedge vxc = horizontal_compression_velocity(xyf[0], vc, Lx) vyc = vertical_compression_velocity(xyf[0], xyf[1], vc, alpha, beta, Lx) vxt = vd + vxa vyt = beta * vd + vya vx = vxc + vxt vy = vyc + vyt q[0] = wedge_f * vx q[1] = wedge_f * vy # calculate advection rates below wedge wedge_angle = np.arctan(beta) vx_downgoing = v_downgoing * np.cos(wedge_angle) vy_downgoing = v_downgoing * np.sin(wedge_angle) # set velocity in front of wedge q[0] = q[0] + outside_wedge_f * v_downgoing # set velocity below wedge q[0] = q[0] + below_wedge_f * vx_downgoing q[1] = q[1] + below_wedge_f * vy_downgoing # set up temperature variable T = fipy.CellVariable(mesh=mesh, name='T') # boudnary conditions surface_nodes = depthf <= 1.0 bottom_nodes = ((xyf[0] <= 0) & (np.abs(depthf - Ly) < 1.0)) | ((xyf[0] > 0) & (np.abs(xyf[1] - (beta*xyf[0] - Ly)) < 1.0)) surface_temp_variable = sea_lvl_temp + xyf[1] * lapse_rate #T.constrain(surface_temp_variable, mesh.physicalFaces['surface']) T.constrain(surface_temp_variable, surface_nodes) #T.constrain(lab_temp, mesh.physicalFaces['bottom']) T.constrain(lab_temp, bottom_nodes) # constrain flux at lateral boundaries. for some reason zero-flux not automatically enforced by fipy in this case. T.faceGrad.constrain([0.0], mesh.facesRight) T.faceGrad.constrain([0.0], mesh.facesLeft) # coefficients k = K / (rho * c) HP_adj = HP / (rho * c) # set up equation diffTerm = fipy.DiffusionTerm(coeff=k) sourceTerm = fipy.CellVariable(mesh=mesh, value=HP_adj) #sourceTerm_v2 = fipy.ImplicitSourceTerm(HP_adj) #convTerm = fipy.ExponentialConvectionTerm(coeff=q) convTerm = fipy.PowerLawConvectionTerm(coeff=q) #convTerm = fipy.UpwindConvectionTerm(coeff=q) eq = (diffTerm == convTerm - sourceTerm) # solve eequation solver = fipy.solvers.LinearGMRESSolver(tolerance=1e-20, iterations=10000) eq.solve(var=T, solver=solver) # convert result to arrays T_array = T(mesh.cellCenters.globalValue) x, y = mesh.cellCenters.globalValue #q_array = q(mesh.cellCenters.globalValue) return x, y, T_array, q, mesh
def QAx(self): return fp.FaceVariable(mesh=self.mesh, value=-self.thermalConductivity * self.AcsMult * self.mesh.scaledFaceAreas * self.TCore.faceGrad)
def solve(self, heatExch): # Create variables for temperature and thermal conductivity self.T = FP.CellVariable(name='T', mesh=self.mesh) self.thermCond = FP.FaceVariable(name='lambda', mesh=self.mesh) # Create the coefficient for the source terms emulating boundary conditions self.cPrimCoeff = (self.primChannels * self.mesh.faceNormals).divergence self.cSecCoeff = (self.secChannels * self.mesh.faceNormals).divergence self.cExtCoeff = (self.extChannel * self.mesh.faceNormals).divergence # Initial guess self.T.setValue(heatExch.externalFlowIn.T) self.primChannelCalc.sections[0].fState.update_Tp( heatExch.primaryFlowIn.T, heatExch.primaryFlowIn.p) self.secChannelCalc.sections[0].fState.update_Tp( heatExch.secondaryFlowIn.T, heatExch.secondaryFlowIn.p) self.extChannelCalc.sections[0].fState.update_Tp( heatExch.externalFlowIn.T, heatExch.externalFlowIn.p) # Solve for a single cross section for i in range(self.numSectionSteps): heatExch.updateProgress((i + 1.0) / self.numSectionSteps, 1.) primSection = self.primChannelCalc.sections[i] secSection = self.secChannelCalc.sections[i] extSection = self.extChannelCalc.sections[i] # Compute convection coefficients primSection.computeConvectionCoefficient() secSection.computeConvectionCoefficient() extSection.computeConvectionCoefficient() # Run FV solver TPrim = primSection.fState.T TSec = secSection.fState.T TExt = extSection.fState.T self.solveSectionThermal(TPrim=TPrim, hConvPrim=primSection.hConv, TSec=TSec, hConvSec=secSection.hConv, TExt=TExt, hConvExt=extSection.hConv) # self.checkResults( # TPrim = TPrim, hConvPrim = primSection.hConv, # TSec = TSec, hConvSec = secSection.hConv, # TExt = TExt, hConvExt = extSection.hConv) # Compute average channel wall temperatures TFaces = self.T.arithmeticFaceValue() TPrimWall = self.getFaceAverage(TFaces, self.primChannels) TSecWall = self.getFaceAverage(TFaces, self.secChannels) TExtWall = self.getFaceAverage(TFaces, self.extChannel) primSection.TWall = TPrimWall secSection.TWall = TSecWall extSection.TWall = TExtWall # Compute heat fluxes and new temperatures if (i < self.numSectionSteps - 1): primSection.computeExitState( self.primChannelCalc.sections[i + 1].fState) secSection.computeExitState( self.secChannelCalc.sections[i + 1].fState) extSection.computeExitState( self.extChannelCalc.sections[i + 1].fState) else: primSection.computeExitState(self.primChannelStateOut) secSection.computeExitState(self.secChannelStateOut) extSection.computeExitState(self.extChannelStateOut) # Make a section result plot if (i == 0 or (i - self.lastPlotPos) >= float(self.numSectionSteps) / self.numPlots or i == self.numSectionSteps - 1): if (self.currPlot <= self.numPlots): ax = heatExch.__getattr__('sectionPlot%d' % self.currPlot) ax.set_aspect('equal') ax.set_title('Section at x=%g' % primSection.xStart) if (heatExch.sectionResultsSettings.setTRange): self.plotSolution( heatExch, ax, self.T, zmin=heatExch.sectionResultsSettings.Tmin, zmax=heatExch.sectionResultsSettings.Tmax) else: self.plotSolution(heatExch, ax, self.T) self.currPlot += 1 self.lastPlotPos = i
def hydrology(mode, sensor_positions, nx, ny, dx, dy, days, ele, phi_initial, catchment_mask, wt_canal_arr, boundary_arr, peat_type_mask, peat_bottom_arr, transmissivity, t0, t1, t2, t_sapric_coef, storage, s0, s1, s2, s_sapric_coef, diri_bc=0.0, plotOpt=False, remove_ponding_water=True, P=0.0, ET=0.0, dt=1.0): """ INPUT: - ele: (nx,ny) sized NumPy array. Elevation in m above c.r.p. - Hinitial: (nx,ny) sized NumPy array. Initial water table in m above c.r.p. - catchment mask: (nx,ny) sized NumPy array. Boolean array = True where the node is inside the computation area. False if outside. - wt_can_arr: (nx,ny) sized NumPy array. Zero everywhere except in nodes that contain canals, where = wl of the canal. - value_for_masked: DEPRECATED. IT IS NOW THE SAME AS diri_bc. - diri_bc: None or float. If None, Dirichlet BC will not be implemented. If float, this number will be the BC. - neumann_bc: None or float. If None, Neumann BC will not be implemented. If float, this is the value of grad phi. - P: Float. Constant precipitation. mm/day. - ET: Float. Constant evapotranspiration. mm/day. """ # dneg = [] # track_WT_drained_area = (239,166) # track_WT_notdrained_area = (522,190) ele[~catchment_mask] = 0. ele = ele.flatten() phi_initial = (phi_initial + 0.0 * np.zeros((ny, nx))) * catchment_mask # phi_initial = phi_initial * catchment_mask phi_initial = phi_initial.flatten() if len(ele) != nx * ny or len(phi_initial) != nx * ny: raise ValueError("ele or Hinitial are not of dim nx*ny") mesh = fp.Grid2D(dx=dx, dy=dy, nx=nx, ny=ny) phi = fp.CellVariable( name='computed H', mesh=mesh, value=phi_initial, hasOld=True) #response variable H in meters above reference level if diri_bc != None: phi.constrain(diri_bc, mesh.exteriorFaces) else: raise ValueError("Dirichlet boundary conditions must have a value") #*******omit areas outside the catchment. c is input cmask = fp.CellVariable(mesh=mesh, value=np.ravel(catchment_mask)) cmask_not = fp.CellVariable(mesh=mesh, value=np.array(~cmask.value, dtype=int)) # *** drain mask or canal mask dr = np.array(wt_canal_arr, dtype=bool) dr[np.array(wt_canal_arr, dtype=bool) * np.array( boundary_arr, dtype=bool )] = False # Pixels cannot be canals and boundaries at the same time. Everytime a conflict appears, boundaries win. This overwrites any canal water level info if the canal is in the boundary. drmask = fp.CellVariable(mesh=mesh, value=np.ravel(dr)) drmask_not = fp.CellVariable( mesh=mesh, value=np.array(~drmask.value, dtype=int) ) # Complementary of the drains mask, but with ints {0,1} # mask away unnecesary stuff # phi.setValue(np.ravel(H)*cmask.value) # ele = ele * cmask.value source = fp.CellVariable(mesh=mesh, value=0.) # cell variable for source/sink # CC=fp.CellVariable(mesh=mesh, value=C(phi.value-ele)) # differential water capacity def T_value(phi, ele, cmask, peat_bottom_arr): # Some inputs are in fipy CellVariable type gwt = (phi.value * cmask.value - ele).reshape(ny, nx) # T(h) - T(bottom) T = transmissivity(gwt, t0, t1, t2, t_sapric_coef) - transmissivity( peat_bottom_arr, t0, t1, t2, t_sapric_coef) # d <0 means tra_to_cut is greater than the other transmissivity, which in turn means that # phi is below the impermeable bottom. We allow phi to have those values, but # the transmissivity is in those points is equal to zero (as if phi was exactly at the impermeable bottom). T[T < 0] = 1e-3 # Small non-zero value not to wreck the computation Tcell = fp.CellVariable(mesh=mesh, value=T.flatten( )) # diffusion coefficient, transmissivity. As a cell variable. Tface = fp.FaceVariable(mesh=mesh, value=Tcell.arithmeticFaceValue.value ) # THe correct Face variable. return Tface.value def S_value(phi, ele, cmask, peat_bottom_arr): # Some inputs are in fipy CellVariable type gwt = (phi.value * cmask.value - ele).reshape(ny, nx) S = storage(gwt, s0, s1, s2, s_sapric_coef) - storage( peat_bottom_arr, s0, s1, s2, s_sapric_coef) S[S < 0] = 1e-3 # Same reasons as for D Scell = fp.CellVariable(mesh=mesh, value=S.flatten( )) # diffusion coefficient, transmissivity. As a cell variable. return Scell.value T = fp.FaceVariable(mesh=mesh, value=T_value( phi, ele, cmask, peat_bottom_arr)) # THe correct Face variable. S = fp.CellVariable(mesh=mesh, value=S_value( phi, ele, cmask, peat_bottom_arr)) # differential water capacity largeValue = 1e20 # value needed in implicit source term to apply internal boundaries if plotOpt: big_4_raster_plot( title='Before the computation', # raster1=((hydro_utils.peat_map_h_to_tra(soil_type_mask=peat_type_mask, gwt=(phi.value - ele), h_to_tra_and_C_dict=httd) - tra_to_cut)*cmask.value *drmask_not.value ).reshape(ny,nx), raster2=(ele.reshape(ny, nx) - wt_canal_arr) * dr * catchment_mask, raster3=ele.reshape(ny, nx), raster4=(ele - phi.value).reshape(ny, nx)) # for later cross-section plots y_value = 270 """ ###### PDE ###### """ if mode == 'steadystate': temp = 0. elif mode == 'transient': temp = fp.TransientTerm(coeff=S) if diri_bc != None: # diri_boundary = fp.CellVariable(mesh=mesh, value= np.ravel(diri_boundary_value(boundary_mask, ele2d, diri_bc)) eq = temp == ( fp.DiffusionTerm(coeff=T) + source * cmask * drmask_not - fp.ImplicitSourceTerm(cmask_not * largeValue) + cmask_not * largeValue * np.ravel(boundary_arr) - fp.ImplicitSourceTerm(drmask * largeValue) + drmask * largeValue * (np.ravel(wt_canal_arr)) # - fp.ImplicitSourceTerm(bmask_not*largeValue) + bmask_not*largeValue*(boundary_arr) ) #******************************************************** max_sweeps = 10 # inner loop. avg_wt = [] # wt_track_drained = [] # wt_track_notdrained = [] cumulative_Vdp = 0. #********Finite volume computation****************** for d in range(days): if type(P) == type(ele): # assume it is a numpy array source.setValue( (P[d] - ET[d]) * .001 * np.ones(ny * nx) ) # source/sink, in mm/day. The factor of 10^-3 takes into account that there are 100 x 100 m^2 in one pixel # print("(d,P) = ", (d, (P[d]-ET[d])* 10.)) else: source.setValue((P - ET) * 10. * np.ones(ny * nx)) # print("(d,P) = ", (d, (P-ET)* 10.)) if plotOpt and d != 0: # print "one more cross-section plot" plot_line_of_peat(phi.value.reshape(ny, nx), y_value=y_value, title="cross-section", color='cornflowerblue', nx=nx, ny=ny, label=d) res = 0.0 phi.updateOld() T.setValue(T_value(phi, ele, cmask, peat_bottom_arr)) S.setValue(S_value(phi, ele, cmask, peat_bottom_arr)) for r in range(max_sweeps): resOld = res res = eq.sweep(var=phi, dt=dt) # solve linearization of PDE #print "sum of Ds: ", np.sum(D.value)/1e8 #print "average wt: ", np.average(phi.value-ele) #print 'residue diference: ', res - resOld if abs(res - resOld) < 1e-7: break # it has reached to the solution of the linear system if remove_ponding_water: s = np.where( phi.value > ele, ele, phi.value ) # remove the surface water. This also removes phi in those masked values (for the plot only) phi.setValue(s) # set new values for water table if (T.value < 0.).any(): print("Some value in D is negative!") # For some plots avg_wt.append(np.average(phi.value - ele)) # wt_track_drained.append((phi.value - ele).reshape(ny,nx)[track_WT_drained_area]) # wt_track_notdrained.append((phi.value - ele).reshape(ny,nx)[track_WT_notdrained_area]) """ Volume of dry peat calc.""" not_peat = np.ones(shape=peat_type_mask.shape) # Not all soil is peat! not_peat[peat_type_mask == 4] = 0 # NotPeat not_peat[peat_type_mask == 5] = 0 # OpenWater peat_vol_weights = utilities.PeatV_weight_calc( np.array(~dr * catchment_mask * not_peat, dtype=int)) dry_peat_volume = utilities.PeatVolume(peat_vol_weights, (ele - phi.value).reshape( ny, nx)) cumulative_Vdp = cumulative_Vdp + dry_peat_volume if plotOpt: big_4_raster_plot( title='After the computation', # raster1=((hydro_utils.peat_map_h_to_tra(soil_type_mask=peat_type_mask, gwt=(phi.value - ele), h_to_tra_and_C_dict=httd) - tra_to_cut)*cmask.value *drmask_not.value ).reshape(ny,nx), raster2=(ele.reshape(ny, nx) - wt_canal_arr) * dr * catchment_mask, raster3=ele.reshape(ny, nx), raster4=(ele - phi.value).reshape(ny, nx)) # fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(12,9), dpi=80) # x = np.arange(-21,1,0.1) # axes[0,0].plot(httd[1]['hToTra'](x),x) # axes[0,0].set(title='hToTra', ylabel='depth') # axes[0,1].plot(httd[1]['C'](x),x) # axes[0,1].set(title='C') # axes[1,0].plot() # axes[1,0].set(title="Nothing") # axes[1,1].plot(avg_wt) # axes[1,1].set(title="avg_wt_over_time") # # plot surface in cross-section # ele_with_can = copy.copy(ele).reshape(ny,nx) # ele_with_can = ele_with_can * catchment_mask # ele_with_can[wt_canal_arr > 0] = wt_canal_arr[wt_canal_arr > 0] # plot_line_of_peat(ele_with_can, y_value=y_value, title="cross-section", nx=nx, ny=ny, label="surface", color='peru', linewidth=2.0) # plt.show() # change_in_canals = (ele-phi.value).reshape(ny,nx)*(drmask.value.reshape(ny,nx)) - ((ele-H)*drmask.value).reshape(ny,nx) # resulting_phi = phi.value.reshape(ny,nx) wtd = (phi.value - ele).reshape(ny, nx) wtd_sensors = [wtd[i] for i in sensor_positions] return np.array(wtd_sensors)
def create_coefficient(self): """A spatially varying diffusion coefficient""" arr = self.section.material_property("diffusivity").into("m**2/s") arr = N.append(arr, arr[-1]) assert len(arr) == len(self.mesh.faceCenters[0]) self.diffusion_coefficient = F.FaceVariable(mesh=self.mesh, value=arr)
def QAx(self): return fp.FaceVariable(mesh=self.mesh, value=-self.thermalConductivity * self.areaFaces * self.T.faceGrad)