Skip to content

Commit da12d6e

Browse files
author
Rusty Holleman
committed
Merge branch 'master' of https://github.com/rustychris/stompy
2 parents e8b8b3b + 68ce510 commit da12d6e

File tree

11 files changed

+643
-61
lines changed

11 files changed

+643
-61
lines changed

examples/openfoam.ipynb

Lines changed: 324 additions & 0 deletions
Large diffs are not rendered by default.

stompy/grid/multi_ugrid.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import copy
1616
import xarray as xr
1717
import numpy as np
18+
from .. import utils
1819
from . import unstructured_grid
1920
import logging
2021
log=logging.getLogger('multi_ugrid')
@@ -417,7 +418,7 @@ def __init__(self,paths,cleanup_dfm='auto',xr_kwargs={},grid=None,
417418

418419
def load(self,**xr_kwargs):
419420
if isinstance(self.paths[0],str):
420-
return [xr.open_dataset(p,**xr_kwargs) for p in self.paths]
421+
return [xr.open_dataset(p,**xr_kwargs) for p in utils.progress(self.paths)]
421422
elif isinstance(self.paths[0],xr.Dataset):
422423
return self.paths
423424
else:

stompy/io/rdb_datadescriptors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
def dd_to_synonyms(code):
1616
# print "Looking for synonyms of ",code
17-
m = re.match('\d\d_(.+)',code)
17+
m = re.match(r'\d\d_(.+)',code)
1818
if m:
1919
short_code = m.group(1)
2020
# print "Got a short code ",short_code

stompy/model/delft/custom_process.py

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,10 @@ def build_process_db(self):
8787
shutil.rmtree(self.proc_table_dst_dir)
8888
shutil.copytree(self.proc_table_src_dir,self.proc_table_dst_dir)
8989

90-
print("First call to waqpb_export")
90+
print("First call to waqpb_export - creating procesm.asc from decomposed tables")
9191
sys.stdout.flush()
9292
output = utils.call_with_path(self.waqpbexport,self.proc_table_dst_dir).decode('latin1')
9393
print("Suppressed output")
94-
# That says Normal end, but then goes on to
95-
# make proces.asc
9694
sys.stdout.flush()
9795
print("First call to waqpb_export DONE")
9896
sys.stdout.flush()
@@ -101,6 +99,7 @@ def build_process_db(self):
10199
# First line of proces_asc includes a process count. Rewrite that line, shove in
102100
# our new process, and copy the rest of the file.
103101

102+
print(f"Transcribing procesm.asc -> proces.asc, adding {len(self.custom_procs)} custom processes")
104103
procesm_asc=os.path.join(self.proc_table_dst_dir,'procesm.asc')
105104
proces_asc=os.path.join(self.proc_table_dst_dir,'proces.asc')
106105

@@ -116,18 +115,22 @@ def build_process_db(self):
116115
fp.write(line)
117116
sys.stdout.flush()
118117

119-
print("Calling waqpb_import")
118+
print("Calling waqpb_import to convert proces.asc back to tables")
120119
sys.stdout.flush()
121-
# Pretty sure this is the one that fails.
120+
# This is the one likely to fail.
122121
output = utils.call_with_path(self.waqpbimport,self.proc_table_dst_dir).decode('latin1')
123122
print("Suppressed output")
123+
#print(output) # uncomment this line to diagnose Fatal Error
124124
sys.stdout.flush()
125125
print("Return from waqpb_import")
126126
sys.stdout.flush()
127-
print("Second call to waqpb_export")
127+
128+
print("Second call to waqpb_export to take the updated tables and write compiled form of tables")
128129
sys.stdout.flush()
129130
output = utils.call_with_path(self.waqpbexport,self.proc_table_dst_dir).decode('latin1')
130131
print( "Suppressed output")
132+
#print("Output from waqpb_export")
133+
#print(output)
131134
sys.stdout.flush()
132135
print("Return from second call to waqpb_export")
133136
sys.stdout.flush()
@@ -285,3 +288,62 @@ def fall_velocity(self, **kw):
285288
"""
286289
return process
287290

291+
def custom_DynDen(self,**kw):
292+
idx=self.copy_count['DynDen']
293+
self.copy_count['DynDen']+=1
294+
if self.copy_count['DynDyn']>1:
295+
raise Exception("custom_DynDen can only be used once")
296+
297+
p=dict(name="DynDen")
298+
p.update(kw)
299+
300+
# Presumably want to enable it, though could be optional
301+
if isinstance(self,dflow_model.DFlowModel):
302+
self.dwaq.add_process(p['name'])
303+
else: # WaqModel or related
304+
self.add_process(p['name'])
305+
306+
# With SW_Uitz=0.0:
307+
# SD = PAConstant / ExtVl
308+
process=f"""
309+
{p['name']:10} Reuse Secchi depth for denit rate
310+
SECCHI ; module name
311+
123 ; TRswitch
312+
22; # input items for segments
313+
InvDenRate -999.000 x POC (1/m)
314+
IM1 0.00000 inorganic matter (IM1) (gDM/m3)
315+
IM2 0.00000 inorganic matter (IM2) (gDM/m3)
316+
IM3 0.00000 inorganic matter (IM3) (gDM/m3)
317+
POC1 0.00000 POC1 (fast decomposing fraction) (gC/m3)
318+
POC2 0.00000 POC2 (medium decomposing fraction) (gC/m3)
319+
POC3 0.00000 POC3 (slow decomposing fraction) (gC/m3)
320+
POC4 0.00000 POC4 (particulate refractory fraction) (gC/m3)
321+
ExtVlODS 0.00000 VL extinction by DOC (1/m)
322+
Chlfa 0.00000 Chlorophyll-a concentration (mg/m3)
323+
SW_Uitz 0.00000 Extinction by Uitzicht On (1) or Off (0) (-)
324+
UitZDEPT1 1.20000 Z1 (depth) (m)
325+
UitZDEPT2 1.00000 Z2 (depth) (m)
326+
UitZCORCH 2.50000 CORa correction factor (-)
327+
UitZC_DET 0.260000E-01 C3 coeff. absorption ash weight & detritus (-)
328+
UitZC_GL1 0.730000 C1 coeff. absorption ash weight & detritus (-)
329+
UitZC_GL2 1.00000 C2 coeff. absorption ash weight & detritus (-)
330+
UitZHELHM 0.140000E-01 Hel_h constant (1/nm)
331+
UitZTAU 7.80000 Tau constant calculation transparency (-)
332+
UitZangle 30.0000 Angle of incidence solar radiation (degrees)
333+
DMCFDetC 2.50000 DM:C ratio DetC (gDM/gC)
334+
DetCS1 1.70000 x Poole-Atkins constant (-)
335+
0; # input items for exchanges
336+
1; # output items for segments
337+
RcDenSed x 1st order denit m/d (m)
338+
0; # output items for exchanges
339+
1; # fluxes
340+
dDumDynDen x dummy flux to access Secchi (-)
341+
2; # stoichiometry lines
342+
IM1 dDumDynDen 0.00000
343+
POC1 dDumDynDen 0.00000
344+
0; # stoichiometry lines dispersion arrays
345+
0; # stoichiometry lines velocity arrays
346+
END
347+
"""
348+
self.custom_procs.append(process)
349+

stompy/model/delft/io.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def decstrip(s):
104104

105105
time0,time_unit = parse_time0(time_meta)
106106
times=time0 + time_unit*frames['tsec']
107-
ds['time']=( ('time',), times)
107+
ds['time']=( ('time',), times.astype('M8[ns]'))
108108
ds['tsec']=( ('time',), frames['tsec'])
109109

110110
region_names=[decstrip(s) for s in regions['name']]

stompy/model/delft/waq_scenario.py

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,7 +1040,7 @@ def check_volume_conservation_incr(self,seg_select=slice(None),
10401040
('type','S20')]
10411041
@property
10421042
def n_boundaries(self):
1043-
return -self.pointers[:,:2].min()
1043+
return max(0,-self.pointers[:,:2].min())
10441044

10451045
# for a while, was using 'grouped', but that relies on boundary segments,
10461046
# which didn't transfer well to aggregated domains which usually have many
@@ -1737,7 +1737,7 @@ def trav(c,mark):
17371737
ngroups+=1
17381738
return groups
17391739

1740-
def extract_transect_flow(self,transect,func=False,time_range=None):
1740+
def extract_transect_flow(self, transect, func=False, time_range=None, quantity='Q'):
17411741
"""
17421742
Extract time series of discharge through a transect as a function of time.
17431743
transect is expected to have the same structure as it is configured on
@@ -1749,15 +1749,24 @@ def extract_transect_flow(self,transect,func=False,time_range=None):
17491749
time_range: [np.datetime64,np.datetime64]
17501750

17511751
if func is True, instead return a function that takes a datetime64 and returns flow.
1752+
1753+
quantity: Q or area, determines the variable to extract
17521754
"""
17531755
exchs=transect[1]
17541756

17551757
def fn(t,exchs=exchs,hydro=self):
17561758
t_secs = (t - np.datetime64(hydro.time0))/np.timedelta64(1,'s')
1757-
flo = hydro.flows(t_secs)
1758-
signs=np.sign(exchs)
17591759
exch0=np.abs(exchs)-1
1760-
return np.sum(flo[exch0]*signs)
1760+
signs=np.sign(exchs)
1761+
1762+
if quantity=='Q':
1763+
flo = hydro.flows(t_secs)
1764+
return np.sum(flo[exch0]*signs)
1765+
elif quantity=='area':
1766+
area = hydro.areas(t_secs)
1767+
return np.sum(area[exch0])
1768+
else:
1769+
raise Exception("Only Q and area are supported outputs")
17611770
if func:
17621771
return fn
17631772

@@ -1772,15 +1781,20 @@ def fn(t,exchs=exchs,hydro=self):
17721781
t_secs=self.t_secs[tidx_start:tidx_stop]
17731782
times=np.datetime64(self.time0)+t_secs*np.timedelta64(1,'s')
17741783

1775-
Q=np.zeros( len(t_secs), np.float64)
1776-
for i,t in enumerate(times):
1777-
Q[i] = fn(t)
1784+
data=np.zeros( len(t_secs), np.float64)
1785+
for i,t in utils.progress(enumerate(times)):
1786+
data[i] = fn(t)
17781787

1779-
da= xr.DataArray(data=Q, dims=['time'],
1788+
da= xr.DataArray(data=data, dims=['time'],
17801789
coords=dict( time=times,
17811790
time_seconds=("time",t_secs) ),
1782-
attrs=dict(transect=transect[0], units='m3 s-1'))
1783-
da.name="discharge"
1791+
attrs=dict(transect=transect[0]))
1792+
if quantity=='Q':
1793+
da.name="discharge"
1794+
da.attrs['units']='m3 s-1'
1795+
elif quantity=='area':
1796+
da.name="area"
1797+
da.attrs['units']='m2'
17841798
return da
17851799

17861800
# Data formats on disk
@@ -2447,7 +2461,12 @@ def write_geom(self):
24472461

24482462
dest=self.flowgeom_filename
24492463
if os.path.exists(dest) or os.path.lexists(dest):
2450-
assert not os.path.samefile(dest,orig)
2464+
# RH: originally checked for not samefile
2465+
# then it was commented out, but that seems dangerous
2466+
# If this check is somehow problematic update comments here
2467+
# to explain
2468+
assert dest != orig
2469+
# assert not os.path.samefile(dest,orig)
24512470
if self.overwrite:
24522471
self.log.warning("Removing old geom file %s"%dest)
24532472
os.unlink(dest)

stompy/model/openfoam/cli.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from . import depth_average
2+
from ... import utils
3+
import os
4+
5+
def main(argv=None):
6+
import argparse
7+
8+
parser = argparse.ArgumentParser(description='Postprocess OpenFOAM results, esp. depth-averaging')
9+
10+
parser.add_argument("-c", "--case", help="Path to case, default is working directory",
11+
default=".")
12+
parser.add_argument("-U", "--velocity", help="Enable depth-averaged velocity output",
13+
action="store_true")
14+
parser.add_argument("-s","--stage", help="Enable stage (water surface elevation) output",
15+
action="store_true")
16+
parser.add_argument("-t","--time", help="Select output times as comma-separated time names, or 'all'",
17+
default="all")
18+
parser.add_argument("-r","--res", help="Output raster cell size (positive), or count in x (negative)",
19+
default=20.0, type=float)
20+
parser.add_argument("-f","--force", help="Force overwrite existing files",action='store_true')
21+
22+
args = parser.parse_args(argv)
23+
24+
pf = depth_average.PostFoam(sim_dir=args.case)
25+
26+
if args.time=='all':
27+
times = pf.available_times(omit_zero=True)
28+
else:
29+
times = args.time.split(",")
30+
print(f"{len(times)} time step(s) to process")
31+
32+
raster_info = pf.set_raster_parameters(dx=args.res)
33+
34+
print(f"Output rasters will be {raster_info['ny']}x{raster_info['nx']}")
35+
output_path=os.path.join(args.case,"postProcessing",'python')
36+
if not os.path.exists(output_path):
37+
os.makedirs(output_path)
38+
print(f"Writing output to {output_path}")
39+
40+
for t in utils.progress(times,msg="Time steps: %s"):
41+
if args.velocity:
42+
fn=os.path.join(output_path,f"U_{t}.tif")
43+
if args.force or not os.path.exists(fn):
44+
fld_U=pf.to_raster('U',t)
45+
# should put the velocity components into bands.
46+
fld_U.write_gdal(fn,overwrite=True)
47+
if args.stage:
48+
fn=os.path.join(output_path,f"stage_{t}.tif")
49+
if args.force or not os.path.exists(fn):
50+
fld_stage=pf.to_raster('wse',t)
51+
fld_stage.write_gdal(fn,overwrite=True)
52+
53+
if __name__ == '__main__':
54+
main()

0 commit comments

Comments
 (0)