22import numpy as np
33from datetime import date
44from numba .typed import List
5- #import nvtx
65
76# Defined Functions
87from .jdtodatevec import jdToDateVec
9- from .getdaysperyear import getDaysPerYear
10- from .getwetstate import getWetState
118
129## Global constants
1310from .global_ import missingDay
1411from .global_ import stateBad
1512from .global_ import ndaysYearLeap
1613from .global_ import idxfebTwentyNine
17- from .global_ import yesterday , today , tomorrow
14+ from .global_ import stateWetWet , stateWetDry , stateDryWet , stateDryDry
15+
16+
17+ def _build_day_mapping (yearStart , yearEnd , idxYearBase ):
18+ """Build mapping from linear day index to (loopDay, idxYear) for a station."""
19+ loopDay_list = []
20+ idxYear_list = []
21+ for yr_offset in range (yearEnd - yearStart + 1 ):
22+ year = yearStart + yr_offset
23+ is_leap = (year % 4 == 0 and (year % 100 != 0 or year % 400 == 0 ))
24+ nDays = 366 if is_leap else 365
25+ for d in range (ndaysYearLeap ):
26+ if d == idxfebTwentyNine and nDays != ndaysYearLeap :
27+ continue
28+ loopDay_list .append (d )
29+ idxYear_list .append (idxYearBase + yr_offset )
30+ return np .array (loopDay_list , dtype = np .intp ), np .array (idxYear_list , dtype = np .intp )
31+
1832
19- #@nvtx.annotate()
2033def dailySequences (nSeasons ,
2134 nYearsPool ,
2235 stnDetails ,
@@ -44,22 +57,23 @@ def dailySequences(nSeasons,
4457 dimensional with the first dimension being the seasons and the
4558 second the number of stations returned. This is because there
4659 are possible different correlations between stations over
47- different seasons.
60+ different seasons.
4861 param : dict where keys are words and values are float
49- Dictionary of the run parameters.
62+ Dictionary of the run parameters.
5063 param_path : dict where keys are words and values are str
5164 Dictionary of the necessary paths.\n
5265
5366 Returns
5467 ----------
5568 dailyDepth : list
56- A list of arrays (one for each season) containing the daily depth sequences.
69+ A list of arrays (one for each season) containing the daily depth sequences.
5770 dailyWetState : list
5871 A list of arrays (one for each season) containing the daily wetState sequences.
59- """
72+ """
6073 dailyDepth = List ()
6174 dailyWetState = List ()
62- workingRainDepth = np .zeros ((3 ,1 ))
75+ dryWetCutoff = param ['dryWetCutoff' ]
76+
6377 for loopSeason in range (nSeasons ):
6478 # Allocate RAM for this daily series:
6579 nYears = int (nYearsPool [loopSeason ].item ())
@@ -71,115 +85,91 @@ def dailySequences(nSeasons,
7185 (ndaysYearLeap ,
7286 nYears ))
7387 * stateBad )
74-
88+
89+ depth_s = dailyDepth [loopSeason ]
90+ wetState_s = dailyWetState [loopSeason ]
91+
7592 #Counter through the year dimension of our arrays
7693 idxYear = 0
77-
94+
7895 for loopStation in range (nearStationIdx [0 ,:].size ):
7996 # Grab a conveniance variable:
8097 currStnIndex = int (nearStationIdx [loopSeason , loopStation ])
8198
8299 if currStnIndex == 0 :
83100 # There are no more stations for this season
84101 break
102+
103+ # Get the start and end years from the NetCDF then compute the
104+ # number of years in the sequence.
105+ stnIdx = int (stnDetails ['stnIndex' ][currStnIndex ])
106+ if station_cache is not None :
107+ daySeries , rainfall_data = station_cache .get (stnIdx )
108+ fnameNC = station_cache .filename (stnIdx )
85109 else :
86- # Get the start and end years from the NetCDF then compute the
87- # number of years in the sequence. We also want to reshape our
88- # linear vector of rainfall data from the NetCDF into the
89- # year-on-year array herein.
90- stnIdx = int (stnDetails ['stnIndex' ][currStnIndex ])
91- if station_cache is not None :
92- daySeries , rainfall_data = station_cache .get (stnIdx )
93- fnameNC = station_cache .filename (stnIdx )
94- else :
95- import netCDF4 as nc
96- fnameNC = ('{}/plv{:06}.nc' .format (param_path ['pathSubDaily' ], stnIdx ))
97- ds = nc .Dataset (fnameNC )
98- daySeries = ds ['day' ][:].data
99- rainfall_data = ds ['rainfall' ][:].data
100- print ('Processing station:' , fnameNC )
101- dayVecStart = jdToDateVec (daySeries [0 ])
102- dayVecEnd = jdToDateVec (daySeries [- 1 ])
103- yearStart = int (dayVecStart [0 ])
104- yearEnd = int (dayVecEnd [0 ])
105- tmpSubDaily = rainfall_data / 10
106- # The algorithm below works on the assumption that the
107- # tmpSubDaily array is populated with full years. So pad out
108- # the data array to make full years with missingDay values.
109- nDaysKnown = (date .toordinal (date (yearEnd ,12 ,31 ))
110- - date .toordinal (date (yearStart ,1 ,1 ))+ 1 )
111- tmpDaily = np .ones ((1 ,nDaysKnown )) * missingDay
112- # Get an index into the tmpDaily array where our known data
113- # should start.
114- dataIdxStart = (date .toordinal (date (
115- int (dayVecStart [0 ]), int (dayVecStart [1 ]), int (dayVecStart [2 ])))
116- - date .toordinal (date (yearStart ,1 ,1 )))
117- dataIdxEnd = (date .toordinal (date (
118- int (dayVecEnd [0 ]),int (dayVecEnd [1 ]),int (dayVecEnd [2 ])))
119- - date .toordinal (date (yearStart ,1 ,1 ))+ 1 )
120-
121- # The sum here works because the method described in the paper
122- # is based on increments per day. We do a bit of a fudge when
123- # dealing with daily read data aggregated to 9am.
124- tmpDaily [0 ,dataIdxStart :dataIdxEnd ] = tmpSubDaily .sum (axis = 2 )
125-
126- # This is the index into the linear array tmpDaily.
127- idxDayLinear = 0
128- # Loop over each year in this station
129- for loopYear in range (yearStart ,yearEnd + 1 ):
130- # Loop over the days and compute the wetness index. I
131- # don't think that this can be parallelised as it works on
132- # offset indexing.
133- nDaysCurrYear = getDaysPerYear (loopYear )
134- #idxYear += 1
135-
136- #for loopDay in range(nDaysCurrYear):
137- for loopDay in range (ndaysYearLeap ):
138- # We are now looping through the days of a particular
139- # year.
140- if loopDay == 0 and loopYear == yearStart :
141- # No previous day, assume dry as its a pretty good bet
142- # in Australia, except in the tropics. :-[, got to
143- # assume something.
144- #
145- # This is the first time through, so populate our
146- # working array
147- workingRainDepth [yesterday ] = 0
148- workingRainDepth [today ] = tmpDaily [0 , idxDayLinear ]
149- workingRainDepth [tomorrow ] = tmpDaily [0 , idxDayLinear + 1 ]
150- elif (loopDay == idxfebTwentyNine and
151- nDaysCurrYear != ndaysYearLeap ):
152- # If we are on the 29th day of the year but this is
153- # not a leap year increment loopDay by one as that
154- # is the index we are looping through our 2D array
155- # year with. Or, just continue to the next
156- # iteration of the day loop. But don't index our
157- # linear counter.
158- continue
159- elif loopDay == (ndaysYearLeap - 1 ) and loopYear == yearEnd :
160- # This is the end of our sequence, assume dry as
161- # per the start
162- workingRainDepth [tomorrow ] = 0
163- else :
164- # This is a normal day out in the middle of the
165- # sequence, so get ready for the next loop:
166- idxDayLinear = idxDayLinear + 1
167- workingRainDepth [yesterday ] = workingRainDepth [today ]
168- workingRainDepth [today ] = workingRainDepth [tomorrow ]
169- workingRainDepth [tomorrow ] = tmpDaily [0 , idxDayLinear + 1 ]
170-
171-
172- # Store the depth for this day
173- dailyDepth [loopSeason ][loopDay , idxYear ] = (
174- tmpDaily [0 , idxDayLinear ]
175- )
176-
177- # Decide our wetness state for this day
178- if np .all (workingRainDepth >= 0.0 - np .spacing (abs (workingRainDepth ))):
179- # But only if we have "good" data. The default is
180- # to leave it as zero
181- dailyWetState [loopSeason ][loopDay ,idxYear ] = (
182- getWetState (workingRainDepth , param ['dryWetCutoff' ])
183- )
184- idxYear += 1
185- return dailyDepth , dailyWetState
110+ import netCDF4 as nc
111+ fnameNC = ('{}/plv{:06}.nc' .format (param_path ['pathSubDaily' ], stnIdx ))
112+ ds = nc .Dataset (fnameNC )
113+ daySeries = ds ['day' ][:].data
114+ rainfall_data = ds ['rainfall' ][:].data
115+ print ('Processing station:' , fnameNC )
116+ dayVecStart = jdToDateVec (daySeries [0 ])
117+ dayVecEnd = jdToDateVec (daySeries [- 1 ])
118+ yearStart = int (dayVecStart [0 ])
119+ yearEnd = int (dayVecEnd [0 ])
120+ nYearsStation = yearEnd - yearStart + 1
121+
122+ # Pad out the data array to make full years with missingDay values.
123+ nDaysKnown = (date .toordinal (date (yearEnd ,12 ,31 ))
124+ - date .toordinal (date (yearStart ,1 ,1 ))+ 1 )
125+ tmpDaily = np .full (nDaysKnown , missingDay )
126+ dataIdxStart = (date .toordinal (date (
127+ int (dayVecStart [0 ]), int (dayVecStart [1 ]), int (dayVecStart [2 ])))
128+ - date .toordinal (date (yearStart ,1 ,1 )))
129+ dataIdxEnd = (date .toordinal (date (
130+ int (dayVecEnd [0 ]),int (dayVecEnd [1 ]),int (dayVecEnd [2 ])))
131+ - date .toordinal (date (yearStart ,1 ,1 ))+ 1 )
132+
133+ # Sum sub-daily timesteps to get daily totals.
134+ # rainfall_data has shape (1, nDays, 240); sum over subday axis
135+ tmpDaily [dataIdxStart :dataIdxEnd ] = (rainfall_data / 10 ).sum (axis = 2 ).ravel ()
136+
137+ # Build mapping from linear day index to (loopDay, idxYear)
138+ loopDay_arr , idxYear_arr = _build_day_mapping (yearStart , yearEnd , idxYear )
139+
140+ # Store daily depths for all days at once
141+ depth_s [loopDay_arr , idxYear_arr ] = tmpDaily
142+
143+ # Vectorized 3-day sliding window for wet state computation.
144+ # Yesterday is previous calendar day (0 for very first day).
145+ # Tomorrow is next calendar day (0 for very last day).
146+ yesterday_vals = np .empty (nDaysKnown )
147+ yesterday_vals [0 ] = 0
148+ yesterday_vals [1 :] = tmpDaily [:- 1 ]
149+
150+ tomorrow_vals = np .empty (nDaysKnown )
151+ tomorrow_vals [- 1 ] = 0
152+ tomorrow_vals [:- 1 ] = tmpDaily [1 :]
153+
154+ # Good data: all three days non-negative (within floating point tolerance)
155+ good_mask = (
156+ (yesterday_vals >= - np .spacing (np .abs (yesterday_vals ))) &
157+ (tmpDaily >= - np .spacing (np .abs (tmpDaily ))) &
158+ (tomorrow_vals >= - np .spacing (np .abs (tomorrow_vals )))
159+ )
160+
161+ # Vectorized wet state classification
162+ y_wet = yesterday_vals > dryWetCutoff
163+ t_wet = tomorrow_vals > dryWetCutoff
164+
165+ wet_states = np .full (nDaysKnown , stateBad )
166+ wet_states [good_mask & y_wet & t_wet ] = stateWetWet
167+ wet_states [good_mask & y_wet & ~ t_wet ] = stateWetDry
168+ wet_states [good_mask & ~ y_wet & t_wet ] = stateDryWet
169+ wet_states [good_mask & ~ y_wet & ~ t_wet ] = stateDryDry
170+
171+ wetState_s [loopDay_arr , idxYear_arr ] = wet_states
172+
173+ idxYear += nYearsStation
174+
175+ return dailyDepth , dailyWetState
0 commit comments