Source code for spawnwind.nrel.wind_input
# spawnwind
# Copyright (C) 2018-2019, Simmovation Ltd.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
"""
Handlers of input files relating to wind inflow
"""
from os import path
from .nrel_input_line import NrelInputLine
from .simulation_input import NRELSimulationInput
[docs]class WindInput(NRELSimulationInput):
"""
Base class for NREL input file determining the wind conditions for a FAST simulation. In FAST v7, this is an Aerodyn
input file and in v8 it is the InflowWind input file
"""
def __init__(self, lines, root_folder, wind_gen_spawner):
"""
:param lines: Lines, each with a key and value, making up the wind input
:type lines: list of :class:`NrelInputLine`
:param root_folder: The root folder containing the input file
:param wind_gen_spawner: Spawner for wind generation tasks into which properties relating to wind generation are
set
"""
super().__init__(lines, root_folder)
self._wind_gen_spawner = wind_gen_spawner
self._wind_task_cache = {}
self._wind_is_explicit = False
[docs] @classmethod
# pylint: disable=arguments-differ
def from_file(cls, file_path, wind_gen_spawner):
with open(file_path, 'r') as fp:
input_lines = fp.readlines()
root_folder = path.abspath(path.split(file_path)[0])
return cls([NrelInputLine(line) for line in input_lines], root_folder, wind_gen_spawner)
[docs] def get_wind_gen_tasks(self, prereq_dir, metadata):
"""
Create wind generation tasks to create new wind find if necessary
:param prereq_dir: Output directory for prerequisite simulations
:param metadata: Metadata for wind generation task
:return: list of wind generation tasks (size 0 or 1)
"""
raise NotImplementedError()
@property
def wind_gen_spawner(self):
"""
:return: underlying spawner for wind generation tasks
"""
return self._wind_gen_spawner
@wind_gen_spawner.setter
def wind_gen_spawner(self, spawner):
"""
:param spawner: underlying spawner for wind generation tasks
"""
self._wind_gen_spawner = spawner
@property
def wind_type(self):
"""
:return: Type of wind as a lowercase string (.e.g. 'bladed', 'turbsim', 'steady')
"""
raise NotImplementedError()
@wind_type.setter
def wind_type(self, type_):
"""
:param type_: Lowercase string representing wind type (.e.g 'bladed' or 'turbsim')
"""
raise NotImplementedError()
@property
def wind_speed(self):
"""
:return: Reference wind speed in m/s
"""
return self._wind_gen_spawner.wind_speed
@wind_speed.setter
def wind_speed(self, speed):
"""
:param speed: Reference wind speed in m/s
"""
self._wind_gen_spawner.wind_speed = speed
@property
def turbulence_intensity(self):
"""
:return: Turbulence intensity as a percentage
"""
return self._wind_gen_spawner.turbulence_intensity
@turbulence_intensity.setter
def turbulence_intensity(self, turbulence_intensity):
"""
:param turbulence_intensity: Turbulence intensity as a percentage
"""
self._wind_gen_spawner.turbulence_intensity = turbulence_intensity
@property
def turbulence_seed(self):
"""
:return: Integer turbulence seed for wind file generation
"""
return self._wind_gen_spawner.turbulence_seed
@turbulence_seed.setter
def turbulence_seed(self, seed):
self._wind_gen_spawner.turbulence_seed = seed
@property
def wind_shear(self):
"""
:return: Wind shear exponent
"""
return self._wind_gen_spawner.wind_shear
@wind_shear.setter
def wind_shear(self, exponent):
self._wind_gen_spawner.wind_shear = exponent
@property
def upflow(self):
"""
:return: Mean wind flow inclination in degrees upwards from the horizontal
"""
return self._wind_gen_spawner.upflow
@upflow.setter
def upflow(self, angle):
self._wind_gen_spawner.upflow = angle
@property
def wind_file(self):
"""
:return: Path of wind file if set
"""
return self['WindFile']
@wind_file.setter
def wind_file(self, file):
self._set_wind_file(file)
self._wind_is_explicit = True
def _set_wind_file(self, file):
"""
:param file: path of wind file
:return: Set wind file path in input
"""
raise NotImplementedError()
def _spawn_wind_gen_task(self, prereq_dir, metadata):
"""
Get wind task from hash if equivalent exists, otherwise spawn new wind generation task
:param prereq_dir: Output directory for prerequisite simulations
:param metadata: Metadata for siumulation
:return: WindGenerationTask
"""
wind_hash = self._wind_gen_spawner.input_hash()
if wind_hash in self._wind_task_cache:
wind_task = self._wind_task_cache[wind_hash]
else:
outdir = path.join(prereq_dir, wind_hash)
wind_task = self._wind_gen_spawner.spawn(outdir, metadata)
self._wind_task_cache[wind_hash] = wind_task
return wind_task
[docs]class AerodynInput(WindInput):
"""Handles contents of Aerodyn (FAST aerodynamics) input file, which defines wind input for versions < 8.12"""
key = 'ADFile'
[docs] def get_wind_gen_tasks(self, prereq_dir, metadata):
# Generate new wind file if needed
if self._wind_is_explicit:
return []
wind_task = self._spawn_wind_gen_task(prereq_dir, metadata)
self._set_wind_file(wind_task.wind_file_path)
return [wind_task]
@property
def wind_type(self):
return self._wind_gen_spawner.wind_type
@wind_type.setter
def wind_type(self, type_):
if type_ in ['bladed', 'turbsim']:
self._wind_gen_spawner.wind_type = type_
def _set_wind_file(self, file):
self['WindFile'] = file
def _lines_with_paths(self):
num_foils = int(self['NumFoil'])
index = self._get_index('FoilNm')
return range(index, index + num_foils)
class InflowWindInput(WindInput):
"""Handles contents of InflowWind input file which handles wind input of FAST in versions >= 8.12"""
key = 'InflowFile'
_wind_type_names = {
1: 'steady',
2: 'uniform',
3: 'turbsim',
4: 'bladed',
5: 'hawc',
6: 'dll'
}
_wind_type_numbers = {
'steady': 1,
'uniform': 2,
'turbsim': 3,
'bladed': 4,
'hawc': 5,
'dll': 6
}
def get_wind_gen_tasks(self, prereq_dir, metadata):
"""
Create wind generation tasks to create new wind find if necessary
:param prereq_dir: Output directory for prerequisite simulations
:param metadata: Metadata for wind generation task
:return: list of wind generation tasks (size 0 or 1)
"""
# Generate new wind file if needed
if self.wind_type == 'steady' or self.wind_type == 'uniform' or self._wind_is_explicit:
return []
wind_task = self._spawn_wind_gen_task(prereq_dir, metadata)
self._set_wind_file(wind_task.wind_file_path)
return [wind_task]
@property
def wind_type(self):
type_num = int(self['WindType'])
return self._wind_type_names[type_num]
@wind_type.setter
def wind_type(self, type_name):
if type_name not in self._wind_type_numbers:
raise ValueError('Invalid wind type')
self['WindType'] = self._wind_type_numbers[type_name]
if type_name in ['bladed', 'turbsim']:
self._wind_gen_spawner.wind_type = type_name
@property
def wind_speed(self):
if self.wind_type == 'steady':
return float(self['HWindSpeed'])
else:
return float(self._wind_gen_spawner.wind_speed)
@wind_speed.setter
def wind_speed(self, speed):
if self.wind_type == 'steady':
self['HWindSpeed'] = speed
else:
self._wind_gen_spawner.wind_speed = speed
@property
def wind_file(self):
return self._get_wind_file_line().value
@wind_file.setter
def wind_file(self, file):
self._get_wind_file_line().value = file
def _lines_with_paths(self):
def _is_filepath_key(key):
return 'filename' in key.lower()
return self._get_indices_if(_is_filepath_key)
def _get_wind_file_line(self):
type_ = int(self['WindType'])
filename_key_with_type = 'FilenameT{}'.format(type_)
if type_ == 2:
try:
return self._get_line(filename_key_with_type)
except KeyError:
return self._get_line('Filename')
elif type_ == 3:
try:
return self._get_line(filename_key_with_type)
except KeyError:
return self._get_line('Filename', 2)
elif type_ == 4:
try:
return self._get_line(filename_key_with_type)
except KeyError:
return self._get_line('FilenameRoot')
else:
raise KeyError("Cannot find wind file in InflowWind, type_={}. "\
"Set 'wind_type' parameter to an appropriate value".format(type_))
def _set_wind_file(self, file):
line = self._get_wind_file_line()
line.value = path.splitext(file)[0] if (line.key == 'FilenameRoot' or line.key == 'FilenameT4') else file