# Copyright 2014 the National Renewable Energy Laboratory
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This class contains a structure to store hydrodynamic data from WAMTI,
# AQWA, Nemoh, or another code that calculates hydrodynamic coefficients
# and excitation forces
from __future__ import division
import os
import numpy as np
from bemio.data_structures import bem
try:
from astropy.io import ascii
except:
raise Exception('The astropy module must be installed. Try "pip install astropy"')
[docs]class NemohOutput(object):
'''
Class to read and interact with NEMOH simulation data
Parameters:
sim_dir : str, optional
Directory where NEMOH simulation results are located
cal_file : str, optional
Name of NEMOH .cal file
results_dir : float, optional
Name of the directory that contains the NEMOH results files
mesh_dir : str, optional
Name of the directory that contains the NEMOH mesh files
scale : bool, optional
Boolean value to determine if the hydrodynamic data is scaled.
See the bemio.data_structures.bem.scale function for more
information
Examples
The following example assumes that a NEMOH simulation was run and that
there is data ./Results and ./Mesh directories. The Nemoh.cal file is
assumed to be located at ./Nemoh.cal
>>> nemoh_data = NemohOtuput()
'''
def __init__(self, sim_dir='./', cal_file='Nemoh.cal', results_dir = 'Results', mesh_dir='Mesh', scale=False):
# Set files
self.scaled_at_read = scale
self.scaled = True
self.dir = os.path.abspath(sim_dir)
print '\nReading NEMOH output in the ' + self.dir + ' directory'
self.files = bem.generate_file_names(os.path.join(self.dir,cal_file))
self.files['Nemoh'] = os.path.join(self.dir,cal_file)
self.files['RadiationCoefficients'] = os.path.join(self.dir,results_dir,'RadiationCoefficients.tec')
self.files['ExcitationForce'] = os.path.join(self.dir,results_dir,'ExcitationForce.tec')
self.files['DiffractionForce'] = os.path.join(self.dir,results_dir,'DiffractionForce.tec')
self.files['FKForce'] = os.path.join(self.dir,results_dir,'FKForce.tec')
try:
self.files['IRF'] = os.path.join(self.dir,results_dir,'IRF.tec')
except:
print '\tNo IRF forces or infinite frequency added mass forces read because the ' + self.files['IRF'] + ' was not found'
# Initialize data ovject
self.body = {}
# Object to store raw data
self.cal = bem.Raw()
# Read cal file
self._read_cal()
# Read tec plot output files
self.am, self.rd, self.w, raw_rad = _read_radiation(self.files['RadiationCoefficients'])
self.ex_mag, self.ex_phase, temp, self.ex_mag_raw = _read_excitation(self.files['ExcitationForce'])
self.dfr_mag, self.dfr_phase, temp, raw_diff = _read_tec(self.files['DiffractionForce'], data_type=1)
self.fk_mag, self.fk_phase, temp, raw_fk = _read_tec(self.files['FKForce'], data_type=1)
try:
self.am_inf, temp1, temp2, raw_am_inf = _read_tec(self.files['IRF'], data_type=2)
except:
raise Exception('The file ' + self.files['IRF'] + ' was not found. Please make sure the IRF output option is set to 1 in the Nemoh.cal file')
self.ex_im = self.ex_mag*np.sin(self.ex_phase)
self.ex_re = self.ex_mag*np.cos(self.ex_phase)
f_break = ['#'*100]*5
self.raw_output = f_break + raw_rad + f_break + raw_diff + f_break + self.ex_mag_raw + f_break + raw_fk + f_break
self._create_and_load_hydro_data_obj()
def _create_and_load_hydro_data_obj(self):
'''
Function to load hydrodynamic data into HydrodynamicData object
'''
for i in xrange(self.cal.n_bods):
self.body[i] = bem.HydrodynamicData()
self.body[i].am.all = self.am[0+6*i:6+6*i,:]
self.body[i].rd.all = self.rd[0+6*i:6+6*i,:]
self.body[i].ex.mag = self.ex_mag[0+6*i:6+6*i,:,:]
self.body[i].ex.phase = self.ex_phase[0+6*i:6+6*i,:,:]
self.body[i].ex.im = self.ex_im[0+6*i:6+6*i,:,:]
self.body[i].ex.re = self.ex_re[0+6*i:6+6*i,:,:]
self.body[i].am.inf = self.am_inf[0+6*i:6+6*i, :]
self.body[i].w = self.w
self.body[i].T = 2.*np.pi/self.w
self.body[i].water_depth = self.cal.water_depth
self.body[i].g = self.cal.g
self.body[i].rho = self.cal.rho
self.body[i].cg = self.cal.cg[i]
self.body[i].bem_code = 'NEMOH'
self.body[i].bem_raw_data = self.raw_output
self.body[i].body_num = i
self.body[i].name = self.cal.name[i]
self.body[i].num_bodies = self.cal.n_bods
self.body[i].scaled = self.scaled
self.body[i].wave_dir = self.cal.wave_dir
self.body[i].scale(self.scaled_at_read) # Note... this is missing the KH nondimensionalization because of where it is called
def _read_cal(self):
'''
Function to read Nemoh.cal file
'''
with open(self.files['Nemoh']) as fid:
cal = fid.readlines()
try:
np.float(cal[-1].split()[0])
except:
cal.pop()
self.cal.raw = cal
self.cal.rho = np.float(cal[1].split()[0])
self.cal.g = np.float(cal[2].split()[0])
self.cal.water_depth = np.float(cal[3].split()[0])
if self.cal.water_depth == 0:
self.cal.water_depth = 'infinite'
self.cal.wave_point = cal[4].split()[0:2]
self.cal.n_bods = int(cal[6].split()[0])
# Read wave directions
temp = cal[-6]
self.cal.wave_dir_n = np.float(temp.split()[0])
self.cal.wave_dir_start = np.float(temp.split()[1])
self.cal.wave_dir_end = np.float(temp.split()[2])
self.cal.wave_dir = np.linspace(self.cal.wave_dir_start,self.cal.wave_dir_end,self.cal.wave_dir_n)
# Read frequencies
temp = cal[-7]
self.cal.w_n = temp.split()[0]
self.cal.w_start = temp.split()[1]
self.cal.w_end = temp.split()[2]
self.cal.name = {}
self.cal.points_panels = {}
self.cal.n_dof = {}
self.cal.n_forces = {}
self.cal.n_add_lines = {}
self.cal.dof = {}
self.cal.forces = {}
self.cal.add_lines = {}
self.cal.cg = {}
line_count = 0
for i in xrange(self.cal.n_bods):
self.cal.name[i] = cal[8+line_count].split()[0]
self.cal.points_panels[i] = cal[9+line_count].split()[0:2]
self.cal.n_dof[i] = int(cal[10+line_count].split()[0])
self.cal.dof[i] = []
self.cal.forces[i] = []
self.cal.add_lines[i] = []
for j in xrange(self.cal.n_dof[i]):
self.cal.dof[i].append(cal[11+line_count+j])
if int(self.cal.dof[i][-1].split()[0]) == 2:
self.cal.cg[i] = np.array(self.cal.dof[i][-1].split()[4:7],dtype=float)
self.cal.n_forces[i] = int(cal[10+line_count+self.cal.n_dof[i]+1].split()[0])
for j in xrange(self.cal.n_forces[i]):
self.cal.forces[i].append(cal[11+line_count+j+self.cal.n_dof[i]+1])
self.cal.n_add_lines[i] = int(cal[10 + line_count + self.cal.n_dof[i] + self.cal.n_forces[i] + 2].split()[0])
for j in xrange(self.cal.n_add_lines[i]):
self.cal.add_lines[i].append(cal[11+line_count+j+self.cal.n_dof[i]+self.cal.n_forces[i]+1])
line_count += self.cal.n_dof[i] + self.cal.n_forces[i] + self.cal.n_add_lines[i] + 6
[docs] def read_kh(self, file, body_num):
'''
Function to read NEMOH linear spring stiffness data
Parameters:
file : str
Name of the file containing the linear spring stifness data
body_num : int
Number of the body corresponding to the Nemoh.cal input file
Returns:
The function does not directily return any variables, but calculates
self.body[body_num].k (linear spring stiffness)
Examples:
This example assumes there is a file called `KH_1.dat`
that contains linear spring stiffness data for body 1 in the
`Nemoh.cal` file and that there is a NemohOutput object called
`nemoh_data` already created.
>>> nemoh_data.read_kh(body_num=1, file='KH_1.dat')
.. Note:
This function is not necessary for such a simple function, but we may
need to make it more complicated in the future, so i'm leaving it as
a function - mjl 25March2015
'''
self.body[body_num].k = np.loadtxt(file)
if self.body[body_num].scaled is False:
self.body[body_num].k /= (self.body[body_num].rho * self.body[body_num].g)
print '\tSpring stiffness for body ' + self.body[body_num].name + ' scaled by read_kh method'
[docs] def read_hydrostatics(self, file, body_num):
'''
Function to read NEMOH hydrostatic data
Parameters:
file : str
Name of the file containing the hydrostatic data
body_num : int
Number of the body corresponding to the Nemoh.cal input file
Returns:
The function does not directily return any variables, but calculates
self.body[body_num].disp_vol (displace volume),
self.body[body_num].wp_area (water plane area), and
self.body[body_num].cb (center of gravity)
Examples:
This example assumes there is a file called `Hydrostatics_1.dat`
that contains hydrodynamic data for body 1 in the `Nemoh.cal` file
and that there is a NemohOutput object called `nemoh_data` already
created.
>>> nemoh_data.read_hydrostatics(body_num=1,file='./Hydrostatics_1.dat')
'''
with open(file) as fid:
hydrostatics = fid.readlines()
self.body[body_num].disp_vol = np.float(hydrostatics[3].split()[-1])
self.body[body_num].wp_area = np.float(hydrostatics[4].split()[-1])
xf = np.float(hydrostatics[0].split()[2])
yf = np.float(hydrostatics[1].split()[2])
zf = np.float(hydrostatics[2].split()[2])
self.body[body_num].cb = np.array([xf, yf, zf])
# def _reshape_tec(data):
# '''Internal function to reshape .tec data
# '''
# len = np.shape(data)[2]
#
# out = []
#
# for i in xrange(len):
# out.append(data[0,:,i])
#
# out = np.array(out)
#
# return out
def _read_tec(file, data_type):
'''
Internal function to read read am and rd coefficients
'''
# Read added mass and damping
with open(file) as fid:
raw = fid.readlines()
# Get the raw data
proc = {}
first = True
for i, line in enumerate(raw):
if 'Zone' in line:
if first is True:
first = False
n_vars = i-1
zone_length = int(line.split(',')[-2].split()[-1])
proc[i] = ascii.read(raw[i+1:i+zone_length+1])
# Sort the zones from the .tec file
zones = proc.keys()
zones.sort()
# Set the frequencies and calculate number of freqs
w = np.array(proc[zones[0]].field(0))
n_w = np.size(w)
if data_type == 1:
a = np.zeros([n_vars,1,n_w])
b = a.copy()
if data_type == 2:
a = np.zeros([n_vars,n_vars])
b = []
if data_type == 1:
for j in xrange(n_vars):
a[j,0,:] = proc[zones[-1]].field(1+j*2)
b[j,0,:] = proc[zones[-1]].field(2+j*2)
if data_type == 2:
for i, zone in enumerate(zones):
for j in xrange(n_vars):
a[i,j] = proc[zone].field(1+j*2)[0]
return (a, b, w, raw)
def _read_radiation(file, ):
'''
Internal function to read read am and rd coefficients
'''
# Read added mass and damping
with open(file) as fid:
raw = fid.readlines()
# Get the raw data
proc = {}
first = True
for i, line in enumerate(raw):
if 'Zone' in line:
if first is True:
first = False
n_vars = i-1
zone_length = int(line.split(',')[-2].split()[-1])
proc[i] = ascii.read(raw[i+1:i+zone_length+1])
# Sort the zones from the .tec file
zones = proc.keys()
zones.sort()
# Set the frequencies and calculate number of freqs
w = np.array(proc[zones[0]].field(0))
n_w = np.size(w)
# Create and fill coefficient matrices
a = np.zeros([n_vars,n_vars,n_w])
b = a.copy()
# Populate matrices
for i, zone in enumerate(zones):
for j in xrange(n_vars):
a[i,j,:] = proc[zone].field(1+j*2)
b[i,j,:] = proc[zone].field(2+j*2)
return (a, b, w, raw)
def _read_excitation(file, ):
'''
Internal function to read read am and rd coefficients
'''
# Read added mass and damping
with open(file) as fid:
raw = fid.readlines()
# Get the raw data
proc = {}
first = True
for i, line in enumerate(raw):
if 'Zone' in line:
if first is True:
first = False
n_vars = i-1
zone_length = int(line.split(',')[-2].split()[-1])
proc[i] = ascii.read(raw[i+1:i+zone_length+1])
# Sort the zones from the .tec file
zones = proc.keys()
zones.sort()
# Set the frequencies and calculate number of freqs
w = np.array(proc[zones[0]].field(0))
n_w = np.size(w)
# Create and fill coefficient matrices
a = np.zeros([n_vars,np.size(zones),n_w])
b = a.copy()
# Populate matrices
for i, zone in enumerate(zones):
for j in xrange(n_vars):
a[j,i,:] = proc[zone].field(1+j*2)
b[j,i,:] = proc[zone].field(2+j*2)
return (a, b, w, raw)
[docs]def read(sim_dir='./', cal_file='Nemoh.cal', results_dir = 'Results', mesh_dir='Mesh', scale=False):
'''
Function to read NEMOH data into a data object of type(NemohOutput)
Parameters:
sim_dir : str, optional
Directory where NEMOH simulation results are located
cal_file : str, optional
Name of NEMOH .cal file
results_dir : float, optional
Name of the directory that contains the NEMOH results files
mesh_dir : str, optional
Name of the directory that contains the NEMOH mesh files
scale : bool, optional
Boolean value to determine if the hydrodynamic data is scaled.
See the bemio.data_structures.bem.scale function for more
information
Returns:
nemoh_data
A NemohOutput object that contains the hydrodynamic data
Examples
The following example assumes that a NEMOH simulation was run and that
there is data ./Results and ./Mesh directories. The Nemoh.cal file is
assumed to be located at ./Nemoh.cal
>>> nemoh_data = NemohOtuput()
'''
nemoh_data = NemohOutput(sim_dir, cal_file, results_dir, mesh_dir, scale)
return nemoh_data
# def _reshape_tec(data):
#
# data = data.reshape(data.shape[1],1,data.shape[0])
#
# return out