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.')