def test_sed_mpi(): model=base_model() g=unstructured_grid.UnstructuredGrid(max_sides=4) g.add_rectilinear([0,0],[1000,100],101,11) g.add_cell_field('depth',-6*np.ones(g.Ncells())) model.set_grid(g) model.num_procs=4 model.config['dt']=3. # slow down the settling velocities to get some advection mixed in model.config['Ws01']= 0.0001 # constant settling velocity for fraction No.1 (m/s) model.config['Ws02']= 0.0005 # constant settling velocity for fraction No.2 model.config['Ws03']=-0.0005 # constant settling velocity for fraction No.3 geom=np.array([ [1000,0], [1000,100]]) Q_bc=sun_driver.FlowBC(name="Q_bc", geom=geom, Q=100.0) model.add_bcs( [Q_bc, sun_driver.ScalarBC(parent=Q_bc,scalar="S",value=2), sun_driver.ScalarBC(parent=Q_bc,scalar="T",value=0)] ) point_bc=sun_driver.SourceSinkBC(name="bedPoint", geom=np.array([500,20]), Q=10) model.add_bcs( [point_bc, sun_driver.ScalarBC(parent=point_bc,scalar="S",value=3), sun_driver.ScalarBC(parent=point_bc,scalar="T",value=3)] ) model.write() # leave some dry layers at the surface model.ic_ds.eta.values[:]=model.bcs[0].z model.ic_ds.salt.values[:]=1.0 model.ic_ds.temp.values[:]=1.0 model.write_ic_ds() for sed_idx in range(3): name="sed%d"%(sed_idx+1) model.bc_ds['boundary_'+name]=model.bc_ds['boundary_S'].copy() model.bc_ds[name]=model.bc_ds['S'].copy() model.bc_ds['point_'+name]=model.bc_ds['point_S'].copy() model.bc_ds['boundary_'+name].values[:]=sed_idx*1.0 model.bc_ds[name].values[:]=0.0 model.bc_ds['point_'+name].values[:]=sed_idx*2.0 model.write_bc_ds() model.partition() model.sun_verbose_flag='-v' model.run_simulation() return model
def test_sed_bc(): """ basic BC """ model=base_model() # slow down the settling velocities to get some advection mixed in model.config['Ws01']= 0.0001 # constant settling velocity for fraction No.1 (m/s) model.config['Ws02']= 0.0001 # constant settling velocity for fraction No.2 model.config['Ws03']=-0.0001 # constant settling velocity for fraction No.3 geom=np.array([ [1000,0], [1000,100]]) Q_bc=sun_driver.FlowBC(name="Q_bc", geom=geom, Q=100.0) model.add_bcs( [Q_bc, sun_driver.ScalarBC(parent=Q_bc,scalar="S",value=2), sun_driver.ScalarBC(parent=Q_bc,scalar="T",value=0)] ) point_bc=sun_driver.SourceSinkBC(name="bedPoint", geom=np.array([500,20]), Q=10) model.add_bcs( [point_bc, sun_driver.ScalarBC(parent=point_bc,scalar="S",value=3), sun_driver.ScalarBC(parent=point_bc,scalar="T",value=3)] ) model.write() # leave some dry layers at the surface model.ic_ds.eta.values[:]=model.bcs[0].z model.ic_ds.salt.values[:]=1.0 model.ic_ds.temp.values[:]=1.0 model.write_ic_ds() for sed_idx in range(3): name="sed%d"%(sed_idx+1) model.bc_ds['boundary_'+name]=model.bc_ds['boundary_S'].copy() model.bc_ds[name]=model.bc_ds['S'].copy() model.bc_ds['point_'+name]=model.bc_ds['point_S'].copy() model.bc_ds['boundary_'+name].values[:]=sed_idx*1.0 model.bc_ds[name].values[:]=0.0 model.bc_ds['point_'+name].values[:]=sed_idx*2.0 model.write_bc_ds() model.partition() model.sun_verbose_flag='-v' model.run_simulation() return model
def add_potw_bcs(model, cache_dir, temperature=20.0): # WWTP discharging into sloughs potw_dir = "../sfbay_potw" potw_ds = xr.open_dataset( os.path.join(potw_dir, "outputs", "sfbay_delta_potw.nc")) # the gazetteer uses the same names for potws as the source data # omits some of the smaller sources, and this does not include any # benthic discharges for potw_name in [ 'sunnyvale', 'san_jose', 'palo_alto', 'lg', 'sonoma_valley', 'petaluma', 'cccsd', 'fs', 'ddsd', 'ebda', 'ebmud', 'sf_southeast' ]: # This has variously worked and not worked with strings vs bytes. # Brute force and try both. try: Q_da = potw_ds.flow.sel(site=potw_name) except KeyError: Q_da = potw_ds.flow.sel(site=potw_name.encode()) # Have to seek back in time to find a year that has data for the # whole run offset = np.timedelta64(0, 'D') while model.run_stop > Q_da.time.values[-1] + offset: offset += np.timedelta64(365, 'D') if offset: log.info("Offset for POTW %s is %s" % (potw_name, offset)) # use the geometry to decide whether this is a flow BC or a point source hits = model.match_gazetteer(name=potw_name) if hits[0]['geom'].type == 'LineString': log.info("%s: flow bc" % potw_name) Q_bc = drv.FlowBC(name=potw_name, flow=Q_da, filters=[hm.Lag(-offset)], dredge_depth=model.dredge_depth) else: log.info("%s: source bc" % potw_name) Q_bc = drv.SourceSinkBC(name=potw_name, flow=Q_da, filters=[hm.Lag(-offset)], dredge_depth=model.dredge_depth) salt_bc = drv.ScalarBC(parent=Q_bc, scalar='salinity', value=0.0) temp_bc = drv.ScalarBC(parent=Q_bc, scalar='temperature', value=temperature) model.add_bcs([Q_bc, salt_bc, temp_bc])
def base_model_setup(L=100, dx=10): g = unstructured_grid.UnstructuredGrid(max_sides=4) g.add_rectilinear([0, 0], [L, 10], 1 + int(L / dx), 2) g.add_cell_field('depth', -6 * np.ones(g.Ncells())) model = sun_driver.SuntansModel() model.num_procs = 1 model.load_template('point_source_test.dat') model.set_grid(g) model.run_start = np.datetime64("2018-01-01 00:00") model.run_stop = np.datetime64("2018-01-02 00:00") # 50m2, 5.0 m3/s, 0.1m/s source = sun_driver.FlowBC(name='inflow', geom=np.array([[0, 0], [0, 10]]), Q=10.0) outlet = sun_driver.StageBC(name='outflow', geom=np.array([[100, 0], [100, 10]]), z=-1) model.add_bcs([source, outlet]) model.set_run_dir('rundata_2d_average', mode='pristine') model.projection = 'EPSG:26910' model.config['dt'] = 2.5 model.config['ntout'] = 100 model.config['ntaverage'] = 100 model.config['calcaverage'] = 1 model.config['averageNetcdfFile'] = "average.nc" model.config['thetaramptime'] = 100 model.config['Cmax'] = 2 model.config['Nkmax'] = 1 model.config['stairstep'] = 0 model.config['mergeArrays'] = 0 return model
nx=int(L/dx_lon),ny=int(W/dy_lat)) g.add_cell_field('depth',bathy(g.cells_center())) model.set_grid(g) model.config['maxFaces']=4 model.z_offset=0 from shapely import geometry feats=np.zeros( 2, [ ('name','O'), ('geom','O') ] ) feats[0]['name']='left' # HALF STEP: feats[0]['geom']=geometry.LineString( [ [0,W/2], [0,W] ] ) feats[1]['name']='right' feats[1]['geom']=geometry.LineString( [ [L,0], [L,W] ] ) model.add_gazetteer(feats) Q_left=drv.FlowBC(name='left',Q=200.0) h_right=drv.StageBC(name='right',z=0.0) model.add_bcs([Q_left,h_right]) model.write() model.partition() #model.run_simulation()
# freesurface BC is 2.2 (plus offset) # there is that deep hole down to -10m. # what are the options for specifying layers? # rstretch? # # Annoying, but suntans doesn't like signed elevations # this offset will be applied to grid depths and freesurface boundary conditions. # this was -10, but that leaves a lot of dead space on top. model.z_offset = -4 model.config['maxFaces'] = 4 model.add_gazetteer("gis/forcing-v00.shp") Q_upstream = drv.FlowBC(name='SJ_upstream', Q=250.0) Qdown = -100 # target outflow # ramp up to the full outflow over 1h h = np.timedelta64(1, 'h') Qdown = xr.DataArray( data=[0, 0, Qdown, Qdown, Qdown], name='Q', dims=['time'], coords=dict(time=[ model.run_start - 24 * h, model.run_start, model.run_start + ramp_hours * h, model.run_stop, model.run_stop + 24 * h ])) Q_downstream = drv.FlowBC(name='SJ_downstream', Q=Qdown) h_old_river = drv.StageBC(name='Old_River', z=2.2 - 0.65) model.add_bcs([Q_upstream, Q_downstream, h_old_river])
edges = g.shortest_path(n1, n2, return_type='edges') # sloping weir, initially totally dry, will overtop # during run # test both signs of the edge depth g.edges['edge_depth'][edges] = np.linspace(-2, 2, len(edges)) model = sun_driver.SuntansModel() model.use_edge_depths = True model.load_template('sun-template.dat') model.set_grid(g) model.num_procs = 4 inflow = sun_driver.FlowBC(name='inflow', geom=np.array([[0, 0], [0, 100]]), Q=50.0) model.add_bcs(inflow) model.set_run_dir('rundata', mode='pristine') model.run_start = np.datetime64("2018-01-01 00:00") model.run_stop = np.datetime64("2018-01-01 20:00") model.projection = 'EPSG:26910' model.sun_bin_dir = "/home/rusty/src/suntans/main" # for 2D, dt=30 is okay. # for 3D, start getting some CmaxW, so scale it back. # 10 or 15 would probably be okay. 30 is unstable. model.config['dt'] = 5 model.config['Cmax'] = 30 model.config['Nkmax'] = 10
def base_model(run_dir,run_start,run_stop, restart_from=None, grid_option=None, steady=False): if restart_from is None: model=SuntansModel() model.load_template(os.path.join(here,"sun-template.dat")) model.num_procs=num_procs else: old_model=SuntansModel.load(restart_from) model=old_model.create_restart() model.manual_z_offset=-4 model.z_offset=0.0 # moving away from the semi-automated datum shift to manual shift model.projection="EPSG:26910" model.set_run_dir(run_dir,mode='pristine') if model.restart: model.run_start=model.restart_model.restartable_time() else: model.run_start=run_start model.run_stop=run_stop model.config['grid_option']=grid_option or 'snubby' if restart_from is None: ramp_hours=1 # how quickly to increase the outflow else: ramp_hours=0 if not model.restart: # dt=0.25 # for lower friction run on refined shore grid # with new grid that's deformed around the barrier can probably # get away with 0.5 # dt=0.5 # for lower friction run on refined shore grid dt=0.25 # bit of safety for high res grid. # dt=0.1 # for shaved cell grid. still not stable. model.config['dt']=dt model.config['metmodel']=0 # 0: no wind, 4: wind only model.config['nonlinear']=1 if 0: model.config['Nkmax']=1 model.config['stairstep']=0 if 1: model.config['Nkmax']=50 model.config['stairstep']=1 # 3D, stairstep model.config['thetaM']=-1 # with 1, seems to be unstable model.config['z0B']=5e-4 # maybe with ADCP bathy need more friction # slow, doesn't make a huge difference, but does make w nicer. model.config['nonhydrostatic']=0 model.config['nu_H']=0.0 model.config['nu']=1e-5 model.config['turbmodel']=10 # 1: my25, 10: parabolic model.config['CdW']=0.0 # model.config['wetdry']=1 model.config['maxFaces']=4 model.config['mergeArrays']=1 # hopefully easier to use than split up. if model.config['grid_option']=='snubby': # 2019-07-11: refine near-shore swaths of grid, snubby-01 => snubby-04 # 2019-07-12: further refinements 04 => 06 #grid_src="../grid/snubby_junction/snubby-06.nc" #grid_src="../grid/snubby_junction/snubby-07-edit45.nc" #grid_src="../grid/snubby_junction/snubby-08-edit06.nc" #grid_src="../grid/snubby_junction/snubby-08-edit24.nc" #grid_src="../grid/snubby_junction/snubby-08-edit50.nc" # good grid_src="../grid/snubby_junction/snubby-08-edit60.nc" # higher res in hole #grid_src="../grid/snubby_junction/snubby-08-refine-edit03.nc" # double res of edit60 elif model.config['grid_option']=='large': grid_src="../grid/merge_v16/merge_v16_edit09.nc" else: raise Exception("Unknown grid option %s"%grid_option) # bathy_suffix='' # post_suffix='med0' # on-grid bathy postprocessesing: #bathy_suffix='smooth' # pre-smoothed just in sand wave areas bathy_suffix='smoothadcp' # pre-smoothed just in sand wave areas, and revisit ADCP data. #bathy_suffix='smoothadcp2' # same, but extend ADCP data upstream. post_suffix='' # bathy_suffix='adcp' # post_suffix='' grid_bathy=os.path.basename(grid_src).replace('.nc',f"-with_bathy{bathy_suffix}{post_suffix}.nc") if utils.is_stale(grid_bathy,[grid_src]): g_src=unstructured_grid.UnstructuredGrid.from_ugrid(grid_src) g_src.cells_center(refresh=True) bare_edges=g_src.orient_edges(on_bare_edge='return') if len(bare_edges): ec=g_src.edges_center() for j in bare_edges[:50]: print("Bare edge: j=%d xy=%g %g"%(j, ec[j,0], ec[j,1])) raise Exception('Bare edges in grid') add_bathy.add_bathy(g_src,suffix=bathy_suffix) add_bathy.postprocess(g_src,suffix=post_suffix) g_src.write_ugrid(grid_bathy,overwrite=True) g=unstructured_grid.UnstructuredGrid.from_ugrid(grid_bathy) g.cells['z_bed'] += model.manual_z_offset g.delete_edge_field('edge_z_bed') #g.edges['edge_z_bed'] += model.manual_z_offset model.set_grid(g) model.grid.modify_max_sides(4) dt=float(model.config['dt']) # with 0.01, was getting many CmaxW problems. model.config['dzmin_surface']=0.05 # may have to bump this up.. model.config['ntout']=int(1800./dt) model.config['ntaverage']=int(1800./dt) model.config['ntoutStore']=int(86400./dt) # isn't this just duplicating the setting from above? model.z_offset=0.0 # moving away from the semi-automated datum shift to manual shift if model.config['grid_option']=='snubby': model.add_gazetteer(os.path.join(here,"../grid/snubby_junction/forcing-snubby-01.shp")) elif model.config['grid_option']=='large': model.add_gazetteer(os.path.join(here,"../grid/merge_v16/forcing-merge_16-01.shp")) def fill(da): return utils.fill_tidal_data(da,fill_time=False) if steady: # Would like to ramp these up at the very beginning. # Mossdale, tested at 220.0 m3/s Q_upstream=drv.FlowBC(name='SJ_upstream',flow=220.0,dredge_depth=None) Q_downstream=drv.FlowBC(name='SJ_downstream',flow=-100,dredge_depth=None) # had been 1.75. but that's showing about 0.75m too much water. # could be too much friction, or too high BC. h_old_river=drv.StageBC(name='Old_River',water_level=1.75 + model.manual_z_offset) else: # 15 minute data. The flows in particular have enough overtide energy # that the lowpass interval shouldn't be too large. Spot checks of plots # show 1.0h is pretty good lp_hours=1.0 data_upstream=common.msd_flow(model.run_start,model.run_stop) Q_upstream=drv.FlowBC(name="SJ_upstream",flow=data_upstream.flow_m3s,dredge_depth=None, filters=[hm.Lowpass(cutoff_hours=1.0)]) data_downstream=common.sjd_flow(model.run_start,model.run_stop) # flip sign to get outflow. Q_downstream=drv.FlowBC(name="SJ_downstream",flow=-data_downstream.flow_m3s,dredge_depth=None, filters=[hm.Lowpass(cutoff_hours=1.0)]) or_stage=common.oh1_stage(model.run_start,model.run_stop) h_old_river=drv.StageBC(name='Old_River',water_level=or_stage.stage_m, filters=[hm.Transform(fn=lambda x: x+model.manual_z_offset), hm.Lowpass(cutoff_hours=1.0)]) model.add_bcs([Q_upstream,Q_downstream,h_old_river]) model.write() assert np.all(np.isfinite(model.bc_ds.boundary_Q.values)) if not model.restart: # bathy rms ranges from 0.015 to 1.5 # 0.5 appears a little better than 1.0 or 0.1 # cfg007 used 0.1, and the shape was notably not as good # as steady008. # cfg008 will return to 0.5... # BUT - now that I have more metrics in place, it looks like cfg007 # was actually better, in terms of MAE over top 2m, and smaller bias. # with that in mind, steady012 will try an even smaller factor, and infact # something more in line with Nikuradse. # best roughness using constant z0B was 5e-4. # so map 0.1 to that... if 1: print("Adding roughness") grid_roughness.add_roughness(model.grid) cell_z0B=(1./30)*model.grid.cells['bathy_rms'] e2c=model.grid.edge_to_cells() nc1=e2c[:,0] nc2=e2c[:,1] nc2[nc2<0]=nc1[nc2<0] edge_z0B=0.5*( cell_z0B[nc1] + cell_z0B[nc2] ) model.ic_ds['z0B']=('time','Ne'), edge_z0B[None,:] model.write_ic_ds() if int(model.config['metmodel'])>=4: # and how about some wind? times=np.array( [model.run_start - np.timedelta64(10,'D'), model.run_start, model.run_stop, model.run_stop + np.timedelta64(10,'D')] ) nt=len(times) met_ds=model.zero_met(times=times) pnts=model.grid.cells_center()[:1] for comp in ['Uwind','Vwind']: met_ds['x_'+comp]=("N"+comp,),pnts[:,0] met_ds['y_'+comp]=("N"+comp,),pnts[:,1] met_ds['z_'+comp]=("N"+comp,),10*np.ones_like(pnts[:,1]) met_ds['Uwind']=('nt',"NUwind"), 0.0 * np.ones((nt,len(pnts))) met_ds['Vwind']=('nt',"NVwind"),-5.0 * np.ones((nt,len(pnts))) model.met_ds=met_ds model.write_met_ds() model.partition() model.sun_verbose_flag='-v' return model
model.config['thetaM'] = -1 model.run_start = old_model.restartable_time() model.run_stop = model.run_start + np.timedelta64(900, 's') dt = float(model.config['dt']) model.config['ntout'] = int(900. / dt) ## # Annoying, but suntans doesn't like signed elevations # this offset will be applied to grid depths and freesurface boundary conditions. # this was -10, but that leaves a lot of dead space on top. model.z_offset = -4 model.add_gazetteer("gis/forcing-v00.shp") Q_upstream = drv.FlowBC(name='SJ_upstream', Q=210.0) Q_downstream = drv.FlowBC(name='SJ_downstream', Q=-100.0) h_old_river = drv.StageBC(name='Old_River', z=2.2) model.add_bcs([Q_upstream, Q_downstream, h_old_river]) model.write() model.partition() model.run_simulation() ## from stompy.spatial import wkb2shp from stompy import xr_transect tran_shp = "../../gis/model_transects.shp" tran_geoms = wkb2shp.shp2geom(tran_shp)
model.set_grid(g) model.grid.modify_max_sides(4) ## # Annoying, but suntans doesn't like signed elevations # this offset will be applied to grid depths and freesurface boundary conditions. # this was -10, but that leaves a lot of dead space on top. model.z_offset = -4 model.config['maxFaces'] = 4 model.add_gazetteer("../grid/snubby_junction/forcing-snubby-01.shp") Q_upstream = drv.FlowBC(name='SJ_upstream', Q=220.0, dredge_depth=None) Qdown = -100 # target outflow # ramp up to the full outflow over 1h h = np.timedelta64(1, 'h') Qdown = xr.DataArray( data=[0, 0, Qdown, Qdown, Qdown], name='Q', dims=['time'], coords=dict(time=[ model.run_start - 24 * h, model.run_start, model.run_start + ramp_hours * h, model.run_stop, model.run_stop + 24 * h ])) Q_downstream = drv.FlowBC(name='SJ_downstream', Q=Qdown, dredge_depth=None) # 2.5 crashed. # h_old_river=drv.StageBC(name='Old_River',z=2.2-0.65) h_old_river = drv.StageBC(name='Old_River', z=2.5)