Park Calculation ServiceΒΆ
CalcParkService gives access to the PARK module. This service is more complex than the other calcualtion services.
It contains the two function, CalcParkService.GetParkCalc
for getting a park objects and
CalcParkService.SetParkCalc
for passing it back to windPRO.
CalcParkService.GetTApiWtgId()
gets objects that are needed to connects wind turbine generator objects with the
PARK calculation (similar to factory.TApiWtgId()
). The connection is done by adding the Handle of the WTG objects.
A list of these TApiWtgId objects needs to be combined into factory.TApiObjectHandles()
and added to
CalcParkService.NewWtgs
or CalcParkService.ExistWtgs
.
There is access to the time varying calculation where TI is used to dynamically set the wake decay constants.
CalcParkService.GetWdcTiSetup
and CalcParkService.SetWdcTiSetup
for getting and setting those properties
respectively.
Find below an example how to use PARK calculations:
import time
import os
import pandas as pd
from copy import deepcopy
from datetime import datetime
from windproapi.utils import get_windpro_sample_path
from windproapi import nan_to_skipvalue
from windproapi import WindProApi
# Opening windPRO
_windproapi = WindProApi(api_port=1234)
working_dir = os.path.join(get_windpro_sample_path('3.6'), 'New Salem\\3.6')
project_path = os.path.join(working_dir, 'New Salem.w36p')
# _windproapi.start_windpro_random_port()
# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
calculation_service = _windproapi.get_service('CalculationService')
calc_park_service = _windproapi.get_service('CalcParkService')
obj_wtg_service = _windproapi.get_service('ObjWtgService')
factory = _windproapi.get_factory('WindproService')
# Loading project
project_service.LoadFromFile(filename=project_path)
# New PARK calculation
handle_park = calculation_service.CreateEmpty(calcType='CalcPark')
calculation_service.OpenCalcForEdit(handle=handle_park)
park_calc = calc_park_service.GetParkCalc()
# ParkTypeSiteData is a WAsP resource calculation together with a PARK calculation.
park_calc.ParkCalcType = 'ParkTypeSiteData'
# PARK2 type of calculation
park_calc.WakeModelType = 'ParkWakeModelNOJensenPark2'
# Name the calculation with time stamp of the generation
park_calc.Name = 'New Wake Calculation {}'.format(datetime.today().strftime('%Y-%m-%d %H:%M:%S'))
# Adding site data object that is necessary for the WAsP calcualtion
site_data_objs = objects_service.GetObjects(apiObjType='SiteData')
obj_site_data = [o for o in site_data_objs if o.UserDescription == 'for WAsP'][0]
intArr = factory.TApiObjectHandles()
intArr['int'].append(obj_site_data.Handle)
park_calc.StatData.SiteDataObjects = intArr
# Making an array of WTGs that can be added to the park
wtgids = factory.TApiWtgIds()
for w in objects_service.GetObjects(apiObjType='NewWTG'):
dummy = calc_park_service.GetTApiWtgId()
dummy.Handle = w.Handle
dummy.Rowindex = 0
wtgids['TApiWtgId'].append(dummy)
# Adding turbines
park_calc.NewWtgs = wtgids
# No existing wtgs
park_calc.ExistWtgs = factory.TApiWtgIds()
nan_to_skipvalue(park_calc)
calc_park_service.SetParkCalc(park_calc)
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=handle_park)
# Exporting PDF report
name = park_calc.Name
calculation_service.SaveReportAsPdf(handle=handle_park,
id=-1,
filename=os.path.join(working_dir, f'Park_{name}.pdf'))
A second example is provided using scripting to repeat PARK calculations to find the marginal AEP for each turbine. This is done by repeating the PARK calculation removing one turbine at a time.
import time
import os
import pandas as pd
from copy import deepcopy
from windproapi.utils import get_windpro_sample_path
from windproapi import nan_to_skipvalue
from windproapi import WindProApi
# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('3.6'), 'New Salem\\3.6')
project_path = os.path.join(working_dir, 'New Salem.w36p')
_windproapi.start_windpro_random_port()
# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
calculation_service = _windproapi.get_service('CalculationService')
calc_park_service = _windproapi.get_service('CalcParkService')
obj_wtg_service = _windproapi.get_service('ObjWtgService')
factory = _windproapi.get_factory('WindproService')
# Loading project
project_service.LoadFromFile(filename=project_path)
# Getting a list of all calculations present
for i in range(calculation_service.GetCount()):
print(calculation_service.GetCalc(ndx=i))
# Getting all PARK calculations in
all_park_calcs = calculation_service.GetCalcs(calcType='CalcPark')
# Choosing one of the PARK calculation, simply by index.
Handle = all_park_calcs[1].Handle
calculation_service.OpenCalcForEdit(handle=Handle)
park_calc = calc_park_service.GetParkCalc()
print(park_calc)
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=Handle)
# Make a copy to modify it
cloneHandle = calculation_service.Clone(handle=all_park_calcs[1].Handle)
calculation_service.OpenCalcForEdit(handle=cloneHandle)
clonedParkCalc = calc_park_service.GetParkCalc()
# Setting new wake model type
# You need to check in the wsdl file or try out different settings in windPRO to get to the right naming conventions.
clonedParkCalc.WakeModelType = 'ParkWakeModelNOJensenPark2'
nan_to_skipvalue(clonedParkCalc)
# Setting the with new settings to park calculation and calculating results
calc_park_service.SetParkCalc(clonedParkCalc)
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=cloneHandle)
# Exporting results from the wind farm
path_result = os.path.join(working_dir, 'park_result.csv')
res_to_file = calculation_service.GetResToFiles(handle=Handle)
print(f'Result {res_to_file}')
calculation_service.ResultToFile(handle=Handle, id='Park result', filename=path_result)
df = pd.read_csv(path_result, sep=';', thousands='.', decimal=',', header=1, skiprows=[2])
print(df[['Free mean wind speed', 'Result', 'Row data/Description']])
# Making park calculations ommiting single turbines
turbine_list = clonedParkCalc.NewWtgs.TApiWtgId
# Looping over all turbines and
for i in range(len(turbine_list)):
# List of turbines to include
temp_turbine_list = deepcopy(turbine_list)
temp_turbine_list.pop(i)
# Getting user description to identify turbine that is ommitted later.
obj = objects_service.GetObjectFromHandle(handle=turbine_list[i].Handle)
label_ommited = obj.UserDescription
# Opening for edit
calculation_service.OpenCalcForEdit(handle=cloneHandle)
temp_park_calc = calc_park_service.GetParkCalc()
# Changing number of turbines in the calculation
temp_park_calc.NewWtgs.TApiWtgId = temp_turbine_list
nan_to_skipvalue(temp_park_calc)
# Setting the with new settings to park calculation and calculating results
calc_park_service.SetParkCalc(temp_park_calc)
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=cloneHandle)
# Exporting results from the wind farm
path_result = os.path.join(working_dir, f'park_result_{i}.csv')
calculation_service.ResultToFile(handle=cloneHandle, id='Park result', filename=path_result)
# Reading the files and
try:
import pandas as pd
# CAUTION: this can fail depending on the setup of the computer to use , or . as decimal seperators.
# Or depending on your language setup the names of the columns might be different.
df = pd.read_csv(path_result, sep=';', thousands='.', decimal=',', header=1, skiprows=[2],
usecols=['Row data/Description', 'Free mean wind speed', 'Result'],
dtype={'Row data/Description': str, 'Free mean wind speed': float, 'Result': float})
print(f'Calculating for turbines:')
print(df['Row data/Description'].to_list())
print(f'Ommiting turbine:')
print(label_ommited)
print(df[['Result']].sum())
except:
print('Could not read results file.')