Skip to content

Commit 2e839e0

Browse files
authored
Merge pull request #101 from axch/product-shape
Product shape - implement a python wrapper for the fortran core.
2 parents 6440c60 + 3a113eb commit 2e839e0

File tree

126 files changed

+921
-824
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+921
-824
lines changed

Diff for: .travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ before_install:
1515
- pip install sphinx_rtd_theme
1616
- pip install recommonmark
1717
- pip install matplotlib
18-
- python setup.py install
18+
- pip install -e .
1919

2020
install:
2121

Diff for: aronnax.f90

+2-2
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ program aronnax
116116

117117
namelist /GRID/ nx, ny, layers, dx, dy, fUfile, fVfile, wetMaskFile
118118

119-
namelist /INITIAL_CONDITONS/ initUfile, initVfile, initHfile, initEtaFile
119+
namelist /INITIAL_CONDITIONS/ initUfile, initVfile, initHfile, initEtaFile
120120

121121
namelist /EXTERNAL_FORCING/ zonalWindFile, meridionalWindFile, &
122122
DumpWind, wind_mag_time_series_file
@@ -127,7 +127,7 @@ program aronnax
127127
read(unit=8, nml=SPONGE)
128128
read(unit=8, nml=PHYSICS)
129129
read(unit=8, nml=GRID)
130-
read(unit=8, nml=INITIAL_CONDITONS)
130+
read(unit=8, nml=INITIAL_CONDITIONS)
131131
read(unit=8, nml=EXTERNAL_FORCING)
132132
close(unit=8)
133133

Diff for: aronnax/core.py

+108-36
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from contextlib import contextmanager
1111
import os.path as p
12+
import re
1213

1314
import numpy as np
1415
from scipy.io import FortranFile
@@ -94,61 +95,132 @@ def interpret_raw_file(name, nx, ny, layers):
9495

9596
### General input construction helpers
9697

97-
def write_initial_heights(grid, h_funcs):
98+
def depths(grid, *h_funcs):
9899
X,Y = np.meshgrid(grid.x, grid.y)
99100
initH = np.ones((len(h_funcs), grid.ny, grid.nx))
100101
for i, f in enumerate(h_funcs):
101102
if isinstance(f, (int, long, float)):
102103
initH[i,:,:] = f
103104
else:
104105
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
107107

108-
def write_wind_x(grid, func):
108+
def wind_x(grid, func):
109109
X,Y = np.meshgrid(grid.xp1, grid.y)
110110
if isinstance(func, (int, long, float)):
111111
wind_x = np.ones(grid.ny, grid.nx+1) * func
112112
else:
113113
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
116115

117-
def write_wind_y(grid, func):
116+
def wind_y(grid, func):
118117
X,Y = np.meshgrid(grid.y, grid.xp1)
119118
if isinstance(func, (int, long, float)):
120119
wind_y = np.ones(grid.ny+1, grid.nx) * func
121120
else:
122121
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
125123

126124
### Specific construction helpers
127125

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

Comments
 (0)