[docs]classVariables:""" a container class for easy access to the different swe variables by an integer key """def__init__(self,myd):self.nvar=len(myd.names)# conserved variables -- we set these when we initialize for# they match the CellCenterData2d objectself.ih=myd.names.index("height")self.ixmom=myd.names.index("x-momentum")self.iymom=myd.names.index("y-momentum")# if there are any additional variables, we treat them as# passively advected scalarsself.naux=self.nvar-3ifself.naux>0:self.ihx=3else:self.ihx=-1# primitive variablesself.nq=3+self.nauxself.ih=0self.iu=1self.iv=2ifself.naux>0:self.ix=3# advected scalarelse:self.ix=-1
[docs]defcons_to_prim(U,ivars,myg):""" Convert an input vector of conserved variables :math:`U = (h, hu, hv, {hX})` to primitive variables :math:`q = (h, u, v, {X})`. """q=myg.scratch_array(nvar=ivars.nq)q[:,:,ivars.ih]=U[:,:,ivars.ih]q[:,:,ivars.iu]=U[:,:,ivars.ixmom]/U[:,:,ivars.ih]q[:,:,ivars.iv]=U[:,:,ivars.iymom]/U[:,:,ivars.ih]ifivars.naux>0:fornq,nuinzip(range(ivars.ix,ivars.ix+ivars.naux),range(ivars.ihx,ivars.ihx+ivars.naux)):q[:,:,nq]=U[:,:,nu]/q[:,:,ivars.ih]returnq
[docs]defprim_to_cons(q,ivars,myg):""" Convert an input vector of primitive variables :math:`q = (h, u, v, {X})` to conserved variables :math:`U = (h, hu, hv, {hX})` """U=myg.scratch_array(nvar=ivars.nvar)U[:,:,ivars.ih]=q[:,:,ivars.ih]U[:,:,ivars.ixmom]=q[:,:,ivars.iu]*U[:,:,ivars.ih]U[:,:,ivars.iymom]=q[:,:,ivars.iv]*U[:,:,ivars.ih]ifivars.naux>0:fornq,nuinzip(range(ivars.ix,ivars.ix+ivars.naux),range(ivars.ihx,ivars.ihx+ivars.naux)):U[:,:,nu]=q[:,:,nq]*q[:,:,ivars.ih]returnU
[docs]classSimulation(NullSimulation):"""The main simulation class for the corner transport upwind swe hydrodynamics solver """
[docs]definitialize(self,*,extra_vars=None,ng=4):""" Initialize the grid and variables for swe flow and set the initial conditions for the chosen problem. """my_grid=grid_setup(self.rp,ng=ng)my_data=self.data_class(my_grid)bc,bc_xodd,bc_yodd=bc_setup(self.rp)# are we dealing with solid boundaries? we'll use these for# the Riemann solverself.solid=bnd.bc_is_solid(bc)# density and energymy_data.register_var("height",bc)my_data.register_var("x-momentum",bc_xodd)my_data.register_var("y-momentum",bc_yodd)my_data.register_var("fuel",bc)# any extras?ifextra_varsisnotNone:forvinextra_vars:my_data.register_var(v,bc)# store the gravitational acceration g as an auxiliary quantity# so we can have a# self-contained object stored in output files to make plots.# store grav because we'll need that in some BCsmy_data.set_aux("g",self.rp.get_param("swe.grav"))my_data.create()self.cc_data=my_dataifself.rp.get_param("particles.do_particles")==1:n_particles=self.rp.get_param("particles.n_particles")particle_generator=self.rp.get_param("particles.particle_generator")self.particles=particles.Particles(self.cc_data,bc,n_particles,particle_generator)# some auxiliary data that we'll need to fill GC in, but isn't# really part of the main solutionaux_data=self.data_class(my_grid)aux_data.register_var("ymom_src",bc_yodd)aux_data.create()self.aux_data=aux_dataself.ivars=Variables(my_data)# derived variablesself.cc_data.add_derived(derives.derive_primitives)# initial conditions for the problemself.problem_func(self.cc_data,self.rp)ifself.verbose>0:print(my_data)
[docs]defmethod_compute_timestep(self):""" The timestep function computes the advective timestep (CFL) constraint. The CFL constraint says that information cannot propagate further than one zone per timestep. We use the driver.cfl parameter to control what fraction of the CFL step we actually take. """cfl=self.rp.get_param("driver.cfl")# get the variables we needu,v,cs=self.cc_data.get_var(["velocity","soundspeed"])# the timestep is min(dx/(|u| + cs), dy/(|v| + cs))xtmp=self.cc_data.grid.dx/(abs(u)+cs)ytmp=self.cc_data.grid.dy/(abs(v)+cs)self.dt=cfl*float(min(xtmp.min(),ytmp.min()))
[docs]defevolve(self):""" Evolve the equations of swe hydrodynamics through a timestep dt. """tm_evolve=self.tc.timer("evolve")tm_evolve.begin()myg=self.cc_data.gridFlux_x,Flux_y=flx.unsplit_fluxes(self.cc_data,self.rp,self.ivars,self.solid,self.tc,self.dt)# conservative updatedtdx=self.dt/myg.dxdtdy=self.dt/myg.dyforninrange(self.ivars.nvar):var=self.cc_data.get_var_by_index(n)var.v()[:,:]+= \
dtdx*(Flux_x.v(n=n)-Flux_x.ip(1,n=n))+ \
dtdy*(Flux_y.v(n=n)-Flux_y.jp(1,n=n))ifself.particlesisnotNone:self.particles.update_particles(self.dt)# increment the timeself.cc_data.t+=self.dtself.n+=1tm_evolve.end()
[docs]defdovis(self):""" Do runtime visualization. """plt.clf()plt.rc("font",size=10)# we do this even though ivars is in self, so this works when# we are plotting from a fileivars=Variables(self.cc_data)q=cons_to_prim(self.cc_data.data,ivars,self.cc_data.grid)h=q[:,:,ivars.ih]u=q[:,:,ivars.iu]v=q[:,:,ivars.iv]fuel=q[:,:,ivars.ix]magvel=np.sqrt(u**2+v**2)myg=self.cc_data.gridvort=myg.scratch_array()dv=0.5*(v.ip(1)-v.ip(-1))/myg.dxdu=0.5*(u.jp(1)-u.jp(-1))/myg.dyvort.v()[:,:]=dv-dufields=[h,magvel,fuel,vort]field_names=[r"$h$",r"$|U|$",r"$X$",r"$\nabla\times U$"]_,axes,cbar_title=plot_tools.setup_axes(myg,len(fields))forn,axinenumerate(axes):v=fields[n]img=ax.imshow(np.transpose(v.v()),interpolation="nearest",origin="lower",extent=[myg.xmin,myg.xmax,myg.ymin,myg.ymax],cmap=self.cm)ax.set_xlabel("x")ax.set_ylabel("y")# needed for PDF renderingcb=axes.cbar_axes[n].colorbar(img)cb.solids.set_rasterized(True)cb.solids.set_edgecolor("face")ifcbar_title:cb.ax.set_title(field_names[n])else:ax.set_title(field_names[n])ifself.particlesisnotNone:ax=axes[0]particle_positions=self.particles.get_positions()# dye particlescolors=self.particles.get_init_positions()[:,0]# plot particlesax.scatter(particle_positions[:,0],particle_positions[:,1],s=5,c=colors,alpha=0.8,cmap="Greys")ax.set_xlim([myg.xmin,myg.xmax])ax.set_ylim([myg.ymin,myg.ymax])plt.figtext(0.05,0.0125,f"t = {self.cc_data.t:10.5g}")plt.pause(0.001)plt.draw()