文件

1508 行
57 KiB
Python
可执行文件

import numpy as np
from scipy.io import loadmat
import os
from struct import unpack
from env import Source, Dom, Pos, cInt, Ice, SSPraw, SSP, HS, BotBndry, TopBndry, Bndry, Box, Beam, Modes, Eigenray
import re
from matplotlib import pyplot as plt
from math import floor
"""
translation of basic read functions from Mike Porter's
Matlab ReadWrite folder
Currently lacking bellhop support (just need to translate read_bell)
and bellhop3d (need to translate readsxrx)
"""
def equally_spaced( x ):
# routine to see if the vector x is composed of roughly equally spaced
# values
n = len( x )
xtemp = np.linspace( x[ 0 ], x[ -1 ], n )
delta = abs( x[:] - xtemp[:] ) # the colon converts both to column vectors
if ( np.max( delta ) < 1e-9 ):
return 1
else:
return 0
# Write a field-parameters file
def write_fieldflp(flpfil, Option, Pos, **kwargs):
if flpfil[-4:] != '.flp':
flpfil = flpfil + '.flp'
if 'scooter' in kwargs.keys():
with open(flpfil, 'w') as f:
f.write('\'' + '{:4}'.format(Option) + ' \'' + ' ! Option \r\n')
f.write('{:5d}'.format(len(Pos.r.range)) + ' \t \t \t \t ! NRR')
if ( len( Pos.r.range ) > 2) and equally_spaced( Pos.r.range ):
f.write('\r\n ' + '{:6f}'.format(Pos.r.range[0]))
f.write('\r\n ' + '{:6f}'.format(Pos.r.range[-1]))
elif len(Pos.r.range) == 1:
f.write('\r\n ' + '{:6f}'.format(Pos.r.range[0]) + ' ')
else:
for i in range(len(Pos.r.range)):
f.write('\r\n ' + '{:6f}'.format(Pos.r.range[i]) + ' ')
f.write( '/ \t ! RR(1) ... (km) \r\n' )
return
with open(flpfil, 'w') as f:
f.write('/ ! Title \r\n' )
f.write('\'' + '{:4}'.format(Option) + ' \'' + ' ! Option \r\n')
f.write('999999 ! Mlimit (number of modes to include) \r\n' )
if 'NProf' in kwargs.keys():
Nprof = kwargs['NProf']
f.write(str(Nprof)+' ! NProf' )
rProf = kwargs['rProf']
if ( len( rProf ) > 2) and equally_spaced( rProf ):
f.write('\r\n ' + '{:6f}'.format(rProf[0]))
f.write('\r\n ' + '{:6f}'.format(rProf[-1]))
elif len(rProf) == 1:
f.write('\r\n ' + '{:6f}'.format(rProf[0]) + ' ')
else:
for i in range(len(rProf)):
f.write('\r\n ' + '{:6f}'.format(rProf[i]) + ' ')
f.write(' / ! rProf (km) \r\n')
else:
f.write('1 ! NProf \r\n' )
f.write('0.0 / ! rProf (km) \r\n' )
# receiver ranges
f.write('{:5d}'.format(len(Pos.r.range)) + ' \t \t \t \t ! NRR')
if ( len( Pos.r.range ) > 2) and equally_spaced( Pos.r.range ):
f.write('\r\n ' + '{:6f}'.format(Pos.r.range[0]))
f.write('\r\n ' + '{:6f}'.format(Pos.r.range[-1]))
elif len(Pos.r.range) == 1:
f.write('\r\n ' + '{:6f}'.format(Pos.r.range[0]) + ' ')
else:
for i in range(len(Pos.r.range)):
f.write('\r\n ' + '{:6f}'.format(Pos.r.range[i]) + ' ')
f.write( ' / \t ! R(1) ... (km) \r\n' )
# source depths
f.write('{:5d}'.format(len(Pos.s.depth))+'\t \t \t \t ! NSD')
if ( len( Pos.s.depth ) > 2 and equally_spaced( Pos.s.depth ) ):
f.write('\r\n '+'{:6f}'.format(Pos.s.depth[ 0 ])+' ')
f.write('\r\n '+'{:6f}'.format(Pos.s.depth[ -1 ])+' ')
elif len(Pos.s.depth) == 1:
f.write( '\r\n '+ '{:6f}'.format(Pos.s.depth[0])+' ')
else:
for i in range(len(Pos.s.depth)):
f.write( '\r\n '+ '{:6f}'.format(Pos.s.depth[i])+' ')
f.write( '/ \t ! SD(1) ... (m) \r\n' )
# receiver depths
f.write('{:5}'.format(len(Pos.r.depth)) + ' \t \t \t \t ! NRD')
if ( len( Pos.r.depth ) > 2 and equally_spaced( Pos.r.depth ) ):
f.write( '\r\n '+'{:6f}'.format(Pos.r.depth[0])+' ')
f.write( '\r\n '+'{:6f}'.format(Pos.r.depth[-1])+' ')
elif (len(Pos.r.depth) == 1):
f.write( '\r\n '+'{:6f}'.format(Pos.r.depth[0])+' ')
else:
for i in range(len(Pos.r.depth)):
f.write( '\r\n '+'{:6f}'.format(Pos.r.depth[i]))
f.write( '/ \t ! RD(1) ... (m) \r\n' )
# receiver range offsets
f.write( '{:5}'.format(len( Pos.r.depth ))+' \t \t ! NRR', )
if not hasattr(Pos.r, 'offsets'):
f.write( '\r\n '+'{:6.2f}'.format(0.0) +' ') # just zeros
else:
if ( len( Pos.r.offsets) > 2 and equally_spaced( Pos.r.offsets ) ):
f.write( '\r\n '+'{:6f}'.format(Pos.r.offsets[1])+' ')
f.write( '\r\n '+'{:6f}'.format(Pos.r.offsets[-1])+' ')
elif (len(Pos.r.offsets) == 1):
f.write( '\r\n '+'{:6f}'.format(Pos.r.offsets[0])+' ')
else:
for i in range(len(Pos.r.offsets)):
f.write( '\r\n '+'{:6f}'.format(Pos.r.offsets[i]))
f.write( '/ \t \t ! RR(1) ... (m) \r\n' )
return
def fileparts(fname):
fpath = os.path.dirname(os.path.abspath(fname))
if '.' not in fname:
ext = None
else:
ext_ind = [i for i in range(len(fname)) if fname[i]=='.']
if len(ext_ind) > 1:
print('Warning: lots of periods...')
#raise ValueError("something fishy about a filename with two periods")
ext_ind = ext_ind[-1]
ext = fname[ext_ind:]
#print('ext', ext)
fname = fname[:ext_ind]
return fpath, fname, ext
class Empty():
def __init__(self):
return
def write_sbp(fname, pattern,num_dec_pts=2):
"""
From BELLHOP docs,
``The source beam pattern file has the format
NSBPPts
angle1 power1
....
with angle following the BELLHOP convention, i.e. declination angle in degrees (So that 90 degrees points to the bottom. The power is in dB.
''
Input-
fname - string
basic file name to write.
ends with .sbp, if not it's appended
pattern - np 2d array of floats
first column is angles IN DEGREES
second column is power IN DB
"""
if fname[-4:] != '.sbp':
fname += '.sbp'
num_angles = pattern.shape[0]
np.savetxt(fname, pattern, fmt='%.'+str(num_dec_pts) + 'f', delimiter=' ', header=str(num_angles) + ' # Num pts',comments='')
return
def write_env( envfil, model, TitleEnv, freq, ssp, bdry, pos, beam, cint, RMax, NMESH=None,*varargin ):
'''
write_env - writes an environment file given the environment parameters
Parameters
----------
envfil : str
name of the environment file to save, should not include extensions
model : str
type of model to run. example ['kraken']
TitleEnv : str
title of environment file (printed at top of .env file)
freq : float
frequency of environment
ssp : pyat.pyat.env.SSP
sound speed profile object
bdry : pyat.pyat.env.Bndry
boundary condition (top and bottom) object
pos : pyat.pyat.env.Pos
Position object? contains information about source and reciever depths and ranges
beam : (unknown)
unknown, but passing [] works
cint : an empty class?
unkown, but passing Empty where Empty is defined by (below) works
class Empty:
def __init__(self):
return
RMax : int
maximum range
NMESH : int
number of mesh points used for sound speed profile interpolation. Default is None, which
which uses the number of defined points in the sound speed profile object
'''
if (envfil[-4:] != '.env' ):
envfil = envfil + '.env' # append extension
if ( len( varargin ) == 0 ):
f = open( envfil, 'w' ) # create new env file
else:
f = open( envfil, 'a' ) # append to existing envfil
model = model.upper() # convert to uppercase
f.write('\'' + TitleEnv + '\' ! Title \r\n')
f.write('{:8.2f}'.format(freq) +' \t \t \t ! Frequency (Hz) \r\n')
f.write('{:5d}'.format(ssp.NMedia)+ ' \t \t \t ! NMedia \r\n')
f.write('\'' + bdry.Top.Opt + '\''+ ' \t \t \t ! Top Option \r\n')
if ( bdry.Top.Opt[0] == 'A' ): # analytic boundary
f.write(' {:6.2f}'.format(ssp.depth[0]) + \
' {:6.2f}'.format(bdry.Top.hs.alphaR) + \
' {:6.2f}'.format(bdry.Top.hs.betaR) + \
' {:6.2g}'.format(bdry.Top.hs.rho) + \
' {:6.2f}'.format(bdry.Top.hs.alphaI) + \
' {:6.2f}'.format(bdry.Top.hs.betaI) + \
' \t ! upper halfspace \r\n')
# SSP
for medium in range(ssp.NMedia):
# get NMESH
if NMESH is None:
NMESH = ssp.N[ medium ]
f.write('{:5d}'.format(NMESH) + \
' {:4.2f}'.format(ssp.sigma[ medium ]) + \
' {:6.2f}'.format(ssp.depth[ medium+1 ]) + ' \t ! N sigma depth \r\n')
for ii in range(len( ssp.raw[ medium].z )):
f.write('\t {:6.2f} '.format(ssp.raw[ medium ].z[ ii ]) + \
'{:6.2f} '.format(ssp.raw[ medium ].alphaR[ ii ]) + \
'{:6.2f} '.format(ssp.raw[ medium ].betaR[ ii ]) + \
'{:6.2g}'.format(ssp.raw[ medium ].rho[ ii ] ) + \
' {:10.6f} '.format(ssp.raw[ medium ].alphaI[ ii ]) + \
'{:6.2f} '.format(ssp.raw[ medium ].betaI[ ii ]) + \
'/ \t ! z c cs rho \r\n')
# lower halfspace
f.write('\''+bdry.Bot.Opt + '\'' + ' {:6.2f}'.format(ssp.sigma[1]) + ' \t \t ! Bottom Option, sigma\r\n') # ssp.sigma( 2 ) )
fmtr = {'all': lambda x: ' {:6.2f}'.format(float(x))}#, 'void': lambda x: ''}
a = np.array([])
d = np.array([float(ssp.depth[ssp.NMedia])])
vls = [d, bdry.Bot.hs.alphaR, bdry.Bot.hs.betaR, bdry.Bot.hs.rho, \
bdry.Bot.hs.alphaI, bdry.Bot.hs.betaI]
strings = [np.array2string(x, formatter = fmtr) for x in vls]
s = ' {:6.2f} '.format(ssp.depth[ssp.NMedia])
for string in strings[1:]:
if string == '':
string = ' 0.00'
s += string
if ( bdry.Bot.Opt[0] == 'A' ):
# print('{:6.2f}'.format(bdry.Bot.hs.betaI[0]))
f.write(' ' + s + ' \t / \t ! lower halfspace \r\n')
if model in [ 'SCOOTER', 'KRAKEN', 'KRAKENC', 'SPARC' ]:
f.write('{:6.0f} '.format(cint.Low) + '{:6.0f} \t \t ! cLow cHigh (m/s) \r\n'.format(cint.High)) # phase speed limits
f.write('{:8.2f} \t \t \t ! RMax (km) \r\n'.format(RMax )) # maximum range
# source depths
f.write('{:5d} \t \t \t \t ! NSD'.format(len( pos.s.depth ) ))
if ( len( pos.s.depth ) >= 2) and equally_spaced( pos.s.depth ):
f.write('\r\n {:6f} '.format(pos.s.depth[0]) +' {:6f} '.format(pos.s.depth[-1]))
else:
f.write('\r\n ')#{:6f} '.format( pos.s.depth[0] ))
for tmp_depth in pos.s.depth:
f.write('{:6f} '.format(tmp_depth) )
f.write('/ \t ! SD(1) ... (m) \r\n' )
# receiver depths
f.write('{:5d} \t \t \t \t ! NRD'.format(len( pos.r.depth ) ))
if ( len( pos.r.depth ) >= 2) and equally_spaced( pos.r.depth ) :
f.write('\r\n {:6f} '.format(pos.r.depth[0]) + '{:6f} '.format( pos.r.depth[-1]))
else:
f.write('\r\n ')
for tmp_depth in pos.r.depth:
f.write('{:6f} '.format(tmp_depth) )
f.write('/ \t ! RD(1) ... (m) \r\n' )
# receiver ranges
if (model == 'BELLHOP' ) or (model == 'FirePE' ) :
# receiver ranges
f.write('{:5d} \t \t \t \t ! NRR'.format(len( pos.r.range ) ))
if ( len( pos.r.range ) >= 2) and equally_spaced( pos.r.range ) :
f.write('\r\n {:6f} '.format(pos.r.range[0]) + '{:6f} '.format(pos.r.range[-1]))
else:
f.write('\r\n {:6f} '.format(pos.r.range[0] ))
f.write('/ \t ! RR(1) ... (km) \r\n' )
write_bell(f, beam )
f.close()
def write_ssp(sspfile, cw, r_arr):
"""
Input
sspfile - string
path of file to write
cw - numpy 2d array
ssp profiles, columns are the depth profiles for given range
r_arr - numpy 1d array
ranges (in km) for updating ssp
"""
if sspfile[-3:] != 'ssp':
sspfile += '.ssp'
with open(sspfile, 'w') as f:
f.write(str(r_arr.size)+'\r\n')
for val in r_arr:
f.write(str(val) + '\t')
f.write('\r\n')
for row in range(cw.shape[0]):
for val in cw[row,:]:
f.write(str(val) + '\t')
f.write('\r\n')
return
def write_bathy(btyfile, bathm):
"""
Input
btyfile - string
path of file to write
range_depth_array - numpy 2d array
first column is ranges (in km), second column is depths
"""
if btyfile[-3:] != 'bty':
btyfile += '.bty'
with open(btyfile, 'w') as f:
f.write('CS\r\n')
f.write(str(len(bathm.d)) + '\r\n')
for row in range(len(bathm.r)):
f.write(' ' + str(bathm.r[row]) + ' ' + str(bathm.d[row]) + '\n')
return
def write_bell(f, beam):
f.write('\''+beam.RunType+'\''+ ' \t \t \t \t ! Run Type \r\n');
if (beam.Ibeam != None):
f.write('{:<i}'.format(beam.Nbeams)+'{:<i}'.format(beam.Ibeam) +' \t \t \t \t ! Nbeams Ibeam \r\n');
else:
# if this is a ray trace run and the field beam.Nrays exists to use
# fewer rays in the trace, then use that
if ( (beam).RunType[0 ] == 'R') and (beam.Nrays != None):
f.write('{:d}'.format(beam.Nrays) +' \t \t \t \t \t ! Nbeams \r\n');
else:
f.write('{:d}'.format(beam.Nbeams )+'\t \t \t \t \t ! Nbeams \r\n')
f.write('{:f}'.format( beam.alpha[ 0 ])+' {:f} '.format(beam.alpha[ -1 ])+'/ \t \t ! angles (degrees) \r\n');
f.write('{:f}'.format(beam.deltas)+' {:f}'.format(beam.box.z)+' {:f}'.format(beam.box.r)+'\t ! deltas (m) box.z (m) box.r (km) \r\n');
# Cerveny-style Gaussian beams
if ( len( beam.RunType ) > 1) and ((beam.RunType[1] != 'G') and (beam.RunType[1] != 'B') and (beam.RunType[1] != 'S')) :
f.write('\''+beam.Type[0:1]+'\''+' {:f}'.format(beam.epmult)+' {:f}'.format(beam.rLoop )+' \t \t ! ''Min/Fill/Cer, Sin/Doub/Zero'' Epsmult RLoop (km) \r\n')
f.write('{:d}'.format(beam.Nimage)+' {:d}'.format(beam.Ibwin)+' \t \t \t \t ! Nimage Ibwin \r\n')
def read_shd_bin(*varargin):
s = Source(0)
r = Dom(0,0)
pos = Pos(s,r)
'''
Read TL surfaces from a binary Bellhop/Kraken .SHD file
without having to convert to ASCII first.
Useage:
... = read_shd_bin( filename, xs, ys )
where (xs, ys) is the source coordinate in km
(xs, ys) are optional
Output is a 4-D pressure field p( Ntheta, Nsd, Nrd, Nrr )
'''
if (len(varargin) < 1) or (len(varargin) > 3):
raise ValueError("Can only pass one to three arguments: filename; (filename, xs, ys); or (filename, freq)")
filename = varargin[0]
# optional frequency
if len(varargin) == 2:
freq = varargin[1]
else:
freq = np.NaN
# optional source (x,y) coordinate
if len(varargin) >= 3:
xs = varargin[1]
ys = varargin[2]
else:
xs = np.NaN
ys = np.NaN
##
f = open( filename, 'rb' )
recl = unpack('<I', f.read(4))[0]; #record length in bytes will be 4*recl
title = unpack('80s', f.read(80))
f.seek(4 * recl); #reposition to end of first record
PlotType = unpack('10s', f.read(10))
f.seek(2 * 4 * recl); #reposition to end of second record
Nfreq = unpack('<I', f.read(4))[0]
Ntheta = unpack('<I', f.read(4))[0]
Nsx = unpack('<I', f.read(4))[0]
Nsy = unpack('<I', f.read(4))[0]
Nsd = unpack('<I', f.read(4))[0]
Nrd = unpack('<I', f.read(4))[0]
Nrr = unpack('<I', f.read(4))[0]
atten = unpack('<I', f.read(4))[0]
f.seek(3 * 4 * recl); #reposition to end of record 3
freqVec = unpack(str(Nfreq) +'d', f.read(Nfreq*8))
f.seek(4 * 4 * recl) ; #reposition to end of record 4
pos.theta = unpack(str(Ntheta) +'f', f.read(4*Ntheta))[0]
if ( PlotType[ 1 : 2 ] != 'TL' ):
f.seek(5 * 4 * recl); #reposition to end of record 5
pos.s.x = unpack(str(Nsx)+'f', f.read(Nsx*4))
f.seek( 6 * 4 * recl); #reposition to end of record 6
pos.s.y = unpack(str(Nsy) + 'f', f.read(Nsy*4))
else: # compressed format for TL from FIELD3D
f.seek(5 * 4 * recl, -1 ); #reposition to end of record 5
pos.s.x = f.read(2, 'float32' )
pos.s.x = np.linspace( pos.s.x[0], pos.s.x[-1], Nsx )
f.seek(6 * 4 * recl, -1 ); #reposition to end of record 6
pos.s.y = f.read(2, 'float32' )
pos.s.y = np.linspace( pos.s.y[0], pos.s.y[-1], Nsy )
f.seek(7 * 4 * recl); #reposition to end of record 7
pos.s.depth = unpack(str(Nsd)+'f', f.read(Nsd*4))
pos.s.depth = np.array(pos.s.depth)
f.seek(8 * 4 * recl); #reposition to end of record 8
pos.r.depth = unpack(str(Nrd) + 'f', f.read(Nrd*4))
pos.r.depth = np.array(pos.r.depth)
f.seek(9 * 4 * recl); #reposition to end of record 9
pos.r.range = unpack(str(Nrr) + 'f',f.read(Nrr*4))
# pos.r.range = pos.r.range'; # make it a row vector
pos.r.range = np.array(pos.r.range)
pos.r.range = np.round(pos.r.range, 3)
##
# Each record holds data from one source depth/receiver depth pair
if PlotType == 'rectilin ':
pressure = np.zeros(( Ntheta, Nsd, Nrd, Nrr ), dtype=np.complex128)
Nrcvrs_per_range = Nrd
if PlotType == 'irregular ':
pressure = np.zeros(( Ntheta, Nsd, 1, Nrr ), dtype=np.complex128)
Nrcvrs_per_range = 1
else:
pressure = np.zeros(( Ntheta, Nsd, Nrd, Nrr ), dtype=np.complex128)
Nrcvrs_per_range = Nrd
##
if np.isnan( xs ): # Just read the first xs, ys, but all theta, sd, and rd
# get the index of the frequency if one was selected
ifreq = 0
if not np.isnan(freq):
freqdiff = [abs( x - freq ) for x in freqVec]
ifreq = min( freqdiff )
for itheta in range (Ntheta):
for isd in range(Nsd):
# disp( [ 'Reading data for source at depth ' num2str( isd ) ' of ' num2str( Nsd ) ] )
for ird in range( Nrcvrs_per_range):
recnum = 10 + ( ifreq ) * Ntheta * Nsd * Nrcvrs_per_range + \
( itheta ) * Nsd * Nrcvrs_per_range + \
( isd ) * Nrcvrs_per_range + \
ird
status = f.seek(int(recnum) * 4 * recl); #Move to end of previous record
if ( status == -1 ):
raise ValueError( 'Seek to specified record failed in read_shd_bin' )
temp = unpack(str(2*Nrr)+'f', f.read(2 * Nrr*4)); #Read complex data
pressure[ itheta, isd, ird, : ] = temp[ 0 : 2 * Nrr -1 : 2 ] + complex(0,1) *np.array((temp[ 1 : 2 * Nrr :2]))
# Transmission loss matrix indexed by theta x sd x rd x rr
else: # read for a source at the desired x, y, z.
xdiff = abs( pos.s.x - xs * 1000. )
[ holder, idxX ] = min( xdiff )
ydiff = abs( pos.s.y - ys * 1000. )
[ holder, idxY ] = min( ydiff )
# show the source x, y that was found to be closest
# [ pos.s.x( idxX ) pos.s.y( idxY ) ]
for itheta in range(Ntheta):
for isd in range(Nsd):
# disp( [ 'Reading data for source at depth ' num2str( isd ) ' of ' num2str( Nsd ) ] )
for ird in range(Nrcvrs_per_range):
recnum = 10 + ( idxX - 1 ) * Nsy * Ntheta * Nsd * Nrcvrs_per_range + \
( idxY - 1 ) * Ntheta * Nsd * Nrcvrs_per_range + \
( itheta - 1 ) * Nsd * Nrcvrs_per_range + \
( isd - 1 ) * Nrcvrs_per_range + ird - 1
status = f.seek(recnum * 4 * recl); # Move to end of previous record
if ( status == -1 ):
raise ValueError( 'Seek to specified record failed in read_shd_bin' )
temp = f.read(2 * Nrr, 'float32' ); #Read complex data
pressure[ itheta, isd, ird, : ] = temp[ 1 : 2 : 2 * Nrr ] + complex(0,1) * np.array(temp[ 2 : 2 : 2 * Nrr ])
# Transmission loss matrix indexed by theta x sd x rd x rr
f.close()
return [ title, PlotType, freqVec, atten, pos, pressure ]
def read_ram_bin(fname):
"""
Red the .out file from rammpl
function [p,f,z,r] = readram(ram_output_file)
[p,f,z,r] = readram(ram_output_file)
MATLAB function that reads in RAM's binary output file and saves
the pressure field in MATLAB binary format. NOTE: This function
is only valid when the output flags on line 7 of the RAM input file
read "1 1 0 0 0"; it is **not** robust to other output flags.
p = matrix of complex pressures
f = source frequency (in Hz)
z = vector of depths in RAM grid (in m)
r = vector of ranges in RAM grid (in m)
K. H. Kim
1 February 2005
"""
with open (fname, 'rb') as f:
nz = unpack('I', f.read(4))[0]
nr = unpack('I', f.read(4))[0]
print('nr, nz', nr, nz)
freq = unpack('d', f.read(8))[0]
recl = 8 * 2 * nz
f.seek(recl-4-4-8,1)
dz = unpack('d', f.read(8))[0]
zmax = unpack('d', f.read(8))[0]
dr = unpack('d', f.read(8))[0]
rmax = unpack('d', f.read(8))[0]
print('dr, dz, rmax, zmax', dr, dz, rmax, zmax)
# Skip to first record of interest
f.seek(recl-8-8-8-8,1)
nvals = int(nz*nr)
p = unpack(str(2*nvals) + 'd', f.read())
p = np.array(p)
cp = np.zeros((nvals),dtype=np.complex128)
cp += p[::2]
cp += complex(0,1)*p[1::2]
cp = cp.reshape(nr, nz)
cp = cp.T
z = np.arange(dz, zmax+dz, dz)
r = np.arange(dr, rmax+dr, dr)
return cp, f, z,r
def read_shd (*varargin ):
'''
Read the shade file
[ PlotTitle, PlotType, freqVec, atten, Pos, pressure ] return vals
calls the appropriate routine (binary, ascii, or mat file) to read in the pressure field
usage: [ PlotTitle, PlotType, freqVec, atten, Pos, pressure ] = read_shd( filename )
Reads first source.
[ PlotTitle, PlotType, freqVec, atten, Pos, pressure ] = read_shd( filename, xs, ys )
Reads source at the specified xs, ys coordinate.
[ PlotTitle, PlotType, freqVec, atten, Pos, pressure ] = read_shd( filename, freq )
Reads source at the specified frequency.
Recommended to include a file extension, if it exists.
Otherwise it may find a different file than you intended.
Output is a 5-D pressure field p( Nfreq, Ntheta, Nsd, Nrd, Nrr )
If omitted, take a guess at the extension
Matlab 'exist' command is simpler; however, it searches the whole Matlab search path.
'''
# Determine type of file:
#error( nargchk( 1, 3, len(varargin), 'struct' ) )
if (len(varargin) < 1) or (len(varargin) > 3):
raise ValueError("Can only pass one to three arguments: filename; (filename, xs, ys); or (filename, freq)")
filename = varargin[0]
# optional frequency
if len(varargin) == 2:
freq = varargin[1]
else:
freq = np.NaN
# optional source (x,y) coordinate
if len(varargin) >= 3:
xs = varargin[1]
ys = varargin[2]
else:
xs = np.NaN
ys = np.NaN
PlotType = []; # in case this was not set
[holder , name, ext ] = fileparts( filename )
if ( ext == '.mat' ) :
[ holder, holder1, ext2 ] = fileparts( name )
if ext2 == '.shd':
FileType = 'shdmat'
elif ext2 == '.grn':
FileType = 'grnmat'
else:
pass
else:
if filename == 'ASCFIL':
FileType = 'asc'
elif filename == 'SHDFIL':
FileType = 'shd'
elif filename == 'tl.grid':
FileType = 'RAM'
else:
endchar = len( filename )
if ( endchar >= 4 ):
FileType = filename[ endchar - 3 : endchar ].lower()
##
PlotTitle, PlotType, freqVec, atten, pos, pressure = [], [], [], [], [], []
if FileType in ['shd', 'grn' ]: # binary format
if len(varargin) == 1:
[ PlotTitle, PlotType, freqVec, atten, pos, pressure ] = read_shd_bin( filename )
if len(varargin) == 2:
[ PlotTitle, PlotType, freqVec, atten, pos, pressure ] = read_shd_bin( filename, freq )
if len(varargin) == 3:
[ PlotTitle, PlotType, freqVec, atten, pos, pressure ] = read_shd_bin( filename, xs, ys )
elif FileType == 'shdmat': # Shade function mat file
loadmat( filename )
# has a specific source xs, ys been given?
if not np.isnan( xs ):
xdiff = abs( pos.s.x - xs * 1000. )
[ holder, idxX ] = min( xdiff )
ydiff = abs( pos.s.y - ys * 1000. )
[ holder, idxY ] = min( ydiff )
# extract the appropriate source index
pressureT = pressure[ idxX, idxY, :, :, :, : ]
pressure = np.reshape( pressureT, [ pos.Nsd, pos.Nrd, pos.Ntheta, pos.Nrr ] )
# has a specific frequency been given?
if not np.isnan( freq ):
freqdiff = abs( freqVec - freq )
[ holder, ifreq ] = min( freqdiff )
# extract the appropriate source index
pressure = pressure[ ifreq, 1, :, : ]
elif FileType == 'asc': # ascii format
[ PlotTitle, PlotType, freqVec, atten, pos, pressure ] = read_shd_asc( filename )
elif FileType == 'grnmat': # Green's function mat file
loadmat( filename )
pos.r.range = np.array(pos.r.range.T); # make it a column vector to match read_shd_bin
elif FileType == 'RAM':
[ PlotTitle, PlotType, freqVec, atten, pos, pressure ] = read_ram_tlgrid( filename )
else:
raise ValueError( 'Unrecognized file extension' )
# clean up PlotTitle by taking only the part up inside the quotes
# nchars = strfind( PlotTitle, '''' ); # find quotes
# PlotTitle = [ PlotTitle( nchars( 1 ) + 1 : nchars( 2 ) - 1 ) ]
return [ PlotTitle, PlotType, freqVec, atten, pos, pressure ]
def crci( c, alpha, freq, AttenUnit ):
'''
Convert real wave speed and attenuation to a single complex wave speed
5 cases:
N for Nepers/meter
M for dB/meter (M for Meters)
F for dB/m-kHZ (F for frequency dependent)
W for dB/wavelength (W for Wavelength)
Q for Q
T for Thorpe
'''
omega = 2.0 * np.pi * freq
# *** Convert to Nepers/m ***
alphaT = 0.0
if AttenUnit[0] == 'N' : # Nepers/m
alphaT = alpha
elif AttenUnit[0] == 'M' : # dB/meter
alphaT = alpha / 8.6858896
elif AttenUnit[0] == 'F' : # dB/m-kHZ
alphaT = alpha * freq / 8685.8896
elif AttenUnit[0] == 'W' : # dB/wavelength
if ( c != 0.0 ):
alphaT = alpha * freq / ( 8.6858896 * c )
elif AttenUnit[0] == 'Q' :
if( c * alpha != 0.0 ):
alphaT = omega / ( 2.0 * c * alpha )
else:
print('Unknown attenuation unit')
# added volume attenuation
if AttenUnit[1] == ( 'T' ) : # Thorp
f2 = np.square(( freq / 1000.0 ))
# Original Thorp (1967) formula
# alphaT = 40.0 * f2 / ( 4100.0 + f2 ) + 0.1 * f2 / ( 1.0 + f2 );
# alphaT = alphaT / 914.4; # dB / m
# alphaT = alphaT / 8.6858896; # Nepers / m
# Updated formula from JKPS Eq. 1.34
Thorp = 3.3e-3 + 0.11 * f2 / ( 1.0 + f2 ) + 44.0 * f2 / ( 4100.0 + f2 ) + 3e-4 * f2 # dB/km
Thorp = Thorp / 8685.8896 # Nepers / m
alphaT = alphaT + Thorp
# *** Convert Nepers/m to equivalent imaginary sound speed ***
alphaT = alphaT * c*c / omega
crci = complex(c, alphaT)
return crci
def topbot( lines, line_ind, freq, BCType, AttenUnit ):
''' Handles top and bottom boundary conditions
Input:
ENVFIL: Environmental file
freq: frequency # BCType: Boundary condition type
Output:
Bdry.cp: P-wave speed in halfspace
Bdry.cs: S-wave speed in halfspace
Bdry.rho: density in halfspace
BumDen: Bump density
eta: Principal radius 1
xi: Principal radius 2
'''
global alphaR, betaR, rhoR, alphaI, betaI
# *** Echo to PRTFIL user's choice of boundary condition ***
bctypes = ['S', 'H', 'T', 'I', 'V', 'R', 'A', 'F', 'W', 'P']
if BCType not in bctypes:
raise ValueError( 'Fatal error: Unknown boundary condition type' )
# ****** Read in BC parameters depending on particular choice ******
cp = 0.0
cs = 0.0
rho = 0.0
ice = Ice(0.0, 0.0, 0.0)
hs = None
# *** Twersky ice model parameters ***
if ( BCType == 'S' or BCType == 'H' or BCType == 'T' or BCType == 'I' ):
tmp = lines[line_ind]
ice.BumDen, ice.eta, ice.xi = float(tmp.split()[0]), float(tmp.split()[1]), float(tmp.split()[2])
line_ind += 1
# *** Half-space properties ***
if ( BCType == 'A' ):
tmp = lines[line_ind]
line_ind += 1
tmp = tmp.split()
tmp = [x for x in tmp if x[0].isnumeric()] # filter out non numbers
num_vals = len(tmp)
if num_vals == 6:
ztmp, alphaR, betaR, rhoR, alphaI, betaI = [float(x) for x in tmp[0:6]]
elif num_vals == 5:
ztmp, alphaR, betaR, rhoR, alphaI = [float(x) for x in tmp[0:5]]
elif num_vals == 4:
ztmp, alphaR, betaR, rhoR = [float(x) for x in tmp[0:4]]
elif num_vals == 3:
ztmp, alphaR, betaR = [float(x) for x in tmp[0:3]]
elif num_vals == 2:
ztmp, alphaR = [float(x) for x in tmp[0:2]]
elif num_vals == 1:
ztmp = [float(x) for x in tmp][0]
else: # there were no vals to read in so defaults will be used
pass
cp = crci( alphaR, alphaI, freq, AttenUnit )
cs = crci( betaR, betaI, freq, AttenUnit )
rho = rhoR
# raw values in user units
hs = HS(alphaR, betaR, rho, alphaI, betaI)
return [ cp, cs, rho, ice, hs, line_ind]
def read_env_core( envfil ):
global HV, NFirstAcoustic, NLastAcoustic
global alphaR, betaR, rhoR, alphaI, betaI
alphaR = 1500 # defaults
betaR = 0
rhoR = 1
alphaI = 0
betaI = 0
bdry = Bndry(None,None)
bdry.Top = TopBndry('dummy_opt')
bdry.Bot = BotBndry('dummy_opt', 10)
NFirstAcoustic = 0
bdry.Top.cp = 0.0
bdry.Top.cs = 0.0
bdry.Top.rho = 0.0
bdry.Bot.cp = 2000.0
bdry.Bot.cs = 0.0
bdry.Bot.rho = 2.0
ssp = None
with open(envfil, 'r') as f:
lines = f.readlines()
# first line is title
line_ind = 0
TitleEnv = re.findall('\'(.*)\'',lines[line_ind] )[0]
# second line is freq
line_ind += 1
freq = float((lines[line_ind].split())[0])
line_ind += 1
# third line is number of media
tmp = lines[line_ind]
line_ind += 1
NMedia = int(tmp.split()[0])
# initialize depth array
ssp_depth = [0]*(NMedia+1)
# fourth line is Top option
tmp = lines[line_ind]
line_ind += 1
TopOpt = re.findall('\'(.*)\'', tmp)[0]
if len(TopOpt) < 5:
TopOpt = TopOpt + ' '*(4-len(TopOpt)) # whitespace pad to 4 characters
# convert the deprecated '*' option to '~'
bdry.Top.Opt = TopOpt.replace('*', '~')
SSPType = bdry.Top.Opt[0]
bdry.Top.BC = bdry.Top.Opt[1]
# this should throw error, I may need to pad the end with spaces
AttenUnit = bdry.Top.Opt[2:4]
# *** SSP approximation options ***
ssp_types = ['N', 'C', 'S', 'Q', 'H', 'A']
if SSPType not in ssp_types:
raise ValueError('SSP must be in ' + str(ssp_types))
# *** Attenuation options ***
atten_opts = ['N', 'F', 'M', 'W', 'Q']
if AttenUnit[0] not in atten_opts:
raise ValueError( 'Unknown attenuation units, must be ' + str(atten_opts) )
[ bdry.Top.cp, bdry.Top.cs, bdry.Top.rho, holder, bdry.Top.hs, line_ind ] = topbot(lines, line_ind, freq, bdry.Top.BC, AttenUnit )
ssp_z = []
ssp_c = []
ssp_cs = []
ssp_rho = []
ssp_raw_list = []
Loc = [0] * NMedia
ssp_Npts = [0]*NMedia
ssp_N = [0]*NMedia
ssp_sigma = [0]*(NMedia+1)
for medium in range(NMedia):
if ( medium == 1 ):
Loc[medium] = 0
else:
Loc[ medium ] = Loc[ medium - 1 ] + ssp_Npts[ medium - 1 ]
# next line is the num points in ssp, sigma, depth
tmp = lines[line_ind]
line_ind += 1
ssp_N[medium], ssp_sigma[medium], ssp_depth[medium+1] = int(float(tmp.split()[0])), float(tmp.split()[1]), float(tmp.split()[2])
# read in the SSP
ssp_raw_list.append(SSPraw([], [], [], [], [], []))
for ii in range(1000):
tmp = lines[line_ind]
line_ind += 1
tmp = tmp.split()
tmp = [x for x in tmp if x[0].isnumeric()] # filter out non numbers
num_vals = len(tmp)
if num_vals == 6:
ztmp, alphaR, betaR, rhoR, alphaI, betaI = [float(x) for x in tmp[0:6]]
elif num_vals == 5:
ztmp, alphaR, betaR, rhoR, alphaI = [float(x) for x in tmp[0:5]]
elif num_vals == 4:
ztmp, alphaR, betaR, rhoR = [float(x) for x in tmp[0:4]]
elif num_vals == 3:
ztmp, alphaR, betaR = [float(x) for x in tmp[0:3]]
elif num_vals == 2:
ztmp, alphaR = [float(x) for x in tmp[0:2]]
elif num_vals == 1:
ztmp = [float(x) for x in tmp[0]][0]
else: # there were no vals to read in so defaults will be used
pass
## TRANSLATE CRCI, IN BELLHOP DIR
cp = crci( alphaR, alphaI, freq, AttenUnit )
cs = crci( betaR, betaI, freq, AttenUnit )
ssp_z.append(ztmp) # add in to existing vector
ssp_c.append(cp)
ssp_cs.append(cs)
ssp_rho.append(rhoR)
ssp_raw_list[ medium].z.append(ztmp) # add in to existing vector
ssp_raw_list[ medium].alphaR.append(alphaR)
ssp_raw_list[ medium].alphaI.append(alphaI)
ssp_raw_list[ medium].betaR.append(betaR)
ssp_raw_list[ medium].betaI.append(betaI)
ssp_raw_list[ medium].rho.append(rhoR)
# check for end of this layer
if ( ztmp == ssp_depth[ medium+1 ] ):
ssp_depth[0] = ssp_z[0]
# break
break
# end of loop for that medium
if ( ssp_N[ medium ] == 0 ): # calculate mesh automatically
# choose a reference sound speed
C = alphaR
if ( betaR > 0.0 ):
C = betaR # shear?
deltaz = 0.05 * C / freq # default sampling: 20 points per wavelength
ssp_N[ medium ] = round( ( ssp_depth[ medium + 1 ] - ssp_depth[ medium ] ) / deltaz )
ssp_N[ medium ] = np.max( [ssp_N[ medium ], 10] ) # require a minimum of 10 points
# keep track of first and last acoustic medium
if ( not np.any( cs ) ): # shear anywhere?
if ( NFirstAcoustic == 0 ):
NFirstAcoustic = medium
NLastAcoustic = medium
# stuff for Bellhop
if ( medium == 0 ):
HV = np.diff( np.array(ssp_z) ) # layer thicknesses
ssp_cz = np.diff( np.array(ssp_c )) / HV # gradient of ssp (centered-difference approximation)
ssp_Npts[ medium ] = ii
if ( medium == 0 ):
ssp_depth[0] = ssp_z[0]
## lower halfspace
# read in next line
tmp = lines[line_ind]
line_ind += 1
BotOpt = re.findall('\'(.*)\'', tmp)[0]
if len(BotOpt) < 5:
BotOpt = BotOpt + ' '*(4-len(BotOpt)) # whitespace pad to 4 characters
# convert the deprecated '*' option to '~'
bdry.Bot.Opt = BotOpt.replace('*', '~')
bdry.Bot.BC = bdry.Bot.Opt[0]
## TRANSLATE TOPBOT IN BELLHOP DIR
[ bdry.Bot.cp, bdry.Bot.cs, bdry.Bot.rho, holder, bdry.Bot.hs, line_ind ] = topbot( lines, line_ind, freq, bdry.Bot.BC, AttenUnit )
bdry.Top.depth = ssp_depth[0]
bdry.Bot.depth = ssp_depth[ NMedia ]
# Get rho, c just INSide the boundary (used later for reflection
# coefficients)
I = NFirstAcoustic
bdry.Top.rhoIns = ssp_rho[ I ]
bdry.Top.cIns = ssp_c[ I ]
I = Loc[ NLastAcoustic ] + ssp_Npts[ NLastAcoustic ]
bdry.Bot.rhoIns = ssp_rho[ I ]
bdry.Bot.cIns = ssp_c[ I ]
ssp = SSP(ssp_raw_list, ssp_depth, NMedia, N=ssp_N, sigma=ssp_sigma)
return [TitleEnv, freq, ssp, bdry, lines, line_ind]
def readvector(lines, line_ind):
'''
Reads a row-vector
This routine emulates a Fortran language capability that allows a '/'
to be used to terminate input
A user can type:
5 ! Number of values
0 1000 /
as a shorthand for
5
0 250 500 750 1000
Here the '/' terminates the reading and the code then creates 4 equally
spaced values between 0 and 1000.
However, one also has the option of using specific (non-equispaced
values)
'''
line_ind = int(line_ind)
tmp = lines[line_ind]
line_ind += 1
Nx = int(tmp.split()[0])
tmp = lines[line_ind]
line_ind += 1
if '/' in tmp:
tmp = tmp.split()
tmp = [x for x in tmp if x[0].isnumeric()]
if len(tmp) == 1:
x = float(tmp[0])
else:
x = [float(tmp[0]), float(tmp[1])]
if Nx > 2:
x = np.linspace( x[ 0 ], x[ 1 ], Nx ) # generate the vector
else:
x = [float(x) for x in tmp if x[0].isnumeric()]
return x, Nx, line_ind
def readsdrd(lines, line_ind):
'''
Read source depths and receiver depths
Variable 'Pos' is a structure:
Pos.r.depth = vector of receiver depths
Pos.Nrd = number of receiver depths
Pos.s.depth = vector of source depths
Pos.Nsd = number of source depths
'''
[ depth, Nx, line_ind] = readvector(lines, line_ind);
source = Source(depth)
[ depth, Nrd, line_ind ] = readvector(lines, line_ind);
dom = Dom(None, depth)
pos = Pos(source, dom)
return pos, line_ind
def read_env( envfil, model ):
''' Read an environmental file
should check here that model is one of the known ones
'''
if ( envfil != 'ENVFIL' ) and (envfil[-4:] != '.env' ):
envfil = envfil + '.env' # append extension
model = model.upper() # convert to uppercase
[ TitleEnv, freq, ssp, bdry, lines, line_ind] = read_env_core( envfil ) # read in the environmental file
model_cat = ['SCOOTER', 'KRAKEN', 'KRAKENC', 'KRAKEL', 'SPARC' ]
if model in model_cat:
tmp = lines[line_ind]
line_ind += 1
tmp = [x for x in tmp.split() if x[0].isnumeric()]
low, high = [float(x) for x in tmp]
cint = cInt(low, high)
tmp = lines[line_ind]
line_ind += 1
RMax = float(tmp.split()[0])
else: # dummy values for BELLHOP
cint = cInt(1500, 1e9)
RMax = 100
# BELLHOP3D has x-y coordinates of sources as well
if ( model == 'BELLHOP3D' ) :
raise ValueError("Bellhop 3d not supported atm")
# !!! check: does this delete Pos.s.x, etc. when executed
[pos, line_ind] = readsdrd(lines, line_ind) # read in the source and receiver depths
if ( model == 'BELLHOP' ) :
raise ValueError("Bellhop not supported at the moment")
# Pos.r.range = readr( fid ) # read in receiver ranges
# Pos.Nrr = length( Pos.r.range )
# Beam = read_bell( fid, Bdry, freq, Bdry.Bot.depth, Bdry.Top.depth, Pos.r.range( end ) )
# elseif ( strcmp( model, 'BELLHOP3D' ) )
# Pos.r.range = readr( fid ) # read in receiver ranges
# Pos.Nrr = length( Pos.r.range )
# Pos.theta = readtheta( fid )
# Pos.Ntheta = length( Pos.theta )
# Beam = read_bell( fid, Bdry, freq, Bdry.Bot.depth, Bdry.Top.depth, Pos.r.range( end ) )
else: # dummy value for models that don't use Beam parameters
beam = Beam('CG', Nbeams=0, alpha= [-15,15], box=Box(1.05*RMax, 1.05*np.max(pos.r.depth)), deltas = 0)
pos.r.range = np.linspace( 0, RMax, 501 ) # set up receiver range vector
return [ TitleEnv, freq, ssp, bdry, pos, beam, cint, RMax]
def read_modes(**kwargs):
'''
Read the modes produced by KRAKEN
usage:
keys are 'fname', 'freq', 'modes'
'fname' and 'freq' are mandatory, 'modes' is if you only want a subset of modes
[ Modes ] = read_modes_bin( filename, modes )
filename is without the extension, which is assumed to be '.moA'
freq is the frequency (involved for broadband runs)
(you can use freq=0 if there is only one frequency)
modes is an optional vector of mode indices
derived from readKRAKEN.m Feb 12, 1996 Aaron Thode
Translated to python by Hunter Akins 2019
Modes.M number of modes
Modes.k wavenumbers
Modes.z sample depths for modes
Modes.phi modes
Modes.Top.bc
Modes.Top.cp
Modes.Top.cs
Modes.Top.rho
Modes.Top.depth
Modes.Bot.bc
Modes.Bot.cp
Modes.Bot.cs
Modes.Bot.rho
Modes.Bot.depth
Modes.N Number of depth points in each medium
Modes.Mater Material type of each medium (acoustic or elastic)
Modes.Nfreq Number of frequencies
Modes.Nmedia Number of media
Modes.depth depths of interfaces
Modes.rho densities in each medium
Modes.freqVec vector of frequencies for which the modes were calculated
'''
filename = kwargs['fname']
if 'freq' in kwargs.keys():
freq = kwargs['freq']
if 'modes' in kwargs.keys():
modes = kwargs['modes']
with open(filename, 'rb') as f:
iRecProfile = 1; # (first time only)
lrecl = 4*unpack('<I', f.read(4))[0]; #record length in bytes
rec = iRecProfile - 1;
f.seek(rec * lrecl + 4) # do I need to do this ?
title = unpack('80s', f.read(80))
Nfreq = unpack('<I', f.read(4))[0]
Nmedia = unpack('<I', f.read(4))[0]
Ntot = unpack('<l', f.read(4))[0]
Nmat = unpack('<l', f.read(4))[0]
N = []
Mater = []
if Ntot < 0:
return
# N and Mater
rec = iRecProfile;
f.seek(rec * lrecl); # reposition to next level
for Medium in range(Nmedia):
N.append(unpack('<I', f.read(4))[0])
Mater.append(unpack('8s', f.read(8))[0])
# depth and density
rec = iRecProfile + 1
f.seek(rec * lrecl)
bulk = unpack('f'*2*Nmedia, f.read(4*2*Nmedia))
depth = [bulk[i] for i in range(0,2*Nmedia,2)]
rho = [bulk[i+1] for i in range(0,2*Nmedia,2)]
# frequencies
rec = iRecProfile + 2;
f.seek(rec * lrecl);
freqVec = unpack('d', f.read(8))[0]
freqVec = np.array(freqVec)
# z
rec = iRecProfile + 3
f.seek(rec * lrecl)
z = unpack('f'*Ntot, f.read(Ntot*4))
# read in the modes
# identify the index of the frequency closest to the user-specified value
freqdiff = abs(freqVec - freq );
freq_index = np.argmin( freqdiff );
# number of modes, m
iRecProfile = iRecProfile + 4;
rec = iRecProfile;
# skip through the mode file to get to the chosen frequency
for ifreq in range(freq_index+1):
f.seek(rec * lrecl);
M = unpack('l', f.read(8))[0]
# advance to the next frequency
if ( ifreq < freq_index ):
iRecProfile = iRecProfile + 2 + M + 1 + floor( ( 2 * M - 1 ) / lrecl ); # advance to next profile
rec = iRecProfile;
f.seek(rec * lrecl)
if 'modes' not in kwargs.keys():
modes = np.linspace(0, M-1, M, dtype=int); # read all modes if the user didn't specify
# Top and bottom halfspace info
# Top
rec = iRecProfile + 1
f.seek(rec * lrecl)
top_bc = unpack('c', f.read(1))[0]
cp = unpack('ff', f.read(8))
top_cp = complex( cp[ 0 ], cp[ 1 ] )
cs = unpack('ff', f.read(8))
top_cs = complex( cs[ 1 ], cs[ 1 ] )
top_rho = unpack('f', f.read(4))[0]
top_depth = unpack('f', f.read(4))[0]
top_hs = HS(alphaR=top_cp.real, alphaI=top_cp.imag, betaR=top_cs.real, betaI=top_cs.imag)
top = TopBndry(top_bc, depth=top_depth)
# Bottom
bot_bc = unpack('c', f.read(1))[0]
cp = unpack('ff', f.read(8))
bot_cp = complex( cp[ 0 ], cp[ 1 ] )
cs = unpack('ff', f.read(8))
bot_cs = complex( cs[ 1 ], cs[ 1 ] )
bot_rho = unpack('f', f.read(4))[0]
bot_depth = unpack('f', f.read(4))[0]
bot_hs = HS(alphaR=bot_cp.real, alphaI=bot_cp.imag, betaR=bot_cs.real, betaI=bot_cs.imag)
bot = BotBndry(bot_bc, bot_hs, depth=bot_depth)
rec = iRecProfile
f.seek(rec * lrecl)
# if there are modes, read them
if ( M == 0 ):
modes_phi = []
modes_k = []
else:
modes_phi = np.zeros((Nmat, len( modes )),dtype=np.complex64) # number of modes
for ii in range(len(modes)):
rec = iRecProfile + 2 + int(modes[ ii ])
f.seek(rec * lrecl)
phi = unpack('f'*2*Nmat, f.read(2*Nmat*4)) # Data is read columwise
#t0 = time.time()
phi = np.array(phi)
phir = phi[::2]
phii = phi[1::2]
#print('aray cast time', time.time()-t0)
#t0 = time.time()
#phir = np.array([phi[i] for i in range(0,2*Nmat,2)])
#phii = np.array([phi[i+1] for i in range(0,2*Nmat,2)])
#print('aray list time', time.time()-t0)
modes_phi[ :, ii ] = phir + complex(0, 1)*phii;
rec = iRecProfile + 2 + M;
f.seek(rec * lrecl)
k = unpack('f'*2*M, f.read(4*2*M))
kr = np.array([k[i] for i in range(0,2*M,2)])
ki = np.array([k[i+1] for i in range(0,2*M,2)])
modes_k = kr+ complex(0,1) * ki
modes_k = np.array([modes_k[i] for i in modes], dtype=np.complex64) # take the subset that the user specified
input_dict = {'M':M, 'modes_k': modes_k, 'z':z, 'modes_phi':modes_phi, 'top':top, 'bot':bot, 'N':N, 'Mater':Mater, 'Nfreq': Nfreq, 'Nmedia': Nmedia, 'depth':depth, 'rho':rho, 'freqVec':freqVec}
modes = Modes(**input_dict)
return modes
def my_float(x):
if x == 'nan' or x == 'NaN':
return np.nan
else:
return float(x)
def read_arrivals_asc(fname, narrmx=200):
if fname[-4:] != '.arr':
fname = fname + '.arr'
with open(fname, 'r') as f:
# read the header info
lines = f.readlines()
tmp = lines[0]
a = tmp.split()
freq = float(a[0])
Nsd, Nrd, Nrr = int(a[1]), int(a[2]), int(a[3])
tmp = lines[1].split()
sd = [float(x) for x in tmp]
tmp = lines[2].split()
rd = [float(x) for x in tmp]
tmp = lines[3].split()
rr = [float(x) for x in tmp] # in meters
dom = Dom(np.array(rr), np.array(rd))
arrival_list = []
source = Source(np.array(sd))
pos = Pos(source, dom)
line_index= 4
for i in range(Nsd):
num_angles = int(lines[line_index].split()[0])
line_index += 1
for j in range(Nrd):
for k in range(Nrr):
num_arrivals = int(lines[line_index].split()[0])
line_index += 1
loc_arrivals = [] #arrivals for this specific sd, rd, and rr
for arr in range(num_arrivals):
tmp = lines[line_index].split()
amp = my_float(tmp[0])*np.exp(complex(0,1)*my_float(tmp[1])*np.pi/180)
delay = complex(my_float(tmp[2]), my_float(tmp[3]))
src_ang = my_float(tmp[4])
rec_ang = my_float(tmp[5])
num_top_bnc = int(tmp[6])
num_bot_bnc = int(tmp[7])
loc_arrivals.append([amp, delay, src_ang, rec_ang, num_top_bnc, num_bot_bnc])
line_index += 1
arrival_list.append(loc_arrivals)
return arrival_list, pos
def read_arrivals_asc_alt(fname, narrmx=200):
"""
Alternate one with different header...
"""
if fname[-4:] != '.arr':
fname = fname + '.arr'
with open(fname, 'r') as f:
# read the header info
lines = f.readlines()
tmp = lines[1]
a = tmp.split()
freq = float(a[0])
b = lines[2].split()
c = lines[3].split()
d = lines[4].split()
Nsd, Nrd, Nrr = int(b[0]), int(c[0]), int(d[0])
sd = [float(x) for x in b[1:]]
rd = [float(x) for x in c[1:]]
rr = [float(x) for x in d[1:]] # in meters
dom = Dom(np.array(rr), np.array(rd))
arrival_list = []
source = Source(np.array(sd))
pos = Pos(source, dom)
line_index= 5
for i in range(Nsd):
num_angles = int(lines[line_index].split()[0])
line_index += 1
for j in range(Nrd):
for k in range(Nrr):
num_arrivals = int(lines[line_index].split()[0])
line_index += 1
loc_arrivals = [] #arrivals for this specific sd, rd, and rr
for arr in range(num_arrivals):
tmp = lines[line_index].split()
amp = my_float(tmp[0])*np.exp(complex(0,1)*my_float(tmp[1])*np.pi/180)
delay = complex(my_float(tmp[2]), my_float(tmp[3]))
src_ang = my_float(tmp[4])
rec_ang = my_float(tmp[5])
num_top_bnc = int(tmp[6])
num_bot_bnc = int(tmp[7])
loc_arrivals.append([amp, delay, src_ang, rec_ang, num_top_bnc, num_bot_bnc])
line_index += 1
arrival_list.append(loc_arrivals)
return arrival_list, pos
def plotray(fname):
"""
Translation of plotray to Python
Hunter Akins 2021
"""
# Plot the RAYfil produced by Bellhop or Bellhop3D
#% usage: plotray( rayfil )
#% where rayfil is the ray file (extension is optional)
#% e.g. plotray( 'foofoo' )
#%
#% for BELLHOP3D files, rays in (x,y,z) are converted to (r,z) coordinates
#%
#% MBP July 1999
#global units jkpsflag
with open(fname, 'r') as f:
lines = f.readlines()
TITLE = lines[0]
freq = float(lines[1])
tmp = lines[2].split(' ')
tmp = [x for x in tmp if x != '']
Nsx = int(tmp[0])
Nsy = int(tmp[1])
Nsz = int(tmp[2])
tmp = lines[3].split(' ')
tmp = [x for x in tmp if x != '']
Nalpha = int(tmp[0])
Nbeta = int(tmp[1])
deptht = float(lines[4])
depthb = float(lines[5])
fig, axis = plt.subplots(1,1)
axis.set_ylim([0, depthb+50])
axis.invert_yaxis()
line_ind = 7
for i in range(Nsz):
for ibeam in range(Nalpha):
if line_ind >= len(lines)-1:
break
else:
angle = float(lines[line_ind])
line_ind += 1
tmp = lines[line_ind].split(' ')
tmp = [x for x in tmp if x != '']
nsteps = int(tmp[0])
num_top_bnc= int(tmp[1])
num_bot_bnc= int(tmp[2])
line_ind += 1
x,y = np.zeros(nsteps), np.zeros(nsteps)
counter = 0
for line_ind in range(line_ind, line_ind + nsteps):
tmp = lines[line_ind].split(' ')
tmp = [x for x in tmp if x != '']
x[counter],y[counter] = float(tmp[0]), float(tmp[1])
counter +=1
if num_top_bnc == 0 and num_bot_bnc==0:
axis.plot(x,y, color='k')
elif num_top_bnc == 1 and num_bot_bnc == 0:
axis.plot(x,y, color='r', alpha=.85)
elif num_top_bnc == 0 and num_bot_bnc == 1:
axis.plot(x,y, color='b', alpha=.85)
else:
axis.plot(x,y, color='g',alpha=.7)
line_ind += 1
return fig, axis
def get_rays(fname):
with open(fname, 'r') as f:
lines = f.readlines()
TITLE = lines[0]
freq = float(lines[1])
tmp = lines[2].split(' ')
tmp = [x for x in tmp if x != '']
Nsx = int(tmp[0])
Nsy = int(tmp[1])
Nsz = int(tmp[2])
tmp = lines[3].split(' ')
tmp = [x for x in tmp if x != '']
Nalpha = int(tmp[0])
Nbeta = int(tmp[1])
deptht = float(lines[4])
depthb = float(lines[5])
line_ind = 7
ray_collections = []
for i in range(Nsz):
source_collection=[]
for ibeam in range(Nalpha):
if line_ind >= len(lines)-1:
break
else:
angle = float(lines[line_ind])
line_ind += 1
tmp = lines[line_ind].split(' ')
tmp = [x for x in tmp if x != '']
nsteps = int(tmp[0])
num_top_bnc= int(tmp[1])
num_bot_bnc= int(tmp[2])
line_ind += 1
xy = np.zeros((2,nsteps))
counter = 0
for line_ind in range(line_ind, line_ind + nsteps):
tmp = lines[line_ind].split(' ')
tmp = [x for x in tmp if x != '']
xy[0,counter],xy[1,counter] = float(tmp[0]), float(tmp[1])
counter +=1
ray = Eigenray(angle, num_top_bnc, num_bot_bnc, xy)
source_collection.append(ray)
line_ind += 1
ray_collections.append(source_collection)
return ray_collections
def read_shd_asc(filename):
raise NotImplementedError
def read_ram_tlgrid(filename):
raise NotImplementedError