Skip to content

Commit a05f812

Browse files
wcarthurmahmudulhasanGAkieranricardo
authored
BUGFIX for autocorrelation function (#140)
* track.ncReadTrackData returns true datetime objects - track.ncReadTrackData previously returned cftime.DatetimeGregorian objects, which caused newer versions of matplotlib.dates.num2date to fail. This is because we write the tracks with units of 'hours since 1900-01-01 00:00', but matplotlib.dates uses 1970-01-01 as the epoch, and works in units of days (with no way to specify units in the num2date function). * Read netcdf-format track file from GA SST - GA's Scenario selection tool allows users to download a selected track file, but due to technical constraints this is in a different format. This change allows TCRM to read the modified format. * Move definition statements to separate file * Move queries to separate file * Add script to run through all permutations of wind parameters * NHIRS 148 fix thread (#119) * assertDictEqual doesn't like complex values (like arrays) * NHIRS-148: Fixed issue with FlushCache failing. (#121) * update wind field model to get direction correct, and update documentation * Enhancement/linear rmax (#125) * fix double calculation of t3 - it was previously L3 / (L2 * L2) instead of L3 / L2 * only call Kepert wind field fortran code when a Holland pressure profile is used * Working version of cartopy-based scalebar * Pycxml compatability (#133) * gdal multi-threading for reprojection * Use expected DataProcess-InputFile * Create daily LTM MSLP file from ERA5 * Low resolution wind multipliers (#136) - Adds in a script to quickly downscale the wind multipliers and apply them to many wind fields quickly. If the wind multipliers have already been extracted this process takes minutes instead of weeks. * Bugfix for ACF function (#139) --------- Co-authored-by: mahmudulhasanGA <66761046+mahmudulhasanGA@users.noreply.github.com> Co-authored-by: Kieran Ricardo <kieranricardo@hotmail.com>
1 parent a9be18f commit a05f812

Some content is hidden

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

73 files changed

+1272
-307
lines changed

.github/workflows/tcrm-pylint.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@ jobs:
1111

1212
steps:
1313
- uses: actions/checkout@v2
14-
- name: Set up Python env
14+
- name: Set up environment
1515
uses: conda-incubator/setup-miniconda@v2.0.0
1616
with:
17+
python-version: 3.7
18+
mamba-version: "*"
19+
channels: conda-forge,defaults
20+
channel-priority: true
1721
activate-environment: tcrm
1822
environment-file: tcrmenv.yml
19-
python-version: 3.7
2023
auto-activate-base: false
24+
use-only-tar-bz2: true
2125

2226
- name: Install dependencies
2327
run: |

.github/workflows/tcrm-tests.yml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,26 @@ jobs:
1313
TCRM:
1414
name: Test TCRM
1515
runs-on: ubuntu-latest
16-
strategy:
16+
strategy:
1717
matrix:
1818
python-version: [3.7, 3.8, 3.9]
1919
steps:
2020
- uses: actions/checkout@v2
2121
- name: Set up environment
2222
uses: conda-incubator/setup-miniconda@v2.0.0
2323
with:
24+
python-version: ${{ matrix.python-version }}
25+
mamba-version: "*"
26+
channels: conda-forge,defaults
27+
channel-priority: true
2428
activate-environment: tcrm
2529
environment-file: tcrmenv.yml
26-
python-version: ${{ matrix.python-version }}
2730
auto-activate-base: false
31+
use-only-tar-bz2: true
2832

29-
- name: Test with nose
33+
- name: Test with pytest
34+
env:
35+
PYTHONPATH: ~/tcrm;~/tcrm/Utilities
3036
shell: bash -l {0}
3137
run: |
32-
python tests/run.py
38+
pytest -x --cov=. --cov-report xml

DataProcess/DataProcess.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,13 +171,17 @@ def processData(self, restrictToWindfieldDomain=False):
171171
if config.has_option('DataProcess', 'InputFile'):
172172
inputFile = config.get('DataProcess', 'InputFile')
173173
self.logger.info(f"Input file from DataProcess: {inputFile}")
174+
else:
175+
inputFile = None
174176

175177
if config.has_option('DataProcess', 'Source'):
176178
source = config.get('DataProcess', 'Source')
177179
self.logger.info(f"Loading {source} dataset")
178-
fn = config.get(source, 'Filename')
179-
path = config.get(source, 'Path')
180-
inputFile = pjoin(path, fn)
180+
if inputFile is None:
181+
# Use this as alternate source of input file (downloaded?)
182+
fn = config.get(source, 'Filename')
183+
path = config.get(source, 'Path')
184+
inputFile = pjoin(path, fn)
181185
self.logger.info(f"Input file set to {inputFile}")
182186

183187
# If input file has no path information, default to tcrm input folder

Evaluate/interpolateTracks.py

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
from Utilities.maputils import latLon2Azi
1010
from Utilities.loadData import loadTrackFile, maxWindSpeed
1111
from Utilities.track import Track, ncSaveTracks
12+
from Utilities.parallel import attemptParallel
13+
import pandas as pd
14+
1215

1316
LOG = logging.getLogger(__name__)
1417
LOG.addHandler(logging.NullHandler())
@@ -139,7 +142,9 @@ def interpolate(track, delta, interpolation_type=None):
139142
if interpolation_type == 'akima':
140143
# Use the Akima interpolation method:
141144
try:
142-
import akima
145+
from Utilities import akima
146+
nLon = akima.interpolate(timestep, track.Longitude, newtime)
147+
nLat = akima.interpolate(timestep, track.Latitude, newtime)
143148
except ImportError:
144149
LOG.exception(("Akima interpolation module unavailable "
145150
" - default to scipy.interpolate"))
@@ -148,10 +153,6 @@ def interpolate(track, delta, interpolation_type=None):
148153
nLat = splev(newtime, splrep(timestep, track.Latitude, s=0),
149154
der=0)
150155

151-
else:
152-
nLon = akima.interpolate(timestep, track.Longitude, newtime)
153-
nLat = akima.interpolate(timestep, track.Latitude, newtime)
154-
155156
elif interpolation_type == 'linear':
156157
nLon = interp1d(timestep, track.Longitude, kind='linear')(newtime)
157158
nLat = interp1d(timestep, track.Latitude, kind='linear')(newtime)
@@ -299,21 +300,48 @@ def parseTracks(configFile, trackFile, source, delta, outputFile=None,
299300
if trackFile.endswith("nc"):
300301
from Utilities.track import ncReadTrackData
301302
tracks = ncReadTrackData(trackFile)
303+
elif trackFile.endswith("xml"):
304+
from pycxml.pycxml import loadfile
305+
dfs = loadfile(trackFile)
306+
tracks = [bom2tcrm(df, i) for i, df in enumerate(dfs)]
302307
else:
303308
tracks = loadTrackFile(configFile, trackFile, source)
304309

305310
results = []
306311

307-
for track in tracks:
308-
if len(track.data) == 1:
309-
results.append(track)
310-
else:
311-
newtrack = interpolate(track, delta, interpolation_type)
312-
results.append(newtrack)
312+
# interpolating is memory intensive - only use a single process
313+
MPI = attemptParallel()
314+
if MPI.COMM_WORLD.rank == 0:
313315

314-
if outputFile:
315-
# Save data to file:
316-
ncSaveTracks(outputFile, results)
316+
for track in tracks:
317+
if len(track.data) == 1:
318+
results.append(track)
319+
else:
320+
newtrack = interpolate(track, delta, interpolation_type)
321+
results.append(newtrack)
317322

323+
if outputFile:
324+
# Save data to file:
325+
ncSaveTracks(outputFile, results)
326+
MPI.COMM_WORLD.barrier()
318327

319328
return results
329+
330+
331+
def bom2tcrm(df, trackId):
332+
"""
333+
Transforms a dataframe in BoM format into a tcrm track.
334+
335+
"""
336+
df['Datetime'] = pd.to_datetime(df.validtime)
337+
df['Speed'] = df.translation_speed
338+
df['CentralPressure'] = df.pcentre
339+
df['Longitude'] = df.longitude
340+
df['Latitude'] = df.latitude
341+
df['EnvPressure'] = df.poci
342+
df['rMax'] = df.rmax
343+
df['trackId'] = df.disturbance.values
344+
345+
track = Track(df)
346+
track.trackId = [trackId, trackId]
347+
return track

PlotInterface/AutoPlotHazard.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
from Utilities import pathLocator
3737
from Utilities import metutils
3838

39+
from database.queries import locationRecords
40+
3941
from PlotInterface.maps import saveHazardMap
4042
from PlotInterface.curves import saveHazardCurve
4143

PlotInterface/curves.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def subplot(self, axes, subfigure):
149149

150150
xdata, ydata, xlabel, ylabel, title = subfigure
151151

152-
axes.semilogx(xdata, ydata, '-', subsx=xdata)
152+
axes.semilogx(xdata, ydata, '-', subs=xdata)
153153
axes.set_xlabel(xlabel)
154154
axes.set_ylabel(ylabel)
155155
axes.set_title(title)
@@ -328,7 +328,7 @@ def subplot(self, axes, subfigure):
328328
"""
329329
xdata, ymean, ymax, ymin, xlabel, ylabel, title = subfigure
330330

331-
axes.semilogx(xdata, ymean, lw=2, subsx=xdata)
331+
axes.semilogx(xdata, ymean, lw=2, subs=xdata)
332332
if (ymin[0] > 0) and (ymax[0] > 0):
333333
self.addRange(axes, xdata, ymin, ymax)
334334

@@ -390,7 +390,7 @@ def subplot(self, axes, subfigure):
390390
log.debug("xvalues = {0} length".format(len(emprp)))
391391
log.debug("xvalues = {0}".format(emprp))
392392

393-
axes.semilogx(xdata, ymean, lw=2, subsx=xdata,
393+
axes.semilogx(xdata, ymean, lw=2, subs=xdata,
394394
label = 'Fitted hazard curve ({0})'.format(fit))
395395
axes.scatter(emprp[emprp > 1], events[emprp > 1], s=100,
396396
color='r', label = 'Empirical ARI')
@@ -478,8 +478,8 @@ def subplot(self, axes, subfigure):
478478
"""
479479
xdata, y1, y2, y2max, y2min, xlabel, ylabel, title = subfigure
480480

481-
axes.semilogx(xdata, y1, color='r', lw=2, label="", subsx=xdata)
482-
axes.semilogx(xdata, y2, color='k', lw=2, label="", subsx=xdata)
481+
axes.semilogx(xdata, y1, color='r', lw=2, label="", subs=xdata)
482+
axes.semilogx(xdata, y2, color='k', lw=2, label="", subs=xdata)
483483
self.addRange(axes, xdata, y2min, y2max)
484484
ylim = (0., np.max([100, np.ceil(y2.max()/10.)*10.]))
485485
axes.set_ylim(ylim)

PlotInterface/maps.py

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
2525
import seaborn as sns
2626

27+
from .scalebar import scale_bar
28+
2729
def levels(maxval, minval=0):
2830
"""
2931
Calculate a nice number of levels between `minval` and `maxval`
@@ -230,35 +232,20 @@ def maskOceans(self, mapobj, fillcolor="#66ccff"):
230232

231233
mapobj.add_feature(cartopy.feature.OCEAN, color=fillcolor)
232234

233-
def addMapScale(self, mapobj):
235+
def addMapScale(self, axes):
234236
"""
235-
Add a map scale to the curent `Basemap` instance. This
236-
automatically determines a 'nice' length forthe scale bar -
237+
Add a map scale to the curent `cartopy.mpl.geoaxes.GeoAxes` instance.
238+
This automatically determines a 'nice' length forthe scale bar -
237239
chosen to be approximately 20% of the map width at the centre
238240
of the map.
239241
240-
:param mapobj: Current `Basemap` instance to add the scale bar to.
242+
:param ax: Current `cartopy.mpl.geoaxes.GeoAxes` instance to add the
243+
scale bar to.
241244
245+
see https://stackoverflow.com/questions/32333870/
242246
"""
243-
return # TODO: migrate to cartopy - see https://stackoverflow.com/questions/32333870/
244-
245-
midlon = (mapobj.lonmax - mapobj.lonmin) / 2.
246-
midlat = (mapobj.latmax - mapobj.latmin) / 2.
247-
248-
xmin = mapobj.llcrnrx
249-
xmax = mapobj.urcrnrx
250-
ymin = mapobj.llcrnry
251-
ymax = mapobj.urcrnry
252-
253-
xloc = xmin + 0.15 * abs(xmax - xmin)
254-
yloc = ymin + 0.1 * abs(ymax - ymin)
247+
axes = scale_bar(axes, (0.05, 0.1))
255248

256-
lonloc, latloc = mapobj(xloc, yloc, inverse=True)
257-
258-
# Set scale length to nearest 100-km for 20% of map width
259-
scale_length = 100*int((0.2 * (xmax - xmin) / 1000.)/100)
260-
mapobj.drawmapscale(lonloc, latloc, midlon, midlat, scale_length,
261-
barstyle='fancy', zorder=10)
262249

263250
def createMap(self, axes, xgrid, ygrid, map_kwargs):
264251
"""
@@ -296,7 +283,7 @@ def subplot(self, axes, subfigure):
296283
self.addGraticule(axes, mapobj)
297284
self.addCoastline(mapobj)
298285
self.fillContinents(mapobj)
299-
self.addMapScale(mapobj)
286+
self.addMapScale(axes)
300287

301288
def plot(self):
302289
"""
@@ -380,7 +367,7 @@ def subplot(self, axes, subfigure):
380367
cmap = selectColormap(lvls)
381368
CS = mapobj.contourf(mx, my, data, levels=lvls,
382369
extend='both', cmap=cmap)
383-
CB = self.colorbar(CS, ticks=lvls[::2], ax=axes, extend='both')
370+
CB = self.colorbar(CS, ticks=lvls[::2], ax=axes)
384371
CB.set_label(cbarlab)
385372
axes.set_title(title)
386373
self.addGraticule(axes, mapobj)

PlotInterface/plotTimeseries.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def loadTimeseriesData(datafile):
4949
comments='#', delimiter=',', skip_header=1,
5050
converters=INPUT_CNVT)
5151
except ValueError:
52-
logging.warn("Timeseries data file is empty - returning empty array")
52+
logging.warning("Timeseries data file is empty - returning empty array")
5353
return np.empty(0, dtype={
5454
'names': INPUT_COLS,
5555
'formats': INPUT_FMTS})

0 commit comments

Comments
 (0)