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 test_point_source_1d(): """ Create a 1D water column, with a point source at the bed. Verifies that all cells with valid thickness (dzz>0.001m) have near unity salinity. """ g=unstructured_grid.UnstructuredGrid(max_sides=4) g.add_rectilinear([0,0],[10,10],2,2) g.add_cell_field('depth',-6*np.ones(g.Ncells())) model=sun_driver.SuntansModel() 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-01 10:00") source=sun_driver.SourceSinkBC(name='inflow',geom=np.array([5,5]), z=-10,Q=1.0) model.add_bcs(source) model.add_bcs( [sun_driver.ScalarBC(parent=source,scalar="S",value=1), sun_driver.ScalarBC(parent=source,scalar="T",value=1)] ) model.set_run_dir('rundata_point_1d', mode='pristine') model.projection='EPSG:26910' model.config['dt']=2.5 model.config['ntout']=1 model.config['Cmax']=30 model.config['Nkmax']=10 model.config['stairstep']=0 model.config['mergeArrays']=0 model.write() model.ic_ds.eta.values[:]=-5.999 model.ic_ds.salt.values[:]=1.0 model.ic_ds.temp.values[:]=1.0 model.write_ic_ds() model.partition() model.sun_verbose_flag='-v' model.run_simulation() ds=xr.open_dataset(model.map_outputs()[0]) dzz=ds.dzz.values dzz_thresh=0.001 salt=ds.salt.values salt_errors=salt[dzz>=dzz_thresh] - 1.0 assert np.max( np.abs(salt_errors) )<0.001
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 add_usgs_stream_bcs(model, cache_dir): # USGS gauged creeks for station, name in [ (11172175, "COYOTE"), (11169025, "SCLARAVCc"), # Alviso Sl / Guad river (11180700, "UALAMEDA"), # Alameda flood control (11458000, "NAPA") ]: Q_bc = hm.NwisFlowBC(name=name, station=station, cache_dir=cache_dir, dredge_depth=model.dredge_depth) salt_bc = drv.ScalarBC(name=name, scalar='salinity', value=0.0) temp_bc = drv.ScalarBC(name=name, scalar='temperature', value=20.0) model.add_bcs([Q_bc, salt_bc, temp_bc])
def base_model(): """ Channel """ g=unstructured_grid.UnstructuredGrid(max_sides=4) g.add_rectilinear([0,0],[1000,100],21,3) g.add_cell_field('depth',-6*np.ones(g.Ncells())) model=sun_driver.SuntansModel() 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-05 00:00") dt=np.timedelta64(600,'s') times=np.arange(model.run_start-dt,model.run_stop+2*dt,dt) secs=(times-times[0])/np.timedelta64(1,'s') eta0=-1 eta_bc=sun_driver.StageBC(name="eta_bc", geom=np.array([ [0,0], [0,100]]), z=eta0) model.add_bcs(eta_bc) model.add_bcs( [sun_driver.ScalarBC(parent=eta_bc,scalar="S",value=34), sun_driver.ScalarBC(parent=eta_bc,scalar="T",value=15)] ) model.set_run_dir('rundata_met_3d', mode='pristine') model.projection='EPSG:26910' model.num_procs=1 # test single first model.config['dt']=120 model.config['ntout']=5 model.config['Cmax']=30 model.config['Nkmax']=10 model.config['stairstep']=0 model.config['mergeArrays']=0 return model
def add_delta_bcs(model, cache_dir): # Delta inflow # SacRiver, SJRiver sac_bc = hm.NwisFlowBC(name='SacRiver', station=11455420, cache_dir=cache_dir, filters=[hm.Lowpass(cutoff_hours=3)], dredge_depth=model.dredge_depth) tmi_bc = hm.NwisFlowBC( name='SacRiver', station=11337080, cache_dir=cache_dir, filters=[hm.Lowpass(cutoff_hours=3), hm.Transform(fn=lambda x: -x)], mode='add') sj_bc = hm.NwisFlowBC(name='SJRiver', station=11337190, cache_dir=cache_dir, filters=[hm.Lowpass(cutoff_hours=3)], dredge_depth=model.dredge_depth) dutch_bc = hm.NwisFlowBC(name='SJRiver', station=11313433, cache_dir=cache_dir, filters=[hm.Lowpass(cutoff_hours=3)], mode='add') sac_salt_bc = drv.ScalarBC(name='SacRiver', scalar='salinity', value=0.0) sj_salt_bc = drv.ScalarBC(name='SJRiver', scalar='salinity', value=0.0) sac_temp_bc = drv.ScalarBC(name='SacRiver', scalar='temperature', value=20.0) sj_temp_bc = drv.ScalarBC(name='SJRiver', scalar='temperature', value=20.0) model.add_bcs( [sac_bc, sj_bc, sac_salt_bc, sj_salt_bc, sac_temp_bc, sj_temp_bc])
model.set_grid(g) model.run_start = np.datetime64("2018-01-01 00:00") model.run_stop = np.datetime64("2018-01-01 10:00") dt = np.timedelta64(600, 's') times = np.arange(model.run_start - dt, model.run_stop + 2 * dt, dt) secs = (times - times[0]) / np.timedelta64(1, 's') eta_values = -6 + 0.75 * np.cos(secs * np.pi / 7200) eta_da = xr.DataArray(eta_values, coords=[('time', times)]) eta_bc = sun_driver.StageBC(name="eta_bc", geom=np.array([[0, 0], [0, 500]]), z=eta_da) model.add_bcs(eta_bc) model.add_bcs([ sun_driver.ScalarBC(parent=eta_bc, scalar="S", value=1), sun_driver.ScalarBC(parent=eta_bc, scalar="T", value=1) ]) # point source that is typically dry hill_source = sun_driver.SourceSinkBC(name='inflow', geom=np.array([150, 150]), z=-10, Q=1.0) # on a saddle saddle_source = sun_driver.SourceSinkBC(name='inflow', geom=np.array([220, 225]), z=-10, Q=1.0)
def test_point_source_3d(): """ A square 3D grid with wetting and drying. the bathymetry is a wavy cos(x)*cos(y) function. two point sources and an oscillating freesurface. one point source at a saddle point, and the other on top of a bump which goes dry. This currently does pretty well, but it could be better -- see the test thresholds near the end which have been relaxed somewhat. """ g=unstructured_grid.UnstructuredGrid(max_sides=4) g.add_rectilinear([0,0],[500,500],50,50) # wavy bed half_wave=150 cc=g.cells_center() cell_depth=-6 + np.cos(cc[:,0]*np.pi/half_wave) * np.cos(cc[:,1]*np.pi/half_wave) g.add_cell_field('depth',cell_depth) model=sun_driver.SuntansModel() 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-01 10:00") dt=np.timedelta64(600,'s') times=np.arange(model.run_start-dt,model.run_stop+2*dt,dt) secs=(times-times[0])/np.timedelta64(1,'s') eta_values=-6 + 0.75*np.cos(secs*np.pi/7200) eta_da=xr.DataArray(eta_values,coords=[ ('time',times) ]) eta_bc=sun_driver.StageBC(name="eta_bc",geom=np.array([ [0,0], [0,500]]), z=eta_da) model.add_bcs(eta_bc) model.add_bcs( [sun_driver.ScalarBC(parent=eta_bc,scalar="S",value=1), sun_driver.ScalarBC(parent=eta_bc,scalar="T",value=1)] ) # point source that is typically dry hill_source=sun_driver.SourceSinkBC(name='inflow',geom=np.array([150,150]), z=-10,Q=1.0) # on a saddle saddle_source=sun_driver.SourceSinkBC(name='inflow',geom=np.array([220,225]), z=-10,Q=1.0) model.add_bcs(hill_source) model.add_bcs(saddle_source) model.add_bcs( [sun_driver.ScalarBC(parent=hill_source,scalar="S",value=1), sun_driver.ScalarBC(parent=hill_source,scalar="T",value=1)] ) model.add_bcs( [sun_driver.ScalarBC(parent=saddle_source,scalar="S",value=1), sun_driver.ScalarBC(parent=saddle_source,scalar="T",value=1)] ) model.set_run_dir('rundata_point_3d', mode='pristine') model.projection='EPSG:26910' model.num_procs=4 model.config['dt']=2.5 model.config['ntout']=5 model.config['Cmax']=30 model.config['Nkmax']=10 model.config['stairstep']=0 model.config['mergeArrays']=0 model.write() model.ic_ds.eta.values[:]=eta_da.values[1] model.ic_ds.salt.values[:]=1.0 model.ic_ds.temp.values[:]=1.0 model.write_ic_ds() model.partition() model.sun_verbose_flag='-v' model.run_simulation() for map_fn in model.map_outputs(): ds=xr.open_dataset(map_fn) dzz=ds.dzz.values # This is not as strict as it should be. # should be 0.001 or less. dzz_thresh=0.05 salt=ds.salt.values valid=np.isfinite(dzz) & (dzz>=dzz_thresh) salt_errors=salt[valid] - 1.0 # This is not as strict as it should be. # we should be able to get this down to 1e-4 or # better. assert np.max( np.abs(salt_errors) )<0.01 ds.close()
if utils.is_stale(dest_grid, [src_grid, "bathy.py"]): set_bathy(src_grid, dest_grid) g = unstructured_grid.UnstructuredGrid.from_ugrid(dest_grid) g.cells['z_bed'] += z_offset_manual g.edges['edge_z_bed'] += z_offset_manual model.set_grid(g) else: print("Grid comes from restart") model.dredge_depth = None # no need to dredge grid if a restart model.add_gazetteer("grid-lsb/linear_features.shp") ## ocean_salt_bc = drv.ScalarBC(name='ocean', scalar='salinity', value=25) ocean_temp_bc = drv.ScalarBC(name='ocean', scalar='temperature', value=10) msl_navd88 = 0.94 # m ocean_bc = drv.NOAAStageBC( name='ocean', station=9414575, # Coyote - will come in as MSL # station=9414750, # Alameda. cache_dir=cache_dir, filters=[dfm.Lowpass(cutoff_hours=1.5)]) ocean_offset_bc = drv.StageBC(name='ocean', mode='add', z=z_offset_manual + msl_navd88) model.add_bcs([ocean_bc, ocean_salt_bc, ocean_temp_bc, ocean_offset_bc])
# eta_da=xr.DataArray(eta_values,coords=[ ('time',times) ]) # eta_bc=sun_driver.StageBC(name="eta_bc",geom=np.array([ [0,0], # [0,500]]), # z=eta_da) # model.add_bcs(eta_bc) #model.add_bcs( [sun_driver.ScalarBC(parent=eta_bc,scalar="S",value=1), # sun_driver.ScalarBC(parent=eta_bc,scalar="T",value=1)] ) # on a saddle source = sun_driver.SourceSinkBC(name='inflow', geom=np.array([5, 5]), z=-10, Q=1.0) model.add_bcs(source) model.add_bcs([ sun_driver.ScalarBC(parent=source, scalar="S", value=1), sun_driver.ScalarBC(parent=source, scalar="T", value=1) ]) model.set_run_dir('rundata_1d', mode='pristine') model.projection = 'EPSG:26910' model.sun_bin_dir = "/home/rusty/src/suntans/main" model.config['dt'] = 2.5 model.config['ntout'] = 1 model.config['Cmax'] = 30 model.config['Nkmax'] = 10 model.config['stairstep'] = 0 model.config['mergeArrays'] = 0 model.write()
def base_model(): """ A channel, uniform bathy. """ g=unstructured_grid.UnstructuredGrid(max_sides=4) g.add_rectilinear([0,0],[1000,100],21,3) g.add_cell_field('depth',-6*np.ones(g.Ncells())) model=sun_driver.SuntansModel() 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-01 10:00") dt=np.timedelta64(600,'s') times=np.arange(model.run_start-dt,model.run_stop+2*dt,dt) secs=(times-times[0])/np.timedelta64(1,'s') eta0=-2 eta_bc=sun_driver.StageBC(name="eta_bc", geom=np.array([ [0,0], [0,100]]), z=eta0) model.add_bcs(eta_bc) model.add_bcs( [sun_driver.ScalarBC(parent=eta_bc,scalar="S",value=1), sun_driver.ScalarBC(parent=eta_bc,scalar="T",value=1)] ) model.set_run_dir('rundata_sedi_3d', mode='pristine') model.projection='EPSG:26910' model.num_procs=1 # test single first model.config['dt']=30 model.config['ntout']=5 model.config['Cmax']=30 model.config['Nkmax']=10 model.config['stairstep']=0 model.config['mergeArrays']=0 # Sediment: model.config['computeSediments']=1 # whether include sediment model model.config['Nlayer']=0 # Number of bed layers (MAX = 5) model.config['Nsize']=3 # Number of sediment fractions (Max = 3) model.config['TBMAX']=1 # whether to output tb for each cell model.config['SETsediment']=0 # When Nlayer>5 or Nsize>3, SETsediment=1 to use SetSediment model.config['WSconstant']=1 # if 1, ws for sediment = ws0 model.config['readSediment']=0 # if 1, read sediment concentration data as IC. only work with Nsize==1 model.config['bedInterval']=150 # the interval steps to update bed change model.config['bedComplex']=0 # whether to consider the possibility to flush away a whole layer model.config['ParabolKappa']=0 # whether to use parabolic tubulent diffusivity model.config['Ds90']=0.000008 # ds90 for calculation of erosion taub model.config['Ds1']=0.00000057 # sediment diameter for fraction No.1 (m) model.config['Ds2']=0.0002 # sediment diameter for fraction No.2 model.config['Ds3']=0.0002 # sediment diameter for fraction No.3 model.config['Ws01']= 0.0001 # constant settling velocity for fraction No.1 (m/s) model.config['Ws02']= 0.01 # constant settling velocity for fraction No.2 model.config['Ws03']=-0.01 # constant settling velocity for fraction No.3 model.config['Gsedi1']=2.65 # relative density for fraction No.1 model.config['Gsedi2']=2.65 # relative density for fraction No.2 model.config['Gsedi3']=2.65 # relative density for fraction No.3 model.config['Prt1']=1 # Prandtl Number for fraction No.1 model.config['Prt2']=1 # Prandtl Number for fraction No.2 model.config['Prt3']=1 # Prandtl Number for fraction No.3 model.config['Consolid1']=0.00 # Consolidation rate (g/m^2/s) for layer No.1 model.config['E01']=0.1 # Basic Erosion Rate Constant (g/m^2/s) for layer No.1 model.config['Taue1']=0.0 # Erosion Critical Shear Stress (N/m^2) for layer No.1 # Taud1 isn't actually used in current code. Deposition always occurs at the rate # of w_s. model.config['Taud1']=0.0 # Deposition Critical Shear Stress (N/m^2) for layer No.1 model.config['Drydensity1']=530000 # Dry density (g/m^3) for layer No.1 model.config['Thickness1']=0.0 # Thickness (m) for layer No.1 model.config['softhard1']=1 # 0 soft or hard for layer No.1 to decide how to calculate erosion model.config['Bedmudratio1']=0.8 # Bed mud ratio for layer No.1 model.config['Chind']=1000000 # Concentration (in volumetric fraction) criterion for hindered settling velocity model.config['Cfloc']=500000 # Concentration (in volumetric fraction) criterion for flocculated settling velcoity model.config['k']=0.0002 # Constant coefficient for settling velocity as a function of conc. model.config['Sediment1File']='sedi1.dat' model.config['Sediment2File']='sedi2.dat' model.config['Sediment3File']='sedi3.dat' model.config['LayerFile']='sedilayer.dat' model.config['tbFile']='Seditb.dat' model.config['tbmaxFile']='Seditbmax.dat' return model