文件
AcousticSO/pyat/DataBase.py
T

349 行
12 KiB
Python

import numpy as np
from scipy.interpolate import Akima1DInterpolator, interp1d
from netCDF4 import Dataset
from pyat.ProfileDataType import Profile
'''
************************************************************
该模块用于从数据库中, 获取剖面信息
************************************************************
'''
# 时间段标识
# def MunkFormula(z, v1, epsilon, z1):
# eta = 2*(z-z1)/z1
# return v1*(1+epsilon*(np.exp(-eta)+eta-1))
def soundSpeed(tem, sal, dep):
'''
声速计算公式
'''
# C = 1449.2 + 4.6*T - 0.055*T^2 + 0.00029*T^3 + (1.34 - 0.01*T)(S-35) + 0.017*D
# 式中:T为海⽔测点的温度,单位为℃;S为海⽔测点的盐度,单位为‰;D为海⽔测点的深度,单位为m。
vel = 1449.2 + 4.6*tem - 0.055*tem*tem + 0.00029 * \
tem*tem*tem + (1.34 - 0.01*tem) * \
(sal-35.0) + 0.017*dep
return vel
class DataBase():
"""
获取数据库中的剖面信息
"""
def __init__(self,ETOPO1path,STpath):
# 打开ETOPO1数据
fileETOPO1 = ETOPO1path+'/ETOPO1_Bed_g_gmt4.nc'
dataETOPO1 = Dataset(fileETOPO1)
# 获取ETOPO1网格
self.latGrid1 = dataETOPO1.variables['y'][:]
self.lonGrid1 = dataETOPO1.variables['x'][:]
# 获取海深数据
self.botGrid = -dataETOPO1.variables['z'][:]
self.STpath=STpath
return
def clearVar(self):
"""
清空变量
"""
self.botDep = None
self.salRawPf = None
self.temRawPf = None
self.velRawPf = None
self.velInterpPf = None
self.velExtPf = None
self.velFullPf = None
self.isValid = True
return
def set_timePeriod(self, timeIndex):
"""
设置时间段
"""
self.timeIndex = timeIndex
# 打开WOA18数据
#fileWOA18sal = '.\WOA18\woa18_A5B7_s' + timeIndex + '_04.nc'
#fileWOA18tem = '.\WOA18\woa18_A5B7_t' + timeIndex + '_04.nc'
fileST=self.STpath+'/CA'+timeIndex+'.nc'
dataST = Dataset(fileST)
# 获取WOA18网格
self.latGrid2 = dataST.variables['lat'][:]
self.lonGrid2 = dataST.variables['lon'][:]
# 获取深度数据
self.depthGrid = dataST.variables['zlev'][:]*(-1)
# 获取盐度数据
self.salGrid = np.squeeze(dataST.variables['SALT'][:])
# 获取温度数据
self.temGrid = np.squeeze(
dataST.variables['THETA'][:])
self.clearVar()
return
def set_coord(self, lat, lon):
"""
设置坐标
"""
self.lat = lat
self.lon = lon
self.clearVar()
return
def interpBilinear(self, latId, lonId, dataGrid, dataTpye):
"""
双线性插值
"""
latId0 = int(np.ceil(latId))
lonId0 = int(np.ceil(lonId))
a = latId - latId0
b = lonId - lonId0
if (dataTpye == 1):
data0 = dataGrid[latId0, lonId0]
data1 = dataGrid[latId0+1, lonId0]
data2 = dataGrid[latId0+1, lonId0+1]
data3 = dataGrid[latId0, lonId0+1]
else:
data0 = dataGrid[:, latId0, lonId0]
data1 = dataGrid[:, latId0+1, lonId0]
data2 = dataGrid[:, latId0+1, lonId0+1]
data3 = dataGrid[:, latId0, lonId0+1]
data01 = (1-a)*data0+a*data1
data32 = (1-a)*data3+a*data2
data = (1-b)*data01+b*data32
return data
def normCoord(self, latGrid, lonGrid):
"""
坐标单位化
"""
lat0 = latGrid[0]
lon0 = lonGrid[0]
dlat = latGrid[1]-latGrid[0]
dlon = lonGrid[1]-lonGrid[0]
latId = (self.lat - lat0)/dlat
lonId = (self.lon - lon0)/dlon
return (latId, lonId)
def get_bottomDepth(self):
"""
获得海底深度
"""
if (self.botDep == None):
# 单位化经纬度
(latId, lonId) = self.normCoord(self.latGrid1, self.lonGrid1)
# 获取网格点海深
self.botDep = self.interpBilinear(latId, lonId, self.botGrid, 1)
return self.botDep
def get_salRawProfile(self):
"""
获得原始盐度剖面
"""
if (self.salRawPf == None):
botDep = self.get_bottomDepth()
# 单位化经纬度
(latId, lonId) = self.normCoord(self.latGrid2, self.lonGrid2)
# 获取盐度剖面信息
sal = self.interpBilinear(latId, lonId, self.salGrid, 2)
# 获取有效数据段
dep = self.depthGrid[sal.mask == False]
sal = sal[sal.mask == False]
dep = dep.flatten()
sal = sal.flatten()
# 若为空,则为无效数据
if sal.size == 0:
self.isValid = False
# 剖面赋值
salRawPf = Profile()
salRawPf.set_data(dep, sal)
salRawPf.set_coord(self.lat, self.lon)
salRawPf.set_bottomDepth(botDep)
self.salRawPf = salRawPf
return self.salRawPf
def get_temRawProfile(self):
"""
获得原始温度剖面
"""
if (self.temRawPf == None):
botDep = self.get_bottomDepth()
# 单位化经纬度
(latId, lonId) = self.normCoord(self.latGrid2, self.lonGrid2)
# 获取温度剖面信息
tem = self.interpBilinear(latId, lonId, self.temGrid, 2)
# 获取有效数据段
dep = self.depthGrid[tem.mask == False]
tem = tem[tem.mask == False]
dep = dep.flatten()
tem = tem.flatten()
# 若为空,则为无效数据
if tem.size == 0:
self.isValid = False
# 剖面赋值
temRawPf = Profile()
temRawPf.set_data(dep, tem)
temRawPf.set_coord(self.lat, self.lon)
temRawPf.set_bottomDepth(botDep)
self.temRawPf = temRawPf
return self.temRawPf
def get_velRawProfile(self):
"""
获得原始声速剖面
"""
if (self.velRawPf == None):
botDep = self.get_bottomDepth()
# 获取温盐深数据
dep = self.get_salRawProfile().x
sal = self.get_salRawProfile().y
tem = self.get_temRawProfile().y
# 计算声速
if self.isValid == True:
vel = soundSpeed(tem, sal, dep)
else:
dep = np.array([0])
vel = np.array([0])
# 剖面赋值
velRawPf = Profile()
velRawPf.set_data(dep, vel)
velRawPf.set_coord(self.lat, self.lon)
velRawPf.set_bottomDepth(botDep)
self.velRawPf = velRawPf
return self.velRawPf
def get_velInterpProfile(self):
"""
获得插值后的声速剖面
将非等距离采样填充为深度间隔为1m的序列, 并应用Akima插值算法, 对数据进行插值
"""
if (self.velInterpPf == None):
botDep = self.get_bottomDepth()
if self.isValid == True:
# 获得原始声速剖面数据
dep = self.get_velRawProfile().x
vel = self.get_velRawProfile().y
# 对剖面进行垂向插值处理,将非等距离采样填充为深度间隔为1m的序列
depInterp = np.arange(0, dep[-1]+1, 1)
# 应用Akima插值算法,对数据进行插值
if dep.size == 1:
velInterp = vel
else:
func = Akima1DInterpolator(dep, vel)
velInterp = func(depInterp)
else:
depInterp = np.array([0])
velInterp = np.array([0])
# 剖面赋值
velInterpPf = Profile()
velInterpPf.set_data(depInterp, velInterp)
velInterpPf.set_coord(self.lat, self.lon)
velInterpPf.set_bottomDepth(botDep)
self.velInterpPf = velInterpPf
return self.velInterpPf
def get_velExtProfile(self):
"""
获得延拓后的声速剖面
"""
if (self.velExtPf == None):
botDep = self.get_bottomDepth()
if self.isValid == True:
# 获得原始声速剖面数据
dep = self.get_velRawProfile().x
vel = self.get_velRawProfile().y
# 获得剖面最低处温盐值
tem = self.get_temRawProfile().y[-1]
sal = self.get_salRawProfile().y[-1]
# 延拓部分深度序列
depExt = np.arange(np.ceil(dep[-1]), botDep+1)
# 按照声速公式延拓,假设温盐不变,仅深度变化
velExt = soundSpeed(tem, sal, depExt)
else:
depExt = np.array([0])
velExt = np.array([0])
# 剖面赋值
velExtPf = Profile()
velExtPf.set_data(depExt, velExt)
velExtPf.set_coord(self.lat, self.lon)
velExtPf.set_bottomDepth(botDep)
self.velExtPf = velExtPf
return self.velExtPf
def get_velFullProfile(self):
'''
获得全海深声速剖面
'''
if (self.velFullPf == None):
botDep = self.get_bottomDepth()
if self.isValid == True:
# 插值后的速度剖面
depInterp = self.get_velInterpProfile().x
velInterp = self.get_velInterpProfile().y
# 延拓后的速度剖面
depExt = self.get_velExtProfile().x
velExt = self.get_velExtProfile().y
# 全海深声速剖面
depFull = np.concatenate((depInterp, depExt))
velFull = np.concatenate((velInterp, velExt))
else:
depFull = np.array([0])
velFull = np.array([0])
# 剖面赋值
velFullPf = Profile()
velFullPf.set_data(depFull, velFull)
velFullPf.set_coord(self.lat, self.lon)
velFullPf.set_bottomDepth(botDep)
self.velFullPf = velFullPf
return self.velFullPf
def export_velFullProfile_txt(self, filePath):
'''
速度剖面导出txt文件
'''
velPf = self.get_velFullProfile()
lat = velPf.lat
lon = velPf.lon
botDep = velPf.botDep
dep = velPf.x
vel = velPf.y
# 导出文本
line1 = 'lat[degree north], {0:>7.4f}, lon[degree east], {1:>7.4f}, BottomDepth[m], {2:>5.0f}\n'.format(
lat, lon, botDep)
line2 = 'depth[m], velocity[m/s]'
np.savetxt(filePath, np.stack((dep, vel), -1),
fmt='%.5e', delimiter=',', comments='', header=line1+line2)
def export_velFullProfile_nc(self, filePath):
'''
速度剖面导出nc文件
'''
velPf = self.get_velFullProfile()
lat = velPf.lat
lon = velPf.lon
botDep = velPf.botDep
dep = velPf.x
vel = velPf.y
data = Dataset(filePath, 'w', format='NETCDF4')
data.createDimension('lon', 1)
data.createDimension('lat', 1)
data.createDimension('botDep', 1)
data.createDimension('dep', dep.size)
data.createDimension('vel', vel.size)
data.createVariable('lon', 'f', ('lon'))
data.createVariable('lat', 'f', ('lat'))
data.createVariable('botDep', 'f', ('botDep'))
data.createVariable('dep', 'f', ('dep'))
data.createVariable('vel', 'f', ('vel'))
data.variables['lat'][:] = lat
data.variables['lon'][:] = lon
data.variables['botDep'][:] = botDep
data.variables['dep'][:] = dep
data.variables['vel'][:] = vel
data.close()