forked from 3rdparty/wrf-python
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
6.7 KiB
230 lines
6.7 KiB
# 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
|
|
|