Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 78 additions & 149 deletions openfast_io/openfast_io/FAST_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,45 +236,86 @@ def loop_dict(vartree, search_var, branch):
var = var.replace(' ', '')
loop_dict(vartree_head, var, [])

def read_outlist_freeForm(self,f,module):
def read_outlist_freeForm(self, f, module):
'''
Replacement for set_outlist that doesn't care about whether the channel is in the outlist vartree
Easier, but riskier because OpenFAST can crash

Inputs: f - file handle
module - of OpenFAST, e.g. SubDyn, SeaState (these modules use this)
'''
all_channels = []
Copy link

Copilot AI May 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The parsing logic in read_outlist_freeForm largely duplicates the code in read_outlist. Consider extracting the common logic into a shared helper to reduce duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
data = f.readline()
while data.split()[0] != 'END':
pattern = r'"?(.*?)"?' # grab only the text between quotes
data = re.findall(pattern, data)[0]
channels = data.split(',') # split on commas
channels = [c.strip() for c in channels] # strip whitespace
for c in channels:
self.fst_vt['outlist'][module][c] = True

# Handle the case if there are blank lines before actual data
while data.strip() == '':
data = f.readline()

while not data.strip().startswith('END'):
# Get part before the dash (comment)
line = data.split('-')[0]

# Replace all delimiters with spaces
for delim in ['"', "'", ',', ';', '\t']:
line = line.replace(delim, ' ')

# Split into words and add non-empty ones to the channel list
line_channels = [word.strip() for word in line.split() if word.strip()]
if line_channels:
all_channels.extend(line_channels)

# Read next line
data = f.readline()

# Handle the case if there are blank lines
while data.strip() == '':
data = f.readline()

# Store all channels in the outlist
for channel in all_channels:
self.fst_vt['outlist'][module][channel] = True

def read_outlist(self,f,module):
def read_outlist(self, f, module):
'''
Read the outlist section of the FAST input file, genralized for most modules
Read the outlist section of the FAST input file, generalized for most modules

Inputs: f - file handle
module - of OpenFAST, e.g. ElastoDyn, ServoDyn, AeroDyn, AeroDisk, etc.

Returns: List of channel names
'''
data = f.readline().split()[0] # to counter if we dont have any quotes
while data != 'END':
if data.find('"')>=0:
channels = data.split('"')
channel_list = channels[1].split(',')
else:
row_string = data.split(',')
if len(row_string)==1:
channel_list = [row_string[0].split('\n')[0]]
else:
channel_list = row_string
self.set_outlist(self.fst_vt['outlist'][module], channel_list)
data = f.readline().split()[0] # to counter if we dont have any quotes
all_channels = []
data = f.readline()

# Handle the case if there are blank lines before actual data
while data.strip() == '':
data = f.readline()

while not data.strip().startswith('END'):
# Get part before the dash (comment)
line = data.split('-')[0]

# Replace all delimiters with spaces
for delim in ['"', "'", ',', ';', '\t']:
line = line.replace(delim, ' ')

# Split into words and add non-empty ones to the channel list
line_channels = [word.strip() for word in line.split() if word.strip()]
if line_channels:
all_channels.extend(line_channels)

# Read next line
data = f.readline()

# Handle the case if there are blank lines
while data.strip() == '':
data = f.readline()

# Store all channels in the outlist
if all_channels:
self.set_outlist(self.fst_vt['outlist'][module], all_channels)

return all_channels

def read_MainInput(self):
# Main FAST v8.16-v8.17 Input File
Expand Down Expand Up @@ -557,38 +598,10 @@ def read_ElastoDyn(self, ed_file):
self.fst_vt['ElastoDyn']['BldGagNd'] = read_array(f,self.fst_vt['ElastoDyn']['NBlGages'], array_type=int)
else:
self.fst_vt['ElastoDyn']['BldGagNd'] = 0
f.readline()

# Loop through output channel lines


f.readline()
data = f.readline()
# if data != '':
# while data.split()[0] != 'END':
# channels = data.split('"')
# channel_list = channels[1].split(',')
# self.set_outlist(self.fst_vt['outlist']['ElastoDyn'], channel_list)

# data = f.readline()
# else:
# # there is a blank line between the outlist and the END of the file
# f.readline()

# Handle the case if there are blank lines before the END statement, check if blank line
while data.split().__len__() == 0:
data = f.readline()

while data.split()[0] != 'END':
if data.find('"')>=0:
channels = data.split('"')
channel_list = channels[1].split(',')
else:
row_string = data.split(',')
if len(row_string)==1:
channel_list = row_string[0].split('\n')[0]
else:
channel_list = row_string
self.set_outlist(self.fst_vt['outlist']['ElastoDyn'], channel_list)
data = f.readline()
self.read_outlist(f,'ElastoDyn')

# ElastoDyn optional outlist
try:
Expand All @@ -597,19 +610,7 @@ def read_ElastoDyn(self, ed_file):
self.fst_vt['ElastoDyn']['BldNd_BlOutNd'] = f.readline().split()[0]

f.readline()
data = f.readline()
while data.split()[0] != 'END':
if data.find('"')>=0:
channels = data.split('"')
opt_channel_list = channels[1].split(',')
else:
row_string = data.split(',')
if len(row_string)==1:
opt_channel_list = row_string[0].split('\n')[0]
else:
opt_channel_list = row_string
self.set_outlist(self.fst_vt['outlist']['ElastoDyn_Nodes'], opt_channel_list)
data = f.readline()
self.read_outlist(f,'ElastoDyn')
except:
# The optinal outlist does not exist.
None
Expand Down Expand Up @@ -856,33 +857,18 @@ def read_BeamDyn(self, bd_file, BladeNumber = 0):
self.fst_vt['BeamDyn'][BladeNumber]['OutNd'] = [idx.strip() for idx in f.readline().split('OutNd')[0].split(',')]
# BeamDyn Outlist
f.readline()
data = f.readline()
while data.split()[0] != 'END':
channels = data.split('"')
channel_list = channels[1].split(',')
self.set_outlist(self.fst_vt['outlist']['BeamDyn'], channel_list)
data = f.readline()


self.read_outlist(f,'BeamDyn')

# BeamDyn optional outlist
try:
f.readline()
# self.fst_vt['BeamDyn']['BldNd_BladesOut'] = int(f.readline().split()[0])
self.fst_vt['BeamDyn'][BladeNumber]['BldNd_BlOutNd'] = f.readline().split()[0]

f.readline()
data = f.readline()
while data.split()[0] != 'END':
if data.find('"')>=0:
channels = data.split('"')
opt_channel_list = channels[1].split(',')
else:
row_string = data.split(',')
if len(row_string)==1:
opt_channel_list = row_string[0].split('\n')[0]
else:
opt_channel_list = row_string
self.set_outlist(self.fst_vt['outlist']['BeamDyn_Nodes'], opt_channel_list)
data = f.readline()

self.read_outlist(f,'BeamDyn_Nodes')
except:
# The optinal outlist does not exist.
None
Expand Down Expand Up @@ -1025,19 +1011,7 @@ def read_InflowWind(self):

# InflowWind Outlist
f.readline()
data = f.readline()
while data.split()[0] != 'END':
if data.find('"')>=0:
channels = data.split('"')
channel_list = channels[1].split(',')
else:
row_string = data.split(',')
if len(row_string)==1:
channel_list = row_string[0].split('\n')[0]
else:
channel_list = row_string
self.set_outlist(self.fst_vt['outlist']['InflowWind'], channel_list)
data = f.readline()
self.read_outlist(f,'InflowWind')

f.close()

Expand Down Expand Up @@ -1194,25 +1168,9 @@ def read_AeroDyn(self):

# AeroDyn Outlist
f.readline()
data = f.readline()

# Handle the case if there are blank lines before the END statement, check if blank line
while data.split().__len__() == 0:
data = f.readline()

self.read_outlist(f,'AeroDyn')

while data.split()[0] != 'END':
if data.find('"')>=0:
channels = data.split('"')
channel_list = channels[1].split(',')
else:
row_string = data.split(',')
if len(row_string)==1:
channel_list = row_string[0].split('\n')[0]
else:
channel_list = row_string
self.set_outlist(self.fst_vt['outlist']['AeroDyn'], channel_list)
data = f.readline()

# AeroDyn optional outlist
try:
Expand All @@ -1221,19 +1179,7 @@ def read_AeroDyn(self):
self.fst_vt['AeroDyn']['BldNd_BlOutNd'] = f.readline().split()[0]

f.readline()
data = f.readline()
while data.split()[0] != 'END':
if data.find('"')>=0:
channels = data.split('"')
opt_channel_list = channels[1].split(',')
else:
row_string = data.split(',')
if len(row_string)==1:
opt_channel_list = row_string[0].split('\n')[0]
else:
opt_channel_list = row_string
self.set_outlist(self.fst_vt['outlist']['AeroDyn_Nodes'], opt_channel_list)
data = f.readline()
self.read_outlist(f,'AeroDyn_Nodes')
except:
# The optinal outlist does not exist.
None
Expand Down Expand Up @@ -1702,12 +1648,7 @@ def read_ServoDyn(self):

# ServoDyn Outlist
f.readline()
data = f.readline()
while data.split()[0] != 'END':
channels = data.split('"')
channel_list = channels[1].split(',')
self.set_outlist(self.fst_vt['outlist']['ServoDyn'], channel_list)
data = f.readline()
self.read_outlist(f,'ServoDyn')

f.close()

Expand Down Expand Up @@ -2230,19 +2171,7 @@ def read_HydroDyn(self, hd_file):

# HydroDyn Outlist
f.readline()
data = f.readline()
while data.split()[0] != 'END':
if data.find('"')>=0:
channels = data.split('"')
channel_list = channels[1].split(',')
else:
row_string = data.split(',')
if len(row_string)==1:
channel_list = row_string[0].split('\n')[0]
else:
channel_list = row_string
self.set_outlist(self.fst_vt['outlist']['AeroDyn'], channel_list)
data = f.readline()
self.read_outlist(f, 'HydroDyn')

f.close()

Expand Down
3 changes: 1 addition & 2 deletions openfast_io/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "hatchling.build"
[project]
name = "openfast_io"
# dynamic = ["version"]
version = "4.0.4"
version = "4.0.5"
description = "Readers and writers for OpenFAST files."
license = {file = "../LICENSE"}
authors = [
Expand Down Expand Up @@ -39,7 +39,6 @@ classifiers = [ # Optional
# Specify the Python versions you support here. In particular, ensure
# that you indicate you support Python 3. These classifiers are *not*
# checked by "pip install". See instead "python_requires" below.
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
Expand Down
2 changes: 1 addition & 1 deletion reg_tests/r-test
Loading