forked from NCAR/wrf-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwps.py
More file actions
230 lines (188 loc) · 6.74 KB
/
wps.py
File metadata and controls
230 lines (188 loc) · 6.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# Hastily made script to read WPS intermediate files
from __future__ import print_function
import struct
import numpy as np
# Number of bytes used at the start and end of a fortran record to
# indicate the record size
SIZE_BYTES = 4
class WPSData(object):
def __init__(self, ifv=None, hdate=None, xfcst=None, map_source=None,
field=None, units=None, desc=None, xlvl=None, nx=None,
ny=None, iproj=None, startloc=None, startlat=None,
startlon=None, deltalat=None, deltalon=None, nlats=None,
dx=None, dy=None, xlonc=None, truelat1=None, truelat2=None,
earth_radius=None, is_wind_earth_rel=None, slab=None):
self.ifv = ifv
self.hdate = hdate
self.xfcst = xfcst
self.map_source = map_source
self.field = field
self.units = units
self.desc = desc
self.xlvl = xlvl
self.nx = nx
self.ny = ny
self.iproj = iproj
self.startloc = startloc
self.startlat = startlat
self.startlon = startlon
self.deltalat = deltalat
self.deltalon = deltalon
self.nlats = nlats
self.dx = dx
self.dy = dy
self.xlonc = xlonc
self.truelat1 = truelat1
self.truelat2 = truelat2
self.earth_radius = earth_radius
self.is_wind_earth_rel = is_wind_earth_rel
self.slab = slab
def _parse_record1(data):
result = {}
result["ifv"] = struct.unpack(">i", data)
return result
def _parse_record2(data):
result = {}
parsed = struct.unpack(">24sf32s9s25s46sfiii", data)
result["hdate"] = parsed[0]
result["xfcst"] = parsed[1]
result["map_source"] = parsed[2]
result["field"] = parsed[3]
result["units"] = parsed[4]
result["desc"] = parsed[5]
result["xlvl"] = parsed[6]
result["nx"] = parsed[7]
result["ny"] = parsed[8]
result["iproj"] = parsed[9]
return result
def _parse_record3(data, iproj):
result = {}
if iproj == 0:
fmt = ">8sfffff"
parsed = struct.unpack(fmt, data)
result["startloc"] = parsed[0]
result["startlat"] = parsed[1]
result["startlon"] = parsed[2]
result["deltalat"] = parsed[3]
result["deltalon"] = parsed[4]
result["earth_radius"] = parsed[5]
elif iproj == 1:
fmt = ">8sffffff"
parsed = struct.unpack(fmt, data)
result["startloc"] = parsed[0]
result["startlat"] = parsed[1]
result["startlon"] = parsed[2]
result["dx"] = parsed[3]
result["dy"] = parsed[4]
result["truelat1"] = parsed[5]
result["earth_radius"] = parsed[6]
elif iproj == 3:
fmt = ">8sffffffff"
parsed = struct.unpack(fmt, data)
result["startloc"] = parsed[0]
result["startlat"] = parsed[1]
result["startlon"] = parsed[2]
result["dx"] = parsed[3]
result["dy"] = parsed[4]
result["xlonc"] = parsed[5]
result["truelat1"] = parsed[6]
result["truelat2"] = parsed[7]
result["earth_radius"] = parsed[8]
elif iproj == 4:
fmt = ">8sfffff"
parsed = struct.unpack(fmt, data)
result["startloc"] = parsed[0]
result["startlat"] = parsed[1]
result["startlon"] = parsed[2]
result["nlats"] = parsed[3]
result["deltalon"] = parsed[4]
result["earth_radius"] = parsed[5]
elif iproj == 5:
fmt = ">8sfffffff"
parsed = struct.unpack(fmt, data)
result["startloc"] = parsed[0]
result["startlat"] = parsed[1]
result["startlon"] = parsed[2]
result["dx"] = parsed[3]
result["dy"] = parsed[4]
result["xlonc"] = parsed[5]
result["truelat1"] = parsed[6]
result["earth_radius"] = parsed[7]
return result
def _parse_record4(data):
result = {}
result["is_wind_earth_rel"] = struct.unpack(">i", data)
return result
def _parse_record5(data, nx, ny):
result = {}
size = nx * ny
fmt = ">{}f".format(size)
parsed = struct.unpack(fmt, data)
arr = np.array(parsed, dtype=np.float32)
result["slab"] = arr.reshape((nx, ny), order="F")
return result
_PARSE_MAP = {0: _parse_record1,
1: _parse_record2,
2: _parse_record3,
3: _parse_record4,
4: _parse_record5}
def parse_record(record_idx, data, iproj=None, nx=None, ny=None):
if record_idx == 0 or record_idx == 1 or record_idx == 3:
return _PARSE_MAP[record_idx](data)
elif record_idx == 2:
return _PARSE_MAP[record_idx](data, iproj)
elif record_idx == 4:
return _PARSE_MAP[record_idx](data, nx, ny)
def read_wps(wps_file, field=None):
with open(wps_file, "rb") as f:
wps_params = {}
record_list = []
raw_data = f.read()
record_size_idx = 0
end_of_file_idx = len(raw_data) - 1
while True:
iproj = None
nx = None
ny = None
keep_record = True
for record_idx in range(5):
# Each record has the size (in SIZE_BYTES bytes) at the
# start and end of each record. This might be compiler
# dependent though, so this might need to be modified.
# Also, the WPS files are stored big endian.
record_size = struct.unpack(
">i",
raw_data[record_size_idx: record_size_idx + SIZE_BYTES])
record_start = record_size_idx + SIZE_BYTES
record_end = record_start + record_size[0]
record_data = raw_data[record_start:record_end]
parsed_record = parse_record(record_idx, record_data, iproj,
nx, ny)
try:
field_name = parsed_record["field"].strip()
except KeyError:
pass
else:
if field is not None:
if field_name != field:
keep_record = False
try:
iproj = parsed_record["iproj"]
except KeyError:
pass
try:
nx = parsed_record["nx"]
except KeyError:
pass
try:
ny = parsed_record["ny"]
except KeyError:
pass
wps_params.update(parsed_record)
record_size_idx = record_end + SIZE_BYTES
if keep_record:
record_list.append(WPSData(**wps_params))
# Repeat for all record slabs
if record_end + SIZE_BYTES > end_of_file_idx:
break
return record_list