|
9 | 9 |
|
10 | 10 | from contextlib import contextmanager
|
11 | 11 | import os.path as p
|
| 12 | +import re |
12 | 13 |
|
13 | 14 | import numpy as np
|
14 | 15 | from scipy.io import FortranFile
|
@@ -94,61 +95,132 @@ def interpret_raw_file(name, nx, ny, layers):
|
94 | 95 |
|
95 | 96 | ### General input construction helpers
|
96 | 97 |
|
97 |
| -def write_initial_heights(grid, h_funcs): |
| 98 | +def depths(grid, *h_funcs): |
98 | 99 | X,Y = np.meshgrid(grid.x, grid.y)
|
99 | 100 | initH = np.ones((len(h_funcs), grid.ny, grid.nx))
|
100 | 101 | for i, f in enumerate(h_funcs):
|
101 | 102 | if isinstance(f, (int, long, float)):
|
102 | 103 | initH[i,:,:] = f
|
103 | 104 | else:
|
104 | 105 | initH[i,:,:] = f(X, Y)
|
105 |
| - with fortran_file('initH.bin', 'w') as f: |
106 |
| - f.write_record(initH.astype(np.float64)) |
| 106 | + return initH |
107 | 107 |
|
108 |
| -def write_wind_x(grid, func): |
| 108 | +def wind_x(grid, func): |
109 | 109 | X,Y = np.meshgrid(grid.xp1, grid.y)
|
110 | 110 | if isinstance(func, (int, long, float)):
|
111 | 111 | wind_x = np.ones(grid.ny, grid.nx+1) * func
|
112 | 112 | else:
|
113 | 113 | wind_x = func(X, Y)
|
114 |
| - with fortran_file('wind_x.bin', 'w') as f: |
115 |
| - f.write_record(wind_x.astype(np.float64)) |
| 114 | + return wind_x |
116 | 115 |
|
117 |
| -def write_wind_y(grid, func): |
| 116 | +def wind_y(grid, func): |
118 | 117 | X,Y = np.meshgrid(grid.y, grid.xp1)
|
119 | 118 | if isinstance(func, (int, long, float)):
|
120 | 119 | wind_y = np.ones(grid.ny+1, grid.nx) * func
|
121 | 120 | else:
|
122 | 121 | wind_y = func(X, Y)
|
123 |
| - with fortran_file('wind_y.bin', 'w') as f: |
124 |
| - f.write_record(wind_y.astype(np.float64)) |
| 122 | + return wind_y |
125 | 123 |
|
126 | 124 | ### Specific construction helpers
|
127 | 125 |
|
128 |
| -def write_f_plane(nx, ny, coeff): |
129 |
| - """Write files defining an f-plane approximation to the Coriolis force.""" |
130 |
| - with fortran_file('fu.bin', 'w') as f: |
131 |
| - f.write_record(np.ones((nx+1, ny), dtype=np.float64) * coeff) |
132 |
| - with fortran_file('fv.bin', 'w') as f: |
133 |
| - f.write_record(np.ones((nx, ny+1), dtype=np.float64) * coeff) |
134 |
| - |
135 |
| -def write_beta_plane(grid, f0, beta): |
136 |
| - """Write files defining a beta-plane approximation to the Coriolis force.""" |
137 |
| - with fortran_file('fu.bin', 'w') as f: |
138 |
| - _, Y = np.meshgrid(grid.xp1, grid.y) |
139 |
| - fu = f0 + Y*beta |
140 |
| - f.write_record(fu.astype(np.float64)) |
141 |
| - with fortran_file('fv.bin', 'w') as f: |
142 |
| - _, Y = np.meshgrid(grid.x, grid.yp1) |
143 |
| - fv = f0 + Y*beta |
144 |
| - f.write_record(fv.astype(np.float64)) |
145 |
| - |
146 |
| -def write_rectangular_pool(nx, ny): |
147 |
| - """Write the wet mask file for a maximal rectangular pool.""" |
148 |
| - with fortran_file('wetmask.bin', 'w') as f: |
149 |
| - wetmask = np.ones((nx, ny), dtype=np.float64) |
150 |
| - wetmask[ 0, :] = 0 |
151 |
| - wetmask[-1, :] = 0 |
152 |
| - wetmask[ :, 0] = 0 |
153 |
| - wetmask[ :,-1] = 0 |
154 |
| - f.write_record(wetmask) |
| 126 | +def f_plane_f_u(grid, coeff): |
| 127 | + """Define an f-plane approximation to the Coriolis force (u location).""" |
| 128 | + return np.ones((grid.nx+1, grid.ny), dtype=np.float64) * coeff |
| 129 | + |
| 130 | +def f_plane_f_v(grid, coeff): |
| 131 | + """Define an f-plane approximation to the Coriolis force (v location).""" |
| 132 | + return np.ones((grid.nx, grid.ny+1), dtype=np.float64) * coeff |
| 133 | + |
| 134 | +def beta_plane_f_u(grid, f0, beta): |
| 135 | + """Define a beta-plane approximation to the Coriolis force (u location).""" |
| 136 | + _, Y = np.meshgrid(grid.xp1, grid.y) |
| 137 | + fu = f0 + Y*beta |
| 138 | + return fu |
| 139 | + |
| 140 | +def beta_plane_f_v(grid, f0, beta): |
| 141 | + """Define a beta-plane approximation to the Coriolis force (v location).""" |
| 142 | + _, Y = np.meshgrid(grid.x, grid.yp1) |
| 143 | + fv = f0 + Y*beta |
| 144 | + return fv |
| 145 | + |
| 146 | +def rectangular_pool(grid): |
| 147 | + """The wet mask file for a maximal rectangular pool.""" |
| 148 | + nx = grid.nx; ny = grid.ny |
| 149 | + wetmask = np.ones((nx, ny), dtype=np.float64) |
| 150 | + wetmask[ 0, :] = 0 |
| 151 | + wetmask[-1, :] = 0 |
| 152 | + wetmask[ :, 0] = 0 |
| 153 | + wetmask[ :,-1] = 0 |
| 154 | + return wetmask |
| 155 | + |
| 156 | +specifier_rx = re.compile(r':(.*):(.*)') |
| 157 | + |
| 158 | +ok_generators = { |
| 159 | + 'depths': depths, |
| 160 | + 'beta_plane_f_u': beta_plane_f_u, |
| 161 | + 'beta_plane_f_v': beta_plane_f_v, |
| 162 | + 'f_plane_f_u': f_plane_f_u, |
| 163 | + 'f_plane_f_v': f_plane_f_v, |
| 164 | + 'rectangular_pool': rectangular_pool, |
| 165 | + 'wind_x': wind_x, |
| 166 | + 'wind_y': wind_y, |
| 167 | +} |
| 168 | + |
| 169 | +def interpret_data_specifier(string): |
| 170 | + m = re.match(specifier_rx, string) |
| 171 | + if m: |
| 172 | + name = m.group(1) |
| 173 | + arg_str = m.group(2) |
| 174 | + if len(arg_str) > 0: |
| 175 | + args = [float(a) for a in arg_str.split(',')] |
| 176 | + else: |
| 177 | + args = [] |
| 178 | + return (ok_generators[name], args) |
| 179 | + else: |
| 180 | + return None |
| 181 | + |
| 182 | +def interpret_requested_data(requested_data, shape, config): |
| 183 | + """Interpret a flexible input data specification. |
| 184 | +
|
| 185 | + The requested_data can be one of |
| 186 | +
|
| 187 | + - TODO A string giving the path to a NetCDF file, whose content |
| 188 | + will be interpolated to match the desired grid specification; |
| 189 | +
|
| 190 | + - A string giving the path to a raw Fortran array file, whose |
| 191 | + content will be used as-is; |
| 192 | +
|
| 193 | + - TODO A numpy array in memory, whose content will be used as-is, |
| 194 | + or TODO interpolated; or |
| 195 | +
|
| 196 | + - A string specifying auto-generation of the required data, in this format: |
| 197 | + :<generator_func_name>:arg1,arg2,...argn |
| 198 | +
|
| 199 | + - Python objects specifying auto-generation of the required data. |
| 200 | + In this case, `interpret_requested_data` will construct the |
| 201 | + appropriate `Grid` instance and pass it, together with the |
| 202 | + `requested_data`, to an appropriate meta-generator for the array |
| 203 | + shape of the needful datum (determined by the `shape` argument). |
| 204 | + The exact API varies with the meta-generator, but they typically |
| 205 | + interpret numbers as that constant and functions as an analytic |
| 206 | + definition of the field, which is evaluated on appropriate numpy |
| 207 | + arrays to produce the needed numerical values. |
| 208 | + """ |
| 209 | + grid = Grid(config.getint("grid", "nx"), config.getint("grid", "ny"), |
| 210 | + config.getfloat("grid", "dx"), config.getfloat("grid", "dy")) |
| 211 | + if isinstance(requested_data, basestring): |
| 212 | + candidate = interpret_data_specifier(requested_data) |
| 213 | + if candidate is not None: |
| 214 | + (func, args) = candidate |
| 215 | + return func(grid, *args) |
| 216 | + else: |
| 217 | + # Assume Fortran file name |
| 218 | + with fortran_file(requested_data, 'r') as f: |
| 219 | + return f.read_reals(dtype=np.float64) |
| 220 | + else: |
| 221 | + if shape == "3d": |
| 222 | + return depths(grid, *requested_data) |
| 223 | + if shape == "2dx": |
| 224 | + return wind_x(grid, requested_data) |
| 225 | + else: |
| 226 | + raise Exception("TODO implement custom generation for other input shapes") |
0 commit comments