Skip to content

Commit 984c422

Browse files
author
Joe Hamman
committed
Add C convolution routine for speedup
-Also changes to structure of ring and unit hydrographs. Changes are internal and are backward compatable. -Fixed bug in agg_tsteps logic
1 parent eb662f1 commit 984c422

File tree

7 files changed

+281
-62
lines changed

7 files changed

+281
-62
lines changed

rvic/c_convolve.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
4+
void c_convolve(const int nsources, /*scalar - number of sources*/
5+
const int noutlets, /*scalar - length of subset*/
6+
const int subset_length, /*scalar - length of subset*/
7+
const int x_size,
8+
const int* source2outlet_ind, /*1d array - source to outlet mapping*/
9+
const int* source_y_ind, /*1d array - source y location*/
10+
const int* source_x_ind, /*1d array - source x location*/
11+
const int* source_time_offset, /*1d array - source time offset*/
12+
const double* unit_hydrograph, /*2d array[times][sources] - unit hydrographs*/
13+
const double* aggrunin, /*2d array[ysize][xsize] - vic runoff flux*/
14+
double* ring) /*2d array[times][outlets] - convolution ring*/
15+
{
16+
const double* runin; /*pointer to sources runoff flux*/
17+
int s, i, j; /*counters*/
18+
int y, x, offset, outlet; /*2d indicies*/
19+
int xyind, rind, uhind; /*1d indicies*/
20+
21+
/*Loop through all sources*/
22+
for (s = 0; s < nsources; s++) {
23+
24+
outlet = source2outlet_ind[s];
25+
y = source_y_ind[s];
26+
x = source_x_ind[s];
27+
offset = source_time_offset[s];
28+
29+
//1d index location
30+
//2d-->1d indexing goes like this: ind = y*x_size + x
31+
xyind = y*x_size + x;
32+
33+
runin = &aggrunin[xyind];
34+
35+
// if (*runin > 10000.0) {
36+
// printf("runin is big: %f\n", *runin);
37+
// printf("xyind: %i\n", xyind);
38+
// printf("y: %i\n", y);
39+
// printf("x: %i\n", x);
40+
41+
/* Do the convolution */
42+
for (i = 0; i < subset_length; i++) {
43+
j = i + offset;
44+
45+
//1d index locations
46+
rind = j * noutlets + outlet;
47+
uhind = i * nsources + s;
48+
49+
ring[rind] += unit_hydrograph[uhind] * *runin;
50+
}
51+
}
52+
}

rvic/convolution_wrapper.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
"""
2+
convolution_wrapper.py
3+
4+
ctypes wrapper for convolve.c
5+
6+
gcc -shared -o c_convolve.so c_convolve.c
7+
"""
8+
9+
import numpy as np
10+
import ctypes
11+
12+
import os
13+
# os.system('gcc -shared -o c_convolve.so c_convolve.c')
14+
15+
try:
16+
path_to_file = os.path.split(__file__)[0]
17+
18+
_convolution = np.ctypeslib.load_library('c_convolve.so', path_to_file)
19+
args = [ctypes.c_int,
20+
ctypes.c_int,
21+
ctypes.c_int,
22+
ctypes.c_int,
23+
np.ctypeslib.ndpointer(np.int32),
24+
np.ctypeslib.ndpointer(np.int32),
25+
np.ctypeslib.ndpointer(np.int32),
26+
np.ctypeslib.ndpointer(np.int32),
27+
np.ctypeslib.ndpointer(np.float64),
28+
np.ctypeslib.ndpointer(np.float64),
29+
np.ctypeslib.ndpointer(np.float64)]
30+
_convolution.c_convolve.argtypes = args
31+
_convolution.c_convolve.restype = None
32+
33+
def rvic_convolve(*args):
34+
"""args:
35+
36+
nsources,
37+
noutlets,
38+
subset_length,
39+
xsize,
40+
source2outlet_ind,
41+
source_y_ind,
42+
source_x_ind,
43+
source_time_offset,
44+
unit_hydrograph,
45+
aggrunin,
46+
ring
47+
"""
48+
_convolution.c_convolve(*args)
49+
50+
return
51+
except:
52+
print('Using brodcasting convolution method because there was a problem '
53+
'loading c_convolve.c')
54+
55+
def rvic_convolve(nsources, noutlets, subset_length, xsize,
56+
source2outlet_ind, source_y_ind, source_x_ind,
57+
source_time_offset, unit_hydrograph, aggrunin, ring):
58+
# numpy brodcasting method
59+
for s, outlet in enumerate(source2outlet_ind):
60+
y = source_y_ind[s]
61+
x = source_x_ind[s]
62+
i = source_time_offset[s]
63+
j = i + subset_length
64+
ring[i:j, outlet] += np.squeeze(unit_hydrograph[:, s] * aggrunin[y, x])
65+
66+
# # pure python convolution
67+
# for s, outlet in enumerate(source2outlet_ind):
68+
# y = source_y_ind[s]
69+
# x = source_x_ind[s]
70+
# for i in xrange(subset_length):
71+
# j = i + source_time_offset[s]
72+
# ring[j, outlet] += (unit_hydrograph[i, s] * aggrunin[y, x])
73+
return
74+
75+
76+
def test():
77+
nsources = 20
78+
subset_length = 10
79+
full_time_length = 15
80+
noutlets = 4
81+
source2outlet_ind = np.linspace(0, noutlets, nsources,
82+
endpoint=False).astype(np.int32)
83+
84+
ysize = 12
85+
xsize = 15
86+
87+
source_y_ind = np.random.randint(0, ysize-1, nsources).astype(np.int32)
88+
source_x_ind = np.random.randint(0, xsize-1, nsources).astype(np.int32)
89+
90+
source_time_offset = np.random.randint(0, 4, nsources).astype(np.int32)
91+
92+
aggrunin = np.random.uniform(size=(ysize, xsize))
93+
unit_hydrograph = np.zeros((subset_length, nsources), dtype=np.float64)
94+
unit_hydrograph[0:4, :] = 0.25
95+
ring = np.zeros((full_time_length, noutlets), dtype=np.float64)
96+
97+
for i in xrange(10):
98+
aggrunin = np.random.uniform(size=(ysize, xsize))
99+
rvic_convolve(nsources, noutlets, subset_length, xsize,
100+
source2outlet_ind, source_y_ind, source_x_ind,
101+
source_time_offset, unit_hydrograph, aggrunin, ring)

rvic/history.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
44
Summary:
55
This is the core history file module for the rvic model.
6-
The core of the module is the Tape class. The basic procedure is as follows:
6+
The core of the module is the Tape class. The basic procedure is as
7+
follows:
78
- initialization: sets tape options, determines filenames, etc.
89
- update: method that incorporates new fluxes into the history tape.
9-
- __next_update_out_data: method to determine when to update the outdata container
10+
- __next_update_out_data: method to determine when to update the
11+
outdata container
1012
1113
"""
1214
import os
@@ -17,9 +19,11 @@
1719
from logging import getLogger
1820
from log import LOG_NAME
1921
from share import SECSPERDAY, HOURSPERDAY, TIMEUNITS, NC_INT, NC_FLOAT
20-
from share import NC_DOUBLE, NC_CHAR, WATERDENSITY
22+
from share import NC_DOUBLE, WATERDENSITY
23+
# from share import NC_CHAR, RVIC_TRACERS
2124
import share
2225

26+
2327
# -------------------------------------------------------------------- #
2428
# create logger
2529
log = getLogger(LOG_NAME)
@@ -60,8 +64,8 @@ def __init__(self, time_ord, caseid, Rvar, tape_num=0,
6064
self._glob_ats = glob_ats
6165

6266
self._out_data_i = 0 # position counter for out_data array
63-
self._out_times = np.zeros(self._mfilt, type=np.float64)
64-
self._out_time_bnds = np.zeros((self._mfilt, 2), type=np.float64)
67+
self._out_times = np.zeros(self._mfilt, dtype=np.float64)
68+
self._out_time_bnds = np.zeros((self._mfilt, 2), dtype=np.float64)
6569

6670
self.__get_rvar(Rvar) # Get the initial Rvar fields
6771
self._grid_shape = grid_area.shape
@@ -189,8 +193,9 @@ def update(self, data2tape, time_ord):
189193
# ------------------------------------------------------------ #
190194
# Update the fields
191195
for field in self._fincl:
196+
tracer = 'LIQ'
192197
log.debug('updating {0}'.format(field))
193-
fdata = data2tape[field]
198+
fdata = data2tape[field][tracer]
194199
if self._avgflag == 'A':
195200
self._temp_data[field] += fdata
196201
elif self._avgflag == 'I':
@@ -237,7 +242,9 @@ def __update_out_data(self):
237242
if self._outtype == 'grid':
238243
# ---------------------------------------------------- #
239244
# Grid the fields
240-
self._out_data[field][self._out_data_i, self._outlet_y_ind, self._outlet_x_ind] = self._temp_data[field][:]
245+
self._out_data[field][self._out_data_i,
246+
self._outlet_y_ind,
247+
self._outlet_x_ind] = self._temp_data[field][:]
241248
# ---------------------------------------------------- #
242249
else:
243250
self._out_data[field][self._out_data_i, :] = self._temp_data[field]
@@ -265,7 +272,7 @@ def __update_out_data(self):
265272
# ---------------------------------------------------------------- #
266273
# Get import rvar fields
267274
def __get_rvar(self, rvar):
268-
""" Get the rvar Fields that are useful in writing output """
275+
""" Get the rvar Fields that are useful for writing output """
269276
self._dt = rvar.unit_hydrograph_dt
270277
self._num_outlets = rvar.n_outlets
271278
self._outlet_decomp_ind = rvar.outlet_decomp_ind

rvic/read_forcing.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,18 +249,21 @@ def read(self, timestamp):
249249
if i == 0:
250250
forcing['LIQ'] = self.current_fhdl.variables[fld][self.current_tind, :, :] * self.fld_mult[fld]
251251
else:
252-
forcing['LIQ'] += self.current_fhdl.variables[fld][self.current_tind, :, :] * self.fld_mult[fld]
252+
forcing['LIQ'] += self.current_fhdl.variables[fld][self.current_tind, :, :] * self.fld_mult[fld].astype(np.float64)
253253

254254
for i, fld in enumerate(self.ice_flds):
255255
self.current_fhdl.variables[fld].set_auto_maskandscale(False)
256256
if i == 0:
257-
forcing['ICE'] = self.current_fhdl.variables[fld][self.current_tind, :, :] * self.fld_mult[fld]
257+
forcing['ICE'] = self.current_fhdl.variables[fld][self.current_tind, :, :] * self.fld_mult[fld].astype(np.float64)
258258
else:
259-
forcing['ICE'] += self.current_fhdl.variables[fld][self.current_tind, :, :] * self.fld_mult[fld]
259+
forcing['ICE'] += self.current_fhdl.variables[fld][self.current_tind, :, :] * self.fld_mult[fld].astype(np.float64)
260260

261261
# move forward one step
262262
self.current_tind += 1
263263

264+
for field in forcing:
265+
forcing[field] = forcing[field].astype(np.float64)
266+
264267
return forcing
265268
# ------------------------------------------------------------ #
266269
# ---------------------------------------------------------------- #

rvic/share.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
RPOINTER = 'rpointer'
5454

5555
# tracers
56-
RVIC_TRACERS = ('LIQ',)
56+
RVIC_TRACERS = ('LIQ',) # Before changing, update history module
5757

5858
# Calendar key number for linking with CESM
5959
CALENDAR_KEYS = {0: ['None'],

0 commit comments

Comments
 (0)