Python Examples

To help getting started with windPRO scripting examples are provided in the references for the windPRO services. You can find them at the bottom of each page describing a service or as a zip file here: Examples

For running the examples you need to have the windproapi package installed as described in Getting started. Some of the examples need Sample projects that can be downloaded within windPRO GUI under “Data/windPro Samples”. It is possible to mark all examples using SHIFT and download them by clicking OK. The examples should be able to locate the folder with the windRPO samples. For running a few examples there might be the need for additional python packages like numpy and pandas. You will have to download them before running the example scripts.

Important notice: All examples here are purely for illustration purposes. They are not intended to reflect correct energy yield assessments.

Below are all example codes that are used in the subsequent sections.

calculation_service.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from pprint import pprint
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'New Salem', '4.1')
project_path = os.path.join(working_dir, 'New Salem.w41p')

_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
calculation_service = _windproapi.get_service('CalculationService')
calc_park_service = _windproapi.get_service('CalcParkService')

# Open project
project_service.LoadFromFile(filename=project_path)

# Printing all the calculations present in the project
for i in range(calculation_service.GetCount()):
    print(calculation_service.GetCalc(ndx=i))

# Finding only PARK calculations. This is the CalcType that was available in all calculations
all_park_calcs = calculation_service.GetCalcs(calcType='CalcPark')
print('All available PARK calculations')
print(all_park_calcs)

# Now to exporting and importing files.
# Getting the handle of the a PARK calculation.
handle = all_park_calcs[1]['Handle']
name = all_park_calcs[1]['Name']
name = name.replace(':','_')
path_export = os.path.join(working_dir, 'park_result.xml')
calculation_service.SaveToFile(handle=handle, filename=path_export)

# Recalculating the PARK results. This does not change the result as we have not changed any of the inputs.
calculation_service.Calculate(handle=handle)

# Cloning one result. Can be useful if results should be
cloned_calc = calculation_service.Clone(handle=handle)

# Saving the data in the result to a file.
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 time variation', filename=path_result)

# Check what reports are available and export the main result
reports = calculation_service.GetReports(handle=handle)
reports_dict = {d['Name']: d['ID'] for d in reports}
pprint(reports_dict)
calculation_service.SaveReportAsPdf(handle=handle, id=reports_dict['Main Result'],
                                    filename=os.path.join(working_dir, f'Main_result_{name}.pdf'))
# All reports are exported in a combined pdf
calculation_service.SaveReportAsPdf(handle=handle, id=-1, filename=os.path.join(working_dir, f'All_result_{name}.pdf'))

# Making a new calculation within the project that can later be manipulated.
handle_new_park = calculation_service.CreateEmpty(calcType='CalcPark')
# To change settings in a calculation it need to be opened.
# More specific examples how to modify calucations are found in respective calculation service.
calculation_service.OpenCalcForEdit(handle=handle_new_park)
# calculations need to be closed before interacting with the GUI again.
calculation_service.CloseCalc(save=True)

engineering_forest_service.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

_windproapi = WindProApi()
_windproapi.start_windpro_random_port()

# Makiing example
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'ScalarService')
os.makedirs(working_dir, exist_ok=True)
project_path = os.path.join(working_dir, 'ScalarExample.w41p')

# Get to all services
scaler_service = _windproapi.get_service('ScalerService')
online_data_service = _windproapi.get_service('OnlineDataService')
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_site_data_service = _windproapi.get_service('ObjSiteDataService')
eng_forest_service = _windproapi.get_service('EngForestService')
obj_elevation_grid_service = _windproapi.get_service('ObjElevationGridService')
obj_meteo_service = _windproapi.get_service('ObjMeteoService')
calc_park_service = _windproapi.get_service('CalcParkService')
calculation_service = _windproapi.get_service('CalculationService')
calc_statgen_service = _windproapi.get_service('CalcStatgenService')
obj_wtg_service = _windproapi.get_service('ObjWtgService')
factory = _windproapi.get_factory('WindproService')


def load_measurement_into_meteo(file, wls_f, m_obj):
    """
    Loading a single file with an already setup wls (inport filter) file into a meteobject.
    :param str file: path to the file containing data.
    :param str wls_f: Path to wls import filter file.
    :param zeep.objects.TApiWpObject m_obj: Meteo Object generated with 'ObjMeteoService'.
    :return:
    """
    # Meteo Import file needs to be set up
    meteo_import = factory.TApiMeteoImportSetup()
    meteo_import.LoggerSetupFile = wls_f
    meteo_import.DontUseInSetup = False
    meteo_import.IsOnlineData = False

    # For the import setup we need infomration on the folder structure and where the data is located.
    # Multiple folders could be included, in this case it is only one
    folder_items = factory.TApiFileFolderItems()
    folder_item = factory.TApiFileFolderItem()
    folder_item.FFType = factory.TFileFolderListType('fftFile')
    folder_item.FFName = file
    folder_item.FFMask = None
    folder_item.FFInclSubFolder = False
    folder_item.FFTreatFolderAsZip = False
    nan_to_skipvalue(folder_item)
    folder_items.TApiFileFolderItem.append(folder_item)

    # Add Folder structure to serach paths
    meteo_import.SearchPathes = folder_items

    # Add import setup to meteo object
    nan_to_skipvalue(meteo_import)
    obj_meteo_service.AddImportSetup(handle=m_obj.Handle, importSetup=meteo_import, loggerSetupFn=wls_f)

    # Automatically create heights. Needs to be done before loading data to determine what data should
    # be loaded in.
    obj_meteo_service.AutoCreateFromImportSetup(handle=m_obj.Handle)
    # Load the actual data
    obj_meteo_service.LoadAllData(handle=m_obj.Handle)


# Create a WindPRO project in a given location
lng = 13
lat = 57
project_service.NewProject(lng=lng, lat=lat, filename=project_path)
# Setting the project coordinate system to UTM
utm_epsg = project_service.GetUtmWgs84EpsgForProject()
project_service.SetProjectCoorEPSG(epsg=utm_epsg)

# Adding forest layers
forest_layer = objects_service.AddLayer(layerName='Forest data', parentFolderHandle=0)
terrain_data_type = 'ODSObjHeightsGrid'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
available_data = online_data_service.GetServices(dataType=terrain_data_type)
handle_forest = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                       implId='SLU_FOREST_TREEHEIGHTS_ServiceID',
                                                       userDesc='Forest Data',
                                                       lat=lat,
                                                       lon=lng,
                                                       width=10000,
                                                       height=10000,
                                                       useAsTin=False)

elevation_grid_obj = obj_elevation_grid_service.GetElevationGridObject(handle=handle_forest)

# Adding the displacement height calculation
new_engforest_name = 'Displacement height from scripting'
new_eng_forest = eng_forest_service.AddEngForest(name=new_engforest_name)
new_eng_forest.HeightGridObjectHandle = handle_forest
new_eng_forest.InputType = 'fApihitHeightGrid'
new_eng_forest.HeightGridObjectLayerId = elevation_grid_obj.GridToHcSetup.Items.TApiGridToHCSetupItem[0].LayerID
nan_to_skipvalue(new_eng_forest)
eng_forest_service.SetEngForest(engForest=new_eng_forest)

# Displacement height calculations
N_sectors = 12
lats = [lat - 0.01, lat + 0.01, lat + 0.01, lat - 0.01]
lngs = [lng - 0.01, lng - 0.01, lng + 0.01, lng + 0.01]
calc_points = factory.TApiGPs()
for lat_i, lng_i in zip(lats, lngs):
    new_gp = factory.TApiGP()
    new_gp.Lat = lat_i
    new_gp.Lng = lng_i
    calc_points.TApiGP.append(new_gp)

# Interface to perform displacement height calculations directly.
# In the windPRO GUI this features needs a wtg at the location but is available in windPRO scripting
disp_heights = eng_forest_service.CalculateDisplacementHeights(engForestHandle=new_eng_forest.Handle,
                                                               siteDataHandle=0,
                                                               gps=calc_points,
                                                               sectors=N_sectors)
print('Displacement height calculations:')
for i, (lat, lng) in enumerate(zip(lats, lngs)):
    print(f'Location Latitude {lat}, Longitude {lng}:')
    print('Sector | displacement height [m]')
    print('--------------------------------')
    for j, dsp in enumerate(disp_heights[i].double):
        print('{:-6} |      {:2.2f}'.format(j, dsp))
    print('\n')

# Additional examples how to use the engineering forest model in Scalars, Statgen, or Park calculations
# Making a scalar that uses the engineering forest height
scaler = scaler_service.AddScaler(name='Engineering Forest Model')
scaler.DisplCalcHandle = new_eng_forest.Handle
scaler.ScalerType = 'ScalerTypeMastScalingGeo'
scaler.DisplType = 'ScalerDisplTypeCalc'
nan_to_skipvalue(scaler)
scaler_service.AddScaler(name=scaler)

# Using this scalar in a statical WAsP calculation with the displacement height.
# Adding mast data
mast_layer = objects_service.AddLayer(layerName='Mast data', parentFolderHandle=0)
objects_service.SetEditLayer(handle=mast_layer)
mast_file = os.path.join(os.path.dirname(__file__), 'data/meteo/New_Salem_2_South_60m_FINAL.csv')
wls_file = os.path.join(os.path.dirname(__file__), 'data/meteo/import_filter.wls')
new_obj = objects_service.AddObject(apiObjType='MeteoObjectData',
                                    lat=lat,
                                    lng=lng,
                                    userDesc='Mast from New Salem')
# Loading measurements with function defined above
load_measurement_into_meteo(mast_file, wls_file, new_obj)
meteo_object = obj_meteo_service.GetMeteoObject(handle=new_obj.Handle)

# Getting terrain data
terrain_layer = objects_service.AddLayer(layerName='Terrain data', parentFolderHandle=0)
objects_service.SetEditLayer(handle=terrain_layer)

terrain_data_type = 'ODSTerrainGrid'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
terrain_services = online_data_service.GetServices(dataType=terrain_data_type)
# Choosing a specific dataset and downloading the data
terrain_service_id = 'SWE50_Grid'
terrain_handle = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                        implId=terrain_service_id,
                                                        userDesc='Terrain data grid',
                                                        lat=lat,
                                                        lon=lng,
                                                        width=20000,
                                                        height=20000,
                                                        useAsTin=True)

# Getting roughness dataset sets
roughness_data_type = 'ODSRoughnessGridToLine'
online_data_service.PrepareService(dataType=roughness_data_type, lat=lat, lon=lng)
roughness_service = online_data_service.GetServices(dataType=roughness_data_type)
# Choosing a specific roughness dataset and downloading it
roughness_service_id = 'DataService_Rou_grid_GlobCover'
roughness_handle = online_data_service.DownloadRoughnessData(dataType=roughness_data_type,
                                                             implId=roughness_service_id,
                                                             userDesc='Roughness data',
                                                             lat=lat,
                                                             lon=lng,
                                                             width=20000,
                                                             height=20000)

# Site data and statgen calculations for 
site_data_layer = objects_service.AddLayer(layerName='Site Data Objects', parentFolderHandle=0)
objects_service.SetEditLayer(handle=site_data_layer)

# Statistical wind climate with a Stat Gen Calculation
obj_site_data_statgen = objects_service.AddObject(apiObjType='SiteData',
                                                  lat=lat,
                                                  lng=lng,
                                                  userDesc='STATGEN SiteData')
obj_site_data_service.SetPurpose(handle=obj_site_data_statgen.Handle, purpose='SdPurpStatgen')
# Connecting elevation and roughness data and
obj_site_data_service.TerrainLinkElevationAndRoughnessLine(handle=obj_site_data_statgen.Handle,
                                                           elevHandle=terrain_handle,
                                                           rouLineHandle=roughness_handle)
# Getting an update of the changes that have been made to the object back to python
obj_site_data_statgen = obj_site_data_service.GetSiteDataObject(obj_site_data_statgen.Handle)

# Creating a new statgen calculation calculation and starting to edit it
wws_file = os.path.join(working_dir, 'wind_stat.wws')
statgen_handle = calculation_service.CreateEmpty(calcType='CalcStatgen')
calculation_service.OpenCalcForEdit(handle=statgen_handle)
# Getting the statgen calculation object and adding information on
statgen = calc_statgen_service.GetStatgenCalc()
statgen.Filename = wws_file
statgen.Name = 'Statgen for test'
statgen.SiteDataHandle = obj_site_data_statgen.Handle
statgen.MeteoHandle = meteo_object.Handle
statgen.MeteoHeiHandle = 1
# Adding displacement height calculation
statgen.DispHeightSetup.DispHeightType = 'DispHeightTypeDHC'
statgen.DispHeightSetup.DispHeightCalcHandle = new_eng_forest.Handle
nan_to_skipvalue(statgen)
calc_statgen_service.SetStatgenCalc(statgen)
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=statgen_handle)

# WAsP calculation 
siteDataWasp = objects_service.AddObject(apiObjType='SiteData',
                                         lat=lat,
                                         lng=lng,
                                         userDesc='WASP SiteData')
obj_site_data_service.SetPurpose(handle=siteDataWasp.Handle, purpose='SdPurpWasp')
obj_site_data_service.TerrainLinkElevationAndRoughnessLine(handle=siteDataWasp.Handle,
                                                           elevHandle=terrain_handle,
                                                           rouLineHandle=roughness_handle)
obj_site_data_service.SetWindstatFn(handle=siteDataWasp.Handle,
                                    fileName=wws_file)

# Adding wind turbines for PARK calculation
wtg_layer = objects_service.AddLayer(layerName='Wind Farm', parentFolderHandle=0)
objects_service.SetEditLayer(handle=wtg_layer)

wtgPath = os.path.join(os.path.dirname(__file__), 'data/SIEMENS SWT-2.3_test.wtg')

for i, i_lat, i_lng in zip(range(4), lats, lngs):
    new_obj = objects_service.AddObject(apiObjType='NewWTG',
                                        lat=i_lat,
                                        lng=i_lng,
                                        userDesc='New WTG obj')
    wtgObj = obj_wtg_service.GetWtgObject(new_obj.Handle)
    wtgObj.Filename = wtgPath
    wtgObj.Hubheight = 92.6
    wtgObj.UserDescription = 'Turbine'
    wtgObj.UserLabel = 'Turbine ' + str(i + 1)
    nan_to_skipvalue(wtgObj)
    res = obj_wtg_service.SetWtgObject(wtgObj)

# Adding a PARK calculation
# Creating a new PARK calculation and opening it for editing
park_handle = calculation_service.CreateEmpty(calcType='CalcPark')
calculation_service.OpenCalcForEdit(handle=park_handle)
calc_park = calc_park_service.GetParkCalc()
calc_park.ParkCalcType = 'ParkTypeSiteData'
calc_park.Name = 'Park for test'

# Adding displacement height calculations
calc_park.StatData.DispHeightSetup.DispHeightType = 'DispHeightTypeDHC'
calc_park.StatData.DispHeightSetup.DispHeightCalcHandle = new_eng_forest.Handle

# Adding WAsP handle
intArr = factory.TApiObjectHandles()
intArr['int'].append(siteDataWasp.Handle)
calc_park.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
calc_park.NewWtgs = wtgids
calc_park.ExistWtgs = factory.TApiWtgIds()
nan_to_skipvalue(calc_park)

# Sending the PARK calculation back to windPRO and calculating
calc_park_service.SetParkCalc(calc_park)
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=park_handle)

LICENSE.txt

Copyright 2024 EMD International

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

myfirstwpapitest.py

import windproapi.windproapi
import time

windproapi = windproapi.windproapi.WindProApi()
windproapi.start_windpro_random_port()

wp_service = windproapi.get_service('WindproService')
version = wp_service.GetVersion()
print(version)
print(windproapi.api_port)

time.sleep(10)

windproapi.close_windpro()

object_service.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from pprint import pprint
from windproapi.utils import get_windpro_sample_path
from windproapi.windproapi import get_layer_dict, nan_to_skipvalue
from windproapi import WindProApi

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'New Salem', '4.1')
project_path = os.path.join(working_dir, 'New Salem.w41p')

_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')

# Open project
project_service.LoadFromFile(filename=project_path)

# Getting all objects that could be included in windPRO
print(objects_service.GetObjectTypes())

for s in objects_service.GetObjectTypes():
    print(f'{s}')

# Getting all turbines that are in the project. NewWTG in contrast to ExistWTG
new_wtgs_objs = objects_service.GetObjects(apiObjType='NewWTG')

# Getting information by handle
handle = new_wtgs_objs[0].Handle
obj = objects_service.GetObjectFromHandle(handle=handle)


# Defining function that loops through the output from objects_service.GetObjects to get an object back by its
# user description
def get_obj_from_list_by_user_description(objs, user_description):
    l_obj = []
    for o in objs:
        if o['UserDescription'] == user_description:
            l_obj.append(o)
    if len(l_obj) == 1:
        return l_obj[0]
    elif len(l_obj) == 0:
        raise ValueError(f'Could not find {user_description} in any UserDescription.')
    else:
        raise ValueError(f'More than one variable UserDescription {user_description}.')


obj_T24 = get_obj_from_list_by_user_description(new_wtgs_objs, 'T24')
print(obj_T24)

# Make some changes to the object before applying them with SetObjectPropsFromHandle()
# This changes the location of the object
obj.Lng = -101.40
obj.Lat = 46.75
obj.UserLabel = 'T24_new_location'
# in principle se can also change some other not so useful properties like the color
obj.Color.Red = 10
obj.Color.Green = 153
obj.Color.Blue = 25

# SetObjectPropsFromHandle() set the properties in the object given as a parameter and returns it with the new
# information.
nan_to_skipvalue(obj)
obj = objects_service.SetObjectPropsFromHandle(inObj=obj)

# WindPRO layers.
# Get information which layers are in the project.
layer_dict, layer_directories_dict = get_layer_dict(objects_service)
print('Layer names and handles')
pprint(layer_dict)
print('Layer folder names and handles')
pprint(layer_directories_dict)

# Adding new layers to the layer structure
# Adding a new layer. 0 is the base layer, so it is not placed in any folder
objects_service.AddLayer(layerName='New Layer Script Added', parentFolderHandle=0)
# Adding a new layer folder. 0 is the base layer, so the folder is visible in the base layer sturcture.
objects_service.AddLayerFolder(folderName='New layer folder', parentFolderHandle=0)
# Loading layers and layer directories again
layer_dict, layer_directories_dict = get_layer_dict(objects_service)
# Adding layer in the newly generated layer folder by name
objects_service.AddLayer(layerName='New layer in folder script added',
                         parentFolderHandle=layer_directories_dict['New layer folder'])

# set a layer to edit layer with a handle
objects_service.SetEditLayer(handle=layer_dict['WTG\\WTG\'s'])

# Making new objects
# AddObject(type, lat, lon, name) adds a new object of a specific type with a name and placed on parsed coordinates.
# To make the object usable in windPRO it needs additional properties set in its specific service.
new_obj = objects_service.AddObject(apiObjType='NewWTG',
                                    lat=46.75,
                                    lng=-101.40,
                                    userDesc='New WTG obj')
handle = new_obj.Handle

# CloneObject(handle) copies an object and returns the properties as a dictionary
clone_obj = objects_service.CloneObject(handle)

# SaveObject(int handle, string path) saves an object to a file and returns true on success
file_path = os.path.join(working_dir, 'turbine.wpobjects')
objects_service.SaveObject(handle=handle, filename=file_path)

# DeleteObject(int handle) deletes an object
objects_service.DeleteObject(handle=handle)

# LoadObject() loads an object from a file and returns true on success
loadObj = objects_service.LoadObject(filename=file_path)

onlinedata_service.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
_windproapi.start_windpro_random_port()

# Paths for running project
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'Online_Service')
os.makedirs(working_dir, exist_ok=True)

# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_elevation_grid_service = _windproapi.get_service('ObjElevationGridService')
online_data_service = _windproapi.get_service('OnlineDataService')

# Project path and location
project_path = os.path.join(working_dir, 'test.w41p')
lng = 9.85
lat = 56.8

# Making a new empty project
project_service.NewProject(lng=lng, lat=lat, filename=project_path)

# The available online data services can be found here in the SOAP reference xml file
for dataType in online_data_service.GetDataTypes():
    print(dataType)

# Getting data for an observed wind climate. This is form the list of above
dataType = 'ODSClimate'
online_data_service.PrepareService(dataType=dataType, lat=lat, lon=lng)

# The GetService returns a list with dertails about all available data.
meteo_services = online_data_service.GetServices(dataType=dataType)
# Use the service ID to download the data
print(meteo_services)
meteo_handles = online_data_service.DownloadMeteoData(implId='siEra5Basic',
                                                      lat=lat,
                                                      lon=lng,
                                                      maxDist=30000,
                                                      numPoints=1,
                                                      fromYear=2014,
                                                      toYear=2020)

# Downloading roughness and terrain data
terrain_layer = objects_service.AddLayer(layerName='Terrain data', parentFolderHandle=0)
objects_service.SetEditLayer(handle=terrain_layer)

terrain_data_type = 'ODSSurfaceGrid'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
terrain_services = online_data_service.GetServices(dataType=terrain_data_type)
print(terrain_services)
terrain_service_id = 'Global_TandemX90_Grid'
terrain_handle = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                        implId=terrain_service_id,
                                                        userDesc='Terrain data grid',
                                                        lat=lat,
                                                        lon=lng,
                                                        width=10000,
                                                        height=10000,
                                                        useAsTin=True)

# Downloading Line data. It is also possible to download data that is stored as grids and convert them to lines directly.
terrain_data_type = 'ODSHeightsLine'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
terrain_services = online_data_service.GetServices(dataType=terrain_data_type)
print(terrain_services)
terrain_service_id = 'Global_TandemX90_Grid'
terrain_handle = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                        implId=terrain_service_id,
                                                        userDesc='Line Terrain data (from grid)',
                                                        lat=lat,
                                                        lon=lng,
                                                        width=10000,
                                                        height=10000,
                                                        useAsTin=True)

roughness_data_type = 'ODSRoughnessGridToLine'
online_data_service.PrepareService(dataType=roughness_data_type, lat=lat, lon=lng)
roughness_service = online_data_service.GetServices(dataType=roughness_data_type)
print(roughness_service)
roughness_service_id = 'DataService_Rou_grid_GlobCover'
roughness_handle = online_data_service.DownloadRoughnessData(dataType=roughness_data_type,
                                                             implId=roughness_service_id,
                                                             userDesc='Roughness data',
                                                             lat=lat,
                                                             lon=lng,
                                                             width=10000,
                                                             height=10000)

# Adding forest layers
forest_layer = objects_service.AddLayer(layerName='Forest data', parentFolderHandle=0)
terrain_data_type = 'ODSObjHeightsGrid'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
available_data = online_data_service.GetServices(dataType=terrain_data_type)
handle_forest = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                       implId='GLOB_Sentinel2_Canopy_Grid',
                                                       userDesc='Forest Data',
                                                       lat=lat,
                                                       lon=lng,
                                                       width=10000,
                                                       height=10000,
                                                       useAsTin=False)

onlinedata_service_compact.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
import windproapi.windproapi
from windproapi.utils import get_windpro_sample_path

# Opening windPRO
windproapi = windproapi.windproapi.WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'Online_Service')
os.makedirs(working_dir, exist_ok=True)
windproapi.start_windpro_random_port()
# Project service for making a new project
online_data_service = windproapi.get_service('OnlineDataService')
service = windproapi.get_service('ProjectService')
lng, lat = 9., 55.
service.NewProject(lng, lat, os.path.join(os.path.join(get_windpro_sample_path('4.1'), 'Online_Service'), 'oneline_test.w41p'))
online_data_service.PrepareService(dataType='ODSClimate', lat=lat, lon=lng)
# Climate data
meteo_services = online_data_service.GetServices(dataType='ODSClimate')
meteo_handles = online_data_service.DownloadMeteoData(implId='siEra5Basic',
                                                      lat=lat,
                                                      lon=lng,
                                                      maxDist=30000,
                                                      numPoints=1,
                                                      fromYear=2014,
                                                      toYear=2020)
# Topography and Orography
online_data_service.PrepareService(dataType='ODSSurfaceGrid', lat=lat, lon=lng)
terrain_services = online_data_service.GetServices(dataType='ODSSurfaceGrid')
terrain_service_id = 'Global_TandemX90_Grid'
terrain_handle = online_data_service.DownloadHeightData(dataType='ODSSurfaceGrid',
                                                        implId=terrain_service_id,
                                                        userDesc='Terrain data grid',
                                                        lat=lat,
                                                        lon=lng,
                                                        width=10000,
                                                        height=10000,
                                                        useAsTin=True)
roughness_data_type = 'ODSRoughnessGridToLine'
online_data_service.PrepareService(dataType='ODSRoughnessGridToLine', lat=lat, lon=lng)
roughness_service = online_data_service.GetServices(dataType='ODSRoughnessGridToLine')
roughness_handle = online_data_service.DownloadRoughnessData(dataType='ODSRoughnessGridToLine',
                                                             implId='DataService_Rou_grid_GlobCover',
                                                             userDesc='Roughness data',
                                                             lat=lat,
                                                             lon=lng,
                                                             width=10000,
                                                             height=10000)

optimize_service.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
import time
from windproapi import WindProApi, nan_to_skipvalue
from windproapi.utils import get_windpro_sample_path
import numpy as np
import pandas as pd

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'Aparados da Serra', '4.1')
project_path = os.path.join(working_dir, 'Aparados da Serra basic project.w41p')

_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
calculation_service = _windproapi.get_service('CalculationService')
optimize_service = _windproapi.get_service('OptimizeService')
wtg_explorer_service = _windproapi.get_service('WtgExplorerService')
factory = _windproapi.get_factory('WindproService')

# Loading project file
project_service.LoadFromFile(filename=project_path)

# Setting up an optimization for multiple sizes as potential layouts.
# Choosing range of number of turbines to be investigated and a resource grid as the basis
N_turbines = np.arange(1, 16, 1)
resource_file = os.path.join(working_dir, 'Aparados da Serra basic project_Hub_98.0_0.rsf')

# Making new session
session_name = 'New Session from scripting'
session_guid = optimize_service.AddSession(session_name)
optimize_service.OpenSession(session_guid)

# Adding a new site to the calculations
site_uid = optimize_service.AddSite('Test Optimization')
site = optimize_service.GetSite(site_uid)

# Here we can decide to change the wake decay constant to user defined
site.WakeDecayDefTerID = 'wkCustom'
site.UserDefWakeDecay = 0.07

# Setting the site specific values.
# We stay with the default onshore wake decay constant which is default behaviour
wtg_area_obj = objects_service.GetObjects('WTGareas')[1]
site.WTGAreaHandle = wtg_area_obj.Handle
site.WindResourceFiles.string[0] = resource_file
nan_to_skipvalue(site)
optimize_service.SetSite(site)

# Adding specific wind turbine
wtg_from_explorer = wtg_explorer_service.GetWtgFromUID(uid='{832CAEAF-54E2-4A29-842C-144AA88B553E}')
# Setting default tower height to match the rsf file
wtg_uid = optimize_service.AddWTG(siteGuid=site_uid,
                                  name=os.path.basename(wtg_from_explorer.FileName),
                                  wtgFile=wtg_from_explorer.FileName)

wtg = optimize_service.GetWTG(wtg_uid)
wtg.Hubheight = 98.0
# wtg.Hubheight = 98.0
nan_to_skipvalue(wtg)
optimize_service.SetWTG(wtg)

# This version do work
for n in N_turbines:
    print(f'Adding {n} turbines size.')
    SizesAndRuns = optimize_service.CreateDefaultLayout(wtgGuid=wtg_uid,
                                                        numTurbines=n)

wtg = optimize_service.GetWTG(wtg_uid)

layout_list = factory.TApiStringArray()
for i in range(len(N_turbines)):
    # Getting the Size to get the Guid for the layout for starting the optimization later
    new_size = optimize_service.GetSize(wtg.RunList.TApiOptimizeNameGuid[i].Guid)
    layout_list.string.append(new_size.LayoutList.TApiOptimizeNameGuid[0].Guid)

# Starting runs
startRuns = optimize_service.StartRuns(layout_list)
# Runs are running in the background in the optimizer.
# Therefore, we need to check the status of all
is_running = True
while is_running:
    is_running = False
    calcs_running = 0
    time.sleep(10)
    for layout_guid in layout_list.string:
        status = optimize_service.GetStatus(layout_guid)
        if status == 'olsApiRunning':
            is_running = True
            calcs_running += 1
    print(f'Number of calculations running: {calcs_running}')

# Printing all AEPs found and the additional AEP for adding one more turbine in an optimized setup
res_dict = {'AEP': [], 'delta AEP': [], 'Guid': []}
last_AEP = 0.0
print('  N_Turbines|         AEP|   delta AEP')
print('-' * 12 + '+' + '-' * 12 + '+' + '-' * 12)
for n, layout_guid in zip(N_turbines, layout_list.string):
    layout = optimize_service.GetLayout(layout_guid)
    aep, delta_aep = layout.MaxObjective, layout.MaxObjective - last_AEP
    print('{:12d}|{:12.2f}|{:12.2f}'.format(n, aep, delta_aep))
    last_AEP = layout.MaxObjective
    res_dict['AEP'].append(aep)
    res_dict['delta AEP'].append(delta_aep)
    res_dict['Guid'].append(layout.Guid)

# Make dataframe with AEPs and GUId
df = pd.DataFrame(res_dict, index=N_turbines)
df.index.name = 'N_turbines'

# As an example selecting the realization where an additional turbine is adding 85% of the AEP the smallest setup
df['rel delta AEP'] = df['delta AEP'] / (df['AEP'][N_turbines[0]] / 2)
ind = (df['rel delta AEP'] - 0.85).abs().idxmin()

# Getting the layout
layout = optimize_service.GetLayout(df.loc[ind]['Guid'])
# Realise layout and return handles for WTG's placed
WTGHandles = optimize_service.ApiRealiseLayout(layoutGuid=layout.Guid,
                                               layerName='Realized Layout')

optimize_service.CloseSession(True)

# ## For filling the area to the maximum
#
# optimize_service.OpenSession(session_guid)
#
# # Adding a new site to the calculations
# site_uid = optimize_service.AddSite('Test Fill Max')
# site = optimize_service.GetSite(site_uid)
#
# # Setting the site specific values.
# # We stay with the default onshore wake decay constant which is default behaviour
# wtg_area_obj = objects_service.GetObjects('WTGareas')[2]
# site.WTGAreaHandle = wtg_area_obj.Handle
# site.WindResourceFiles.string[0] = resource_file
# nan_to_skipvalue(site)
# optimize_service.SetSite(site)
#
# # Adding specific wind turbine
# wtg_from_explorer = wtg_explorer_service.GetWtgFromUID(uid='{832CAEAF-54E2-4A29-842C-144AA88B553E}')
# # Setting default tower height to match the rsf file
# wtg_from_explorer.DefHubHeight = 98.0
# wtg_uid = optimize_service.AddWTG(siteGuid=site_uid,
#                                   name=os.path.basename(wtg_from_explorer.FileName),
#                                   wtgFile=wtg_from_explorer.FileName)
# wtg = optimize_service.GetWTG(wtg_uid)
# wtg.WTGUID.Hubheight = 98.0
# nan_to_skipvalue(wtg)
# optimize_service.SetWTG(wtg)
# wtg = optimize_service.GetWTG(wtg_uid)
#
# # Adding different turbine sizes
# setup = optimize_service.GetLayoutDefaultSetup()
# setup.StartModel = 'optFillStartModel'
# setup.MinTurbines = 10
# setup.MaxTurbines = 10
# nan_to_skipvalue(setup)
# optimize_service.CreateLayout(wtgGuid=wtg_uid, Setup=setup)
#
# wtg = optimize_service.GetWTG(wtg_uid)
#
# layout_list = factory.TApiStringArray()
# # Getting the Size to get the Guid for the layout for starting the optimization later
# new_size = optimize_service.GetSize(wtg.RunList.TApiOptimizeNameGuid[0].Guid)
# layout_list.string.append(new_size.LayoutList.TApiOptimizeNameGuid[0].Guid)
#
# # Starting runs
# startRuns = optimize_service.StartRuns(layout_list)
# # Runs are running in the background in the optimizer.
# # Therefore, we need to check the status of all
# is_running = True
# while is_running:
#     is_running = False
#     calcs_running = 0
#     time.sleep(10)
#     for layout_guid in layout_list.string:
#         status = optimize_service.GetStatus(layout_guid)
#         if status == 'olsApiRunning':
#             is_running = True
#             calcs_running += 1
#     print(f'Number of calculations running: {calcs_running}')
#
#
# optimize_service.CloseSession(True)

optimize_service_more_setups.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
import time
from windproapi import WindProApi, nan_to_skipvalue
from windproapi.utils import get_windpro_sample_path
import numpy as np
import pandas as pd

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'Aparados da Serra', '4.1')
project_path = os.path.join(working_dir, 'Aparados da Serra basic project.w41p')

_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
calculation_service = _windproapi.get_service('CalculationService')
optimize_service = _windproapi.get_service('OptimizeService')
wtg_explorer_service = _windproapi.get_service('WtgExplorerService')
factory = _windproapi.get_factory('WindproService')

# Loading project file
project_service.LoadFromFile(filename=project_path)

# Setting up an optimization for multiple sizes as potential layouts.
# Choosing range of number of turbines to be investigated and a resource grid as the basis
N_turbines = np.arange(1, 5, 1)
resource_file = os.path.join(working_dir, 'Aparados da Serra basic project_Hub_98.0_0.rsf')

# Making new session
session_name = 'New Session from scripting'
session_guid = optimize_service.AddSession(session_name)
optimize_service.OpenSession(session_guid)

# Adding a new site to the calculations
site_uid = optimize_service.AddSite('Test Fill Max')
site = optimize_service.GetSite(site_uid)

# Setting the site specific values.
# We stay with the default onshore wake decay constant which is default behaviour
wtg_area_obj = objects_service.GetObjects('WTGareas')[0]
site.WTGAreaHandle = wtg_area_obj.Handle
site.WindResourceFiles.string[0] = resource_file
nan_to_skipvalue(site)
optimize_service.SetSite(site)

# Adding specific wind turbine
wtg_from_explorer = wtg_explorer_service.GetWtgFromUID(uid='{832CAEAF-54E2-4A29-842C-144AA88B553E}')
# Setting default tower height to match the rsf file
# wtg_from_explorer.DefHubHeight = 98.0
wtg_uid = optimize_service.AddWTG(siteGuid=site_uid,
                                  name=os.path.basename(wtg_from_explorer.FileName),
                                  wtgFile=wtg_from_explorer.FileName)
wtg = optimize_service.GetWTG(wtg_uid)
wtg.Hubheight = 98.0
nan_to_skipvalue(wtg)
optimize_service.SetWTG(wtg)
wtg = optimize_service.GetWTG(wtg_uid)

# Adding different turbine sizes
setup = optimize_service.GetLayoutDefaultSetup()
setup.StartModel = 'optFillStartModel'
setup.StepModel = 'optRandomLocalStepModel'
setup.MaxMinutes = 1
setup.NumberOfTurbines = 12
nan_to_skipvalue(setup)
optimize_service.CreateLayout(wtgGuid=wtg_uid, Setup=setup)

wtg = optimize_service.GetWTG(wtg_uid)

layout_list = factory.TApiStringArray()
# Getting the Size to get the Guid for the layout for starting the optimization later
new_size = optimize_service.GetSize(wtg.RunList.TApiOptimizeNameGuid[0].Guid)
layout_list.string.append(new_size.LayoutList.TApiOptimizeNameGuid[0].Guid)

# Starting runs
startRuns = optimize_service.StartRuns(layout_list)
# Runs are running in the background in the optimizer.
# Therefore, we need to check the status of all
is_running = True
while is_running:
    is_running = False
    calcs_running = 0
    time.sleep(10)
    for layout_guid in layout_list.string:
        status = optimize_service.GetStatus(layout_guid)
        if status == 'olsApiRunning':
            is_running = True
            calcs_running += 1
    print(f'Number of calculations running: {calcs_running}')


optimize_service.CloseSession(True)

project_service.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
import time
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue


# Opening windPRO
_windproapi = WindProApi()
_windproapi.start_windpro_random_port()

working_dir = os.path.join(get_windpro_sample_path('4.1'), 'ProjectService')
os.makedirs(working_dir, exist_ok=True)

# Services
project_service = _windproapi.get_service('ProjectService')

# Project path and location
project_path = os.path.join(working_dir, 'test.w41p')
lng = 10.
lat = 55.

# Making a new empty project and saving it
project_service.NewProject(lng=lng, lat=lat, filename=project_path)
project_service.SaveToFile(filename=project_path)

# It is possible to get the project path from windPRO
project_path_from_windpro = project_service.GetProjectFilename()
print(f'Path from script: {project_path}')
print(f'Path from windPRO: {project_path_from_windpro}')

# Close windPRO
_windproapi.close_windpro()
time.sleep(2)

# Open windPRO again and load the project
# If you open windPRO
_windproapi = WindProApi()
_windproapi.start_windpro_random_port()
time.sleep(2)
# After closing windpro it is necessary to get the project again as the port might have changed
project_service = _windproapi.get_service('ProjectService')
project_service.LoadFromFile(filename=project_path)

# Exporting project to export file
export_path = os.path.join(working_dir, 'test.w36e')
project_service.ExportToFile(filename=export_path)

# Close windPRO again
time.sleep(2)
_windproapi.close_windpro()

project_service_coordinates.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi

# Opening windPRO
windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'ProjectService')
os.makedirs(working_dir, exist_ok=True)

windproapi.start_windpro_random_port()
project_service = windproapi.get_service('ProjectService')

# Project path and location
project_path = os.path.join(working_dir, 'test.w41p')
lng = 11.
lat = 55.

# Making a new empty project and saving it
project_service.NewProject(lng=lng, lat=lat, filename=project_path)

# The the UTM zone's EPSG number
utm_zone = project_service.GetUtmWgs84EpsgForProject()
print(f'UTM EPSG of the project is {utm_zone}.')

# Setting the project coordinate system to DKTM1 with EPSG 4093
project_service.SetProjectCoorEPSG(epsg=4093)

project_service_wasp.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi

# Opening windPRO
_windproapi = WindProApi()

working_dir = os.path.join(get_windpro_sample_path('4.1'), 'New Salem', '4.1')
project_path = os.path.join(working_dir, 'New Salem.w41p')

_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
factory = _windproapi.get_factory('WindproService')

# Loading project
project_service.LoadFromFile(filename=project_path)

# Getting the current WAsP setup
wasp_params = project_service.GetProjectWaspParams()
print(wasp_params)

# Printing all descriptions and their associated IDs
for i, d in enumerate(wasp_params.WaspParams.TApiWaspConfItem):
    print(i, d.Description, d.ID)

# modifying the heat flux over over land
for conf in wasp_params.WaspParams.TApiWaspConfItem:
    if conf['ID'] == 'OFSLAND':
        print(conf)
        conf.Value = -70

for conf in wasp_params.WaspParams.TApiWaspConfItem:
    if conf['ID'] == 'OFSLAND':
        print(conf)

# Setting the values in windPRO
project_service.SetProjectWaspParams(params=wasp_params)

# Confirming that the WAsP paramter has been set correctly.
wasp_params_new = project_service.GetProjectWaspParams()
for conf in wasp_params_new.WaspParams.TApiWaspConfItem:
    if conf['ID'] == 'OFSLAND':
        print(conf)

scalar_service.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

_windproapi = WindProApi()
_windproapi.start_windpro_random_port()

# Makiing example
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'Scalar Service')
os.makedirs(working_dir, exist_ok=True)
project_path = os.path.join(working_dir, 'Scalar Example.w35p')

# Services
scaler_service = _windproapi.get_service('ScalerService')
online_data_service = _windproapi.get_service('OnlineDataService')
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
eng_forest_service = _windproapi.get_service('EngForestService')
obj_elevation_grid_service = _windproapi.get_service('ObjElevationGridService')
obj_site_data_service = _windproapi.get_service('ObjSiteDataService')
factory = _windproapi.get_factory('WindproService')


# Create a WindPRO project in a given location
lng = 13
lat = 57
project_service.NewProject(lng, lat, project_path)

# Getting the defaul scalars
default_scalars = scaler_service.GetScalers()
print(default_scalars)

# Adding forest layers
forest_layer = objects_service.AddLayer('Forest data', 0)
terrain_data_type = 'ODSObjHeightsGrid'
online_data_service.PrepareService(terrain_data_type, lat, lng)
available_data = online_data_service.GetServices(terrain_data_type)
handle_forest = online_data_service.DownloadHeightData(terrain_data_type, 'SLU_FOREST_TREEHEIGHTS_ServiceID',
                                                       'Forest Data', lat, lng, 10000, 10000, False)
elevation_grid_obj = obj_elevation_grid_service.GetElevationGridObject(handle_forest)

# Getting roughness and terrain data
terrain_layer = objects_service.AddLayer('Terrain data', 0)
objects_service.SetEditLayer(terrain_layer)

terrain_data_type = 'ODSTerrainGrid'
online_data_service.PrepareService(terrain_data_type, lat, lng)
terrain_services = online_data_service.GetServices(terrain_data_type)
# Choosing a specific dataset and downloading the data
terrain_service_id = 'MetitDEM_Grid'
terrain_handle = online_data_service.DownloadHeightData(terrain_data_type, terrain_service_id, 'Terrain data grid',
                                                        lat, lng, 20000, 20000, True)

# Getting roughness dataset sets
roughness_data_type = 'ODSRoughnessGridToLine'
online_data_service.PrepareService(roughness_data_type, lat, lng)
roughness_service = online_data_service.GetServices(roughness_data_type)
# Choosing a specific roughness dataset and downloading it
roughness_service_id = 'DataService_Rou_grid_GlobCover'
roughness_handle = online_data_service.DownloadRoughnessData(roughness_data_type, roughness_service_id,
                                                             'Roughness data', lat, lng, 20000, 20000)

# Adding the displacement height calculation
newEngforestName = 'Displacement height from scripting'
newEngforest = eng_forest_service.AddEngForest(newEngforestName)
newEngforest.HeightGridObjectHandle = handle_forest
newEngforest.InputType = 'fApihitHeightGrid'
newEngforest.HeightGridObjectLayerId = elevation_grid_obj.GridToHcSetup.Items.TApiGridToHCSetupItem[0].LayerID
nan_to_skipvalue(newEngforest)
eng_forest_service.SetEngForest(newEngforest)

# Adding a site data object
terrain_layer = objects_service.AddLayer('Site Data Objects', 0)
objects_service.SetEditLayer(terrain_layer)

# Statistical wind climate with a Stat Gen Calculation
obj_site_data_statgen = objects_service.AddObject('SiteData', lat, lng, 'STATGEN SiteData')
obj_site_data_service.SetPurpose(obj_site_data_statgen.Handle, 'SdPurpStatgen')
# Connecting elevation and roughness data and
obj_site_data_service.TerrainLinkElevationAndRoughnessLine(obj_site_data_statgen.Handle, terrain_handle,
                                                           roughness_handle)
# Getting an update of the changes that have been made to the object back to python
obj_site_data_statgen = obj_site_data_service.GetSiteDataObject(obj_site_data_statgen.Handle)

# Making a scalar that uses the engineering forest height
scaler = scaler_service.AddScaler('Engineering Forest Model')

# Adding the displacement height model
scaler.DisplCalcHandle = newEngforest.Handle
scaler.DisplType = 'ScalerDisplTypeCalc'

# Scaling type is from Mast measurement data
scaler.ScalerType = 'ScalerTypeMastScalingGeo'

# Adding site data and with it the link terrain and roughness data
scaler.SiteDataHandle = obj_site_data_statgen.Handle

nan_to_skipvalue(scaler)
scaler_service.SetScaler(scaler)

scaler_service.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

_windproapi = WindProApi()
_windproapi.start_windpro_random_port()

# Makiing example
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'Scalar Service')
os.makedirs(working_dir, exist_ok=True)
project_path = os.path.join(working_dir, 'Scalar Example.w41p')

# Services
scaler_service = _windproapi.get_service('ScalerService')
online_data_service = _windproapi.get_service('OnlineDataService')
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
eng_forest_service = _windproapi.get_service('EngForestService')
obj_elevation_grid_service = _windproapi.get_service('ObjElevationGridService')
obj_site_data_service = _windproapi.get_service('ObjSiteDataService')
factory = _windproapi.get_factory('WindproService')


# Create a WindPRO project in a given location
lng = 13
lat = 57
project_service.NewProject(lng=lng, lat=lat, filename=project_path)

# Getting the defaul scalars
default_scalars = scaler_service.GetScalers()
print(default_scalars)

# Adding forest layers
forest_layer = objects_service.AddLayer(layerName='Forest data', parentFolderHandle=0)
terrain_data_type = 'ODSObjHeightsGrid'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
available_data = online_data_service.GetServices(dataType=terrain_data_type)
handle_forest = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                       implId='SLU_FOREST_TREEHEIGHTS_ServiceID',
                                                       userDesc='Forest Data',
                                                       lat=lat,
                                                       lon=lng,
                                                       width=10000,
                                                       height=10000,
                                                       useAsTin=False)
elevation_grid_obj = obj_elevation_grid_service.GetElevationGridObject(handle=handle_forest)

# Getting roughness and terrain data
terrain_layer = objects_service.AddLayer(layerName='Terrain data', parentFolderHandle=0)
objects_service.SetEditLayer(handle=terrain_layer)

terrain_data_type = 'ODSTerrainGrid'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
terrain_services = online_data_service.GetServices(dataType=terrain_data_type)
# Choosing a specific dataset and downloading the data
terrain_service_id = 'SWE50_Grid'
terrain_handle = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                        implId=terrain_service_id,
                                                        userDesc='Terrain data grid',
                                                        lat=lat,
                                                        lon=lng,
                                                        width=20000,
                                                        height=20000,
                                                        useAsTin=True)

# Getting roughness dataset sets
roughness_data_type = 'ODSRoughnessGridToLine'
online_data_service.PrepareService(dataType=roughness_data_type, lat=lat, lon=lng)
roughness_service = online_data_service.GetServices(dataType=roughness_data_type)
# Choosing a specific roughness dataset and downloading it
roughness_service_id = 'DataService_Rou_grid_GlobCover'
roughness_handle = online_data_service.DownloadRoughnessData(dataType=roughness_data_type,
                                                             implId=roughness_service_id,
                                                             userDesc='Roughness data',
                                                             lat=lat,
                                                             lon=lng,
                                                             width=20000,
                                                             height=20000)

# Adding the displacement height calculation
newEngforestName = 'Displacement height from scripting'
newEngforest = eng_forest_service.AddEngForest(name=newEngforestName)
newEngforest.HeightGridObjectHandle = handle_forest
newEngforest.InputType = 'fApihitHeightGrid'
newEngforest.HeightGridObjectLayerId = elevation_grid_obj.GridToHcSetup.Items.TApiGridToHCSetupItem[0].LayerID
nan_to_skipvalue(newEngforest)
eng_forest_service.SetEngForest(engForest=newEngforest)

# Adding a site data object
terrain_layer = objects_service.AddLayer(layerName='Site Data Objects', parentFolderHandle=0)
objects_service.SetEditLayer(handle=terrain_layer)

# Statistical wind climate with a Stat Gen Calculation
obj_site_data_statgen = objects_service.AddObject(apiObjType='SiteData',
                                                  lat=lat,
                                                  lng=lng,
                                                  userDesc='STATGEN SiteData')
obj_site_data_service.SetPurpose(handle=obj_site_data_statgen.Handle, purpose='SdPurpStatgen')
# Connecting elevation and roughness data and
obj_site_data_service.TerrainLinkElevationAndRoughnessLine(handle=obj_site_data_statgen.Handle,
                                                           elevHandle=terrain_handle,
                                                           rouLineHandle=roughness_handle)
# Getting an update of the changes that have been made to the object back to python
obj_site_data_statgen = obj_site_data_service.GetSiteDataObject(obj_site_data_statgen.Handle)

# Making a scalar that uses the engineering forest height
scaler = scaler_service.AddScaler(name='Engineering Forest Model')

# Adding the displacement height model
scaler.DisplCalcHandle = newEngforest.Handle
scaler.DisplType = 'ScalerDisplTypeCalc'

# Scaling type is from Mast measurement data
scaler.ScalerType = 'ScalerTypeMastScalingGeo'

# Adding site data and with it the link terrain and roughness data
scaler.SiteDataHandle = obj_site_data_statgen.Handle

nan_to_skipvalue(scaler)
scaler_service.SetScaler(scaler=scaler)

windpro_service.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi import WindProApi
import time

# WindProApi instance for interacting with windPRO scripting
_windproapi = WindProApi()

# Starting windPRO
_windproapi.start_windpro_random_port()

# open a windPRO service to interact with windPRO
windpro_service = _windproapi.get_service('WindproService')

# Accessing meta information about the windPRO version etc.
version = windpro_service.GetVersion()
print(version)

in_demo = windpro_service.GetIsInDemo()
print(in_demo)

act_lines = windpro_service.GetActivationLines()
print(act_lines)

# Coordinate system operations like changing between coordinate systems of different EPSG numbers
utm_coords = windpro_service.ConvertCoorEPSG(x=10.,
                                             y=55.,
                                             inEpsg=4326,
                                             toEpsg=32632)
print('Easting: {:.1f}m, Northing: {:.1f}m'.format(utm_coords.X, utm_coords.Y))

# Getting more coordinate system information from the EPSG number
coord_sys_4326 = windpro_service.GetCoorSysFromEPSG(epsg=4326)
coord_sys_32632 = windpro_service.GetCoorSysFromEPSG(epsg=32632)

# Converting from latitude/longitude  using this for coordinate transformations
converted_coords = windpro_service.ConvertCoor(x=10., y=55., fromCoorSys=coord_sys_4326, toCoorSys=coord_sys_32632)
# Converted coordinates are equivalent to result from ConvertCoorEPSG
print('Easting: {:.1f}m, Northing: {:.1f}m'.format(utm_coords.X, utm_coords.Y))

time.sleep(10)

# Closing windPRO
_windproapi.close_windpro()

wtgexplorerservice_service.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from pprint import pprint
from windproapi.utils import get_windpro_sample_path
from windproapi import nan_to_skipvalue
from windproapi import WindProApi

# Opening windPRO
_windproapi = WindProApi()
_windproapi.start_windpro_random_port()

wtg_explorer_service = _windproapi.get_service('WtgExplorerService')
obj_wtg_service = _windproapi.get_service('ObjWtgService')
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
windpro_service = _windproapi.get_service('WindproService')

working_dir = os.path.join(get_windpro_sample_path('4.1'), 'New Salem', '4.1')
project_path = os.path.join(working_dir, 'New Salem.w41p')

# Loading New Salem project
project_service.LoadFromFile(filename=project_path)

# Wind turbine Data Catalogue access
all_manufacturers = wtg_explorer_service.GetManufactorList()
pprint(all_manufacturers)

# Total number of turbines
N_wtgexpl = wtg_explorer_service.GetWtgCount()
print(f'{N_wtgexpl} wind turbines in the catalogue')

# Filtering data
wtgs = wtg_explorer_service.GetWtgsWithFilter(manufactor='VESTAS',
                                              minRatedPower=2000,
                                              maxRatedPower=2500,
                                              minHubHeight=0,
                                              maxHubHeight=1000,
                                              minRotorDiameter=0,
                                              maxRotorDiameter=1000)
print(wtgs)

# Getting a single wind turbine from a unique identifier.
# Preferrable to using the index if data base is updated and more turbines added
wtg = wtg_explorer_service.GetWtgFromUID(uid=wtgs[0].UID)

# Finding a turbines by its user label
objs = objects_service.GetObjects(apiObjType='NewWTG')
wtg_T40 = [o for o in objs if o.UserLabel == 'T40'][0]

# Doing coordinate transformations to find the coordiates for moving a turbine 500 m.
# Get the UTM zone of the current project
utm_zone = project_service.GetUtmWgs84EpsgForProject()
# Convert data from EPSG 4326, the coordinate system used internall in windPRO to utm
utm_coords_T40 = windpro_service.ConvertCoorEPSG(x=wtg_T40.Lng,
                                                 y=wtg_T40.Lat,
                                                 inEpsg=4326,
                                                 toEpsg=utm_zone)

# Converting UTM coordinates with an offset to EPSG 4326 to add to the objects in windPRO
new_wtg_coord = windpro_service.ConvertCoorEPSG(x=utm_coords_T40.X + 500,
                                                y=utm_coords_T40.Y,
                                                inEpsg=utm_zone,
                                                toEpsg=4326)

# Adding the new objects. Be careful latitude is stored in Y and longitude in X
new_obj = objects_service.AddObject(apiObjType='NewWTG',
                                    lat=new_wtg_coord.Y,
                                    lng=new_wtg_coord.X,
                                    userDesc='T100')
wtgObj = obj_wtg_service.GetWtgObject(new_obj.Handle)
wtgObj.UserLabel = 'T100'

# Adding file path found from the wind turbine catalogue
wtgObj.Filename = wtg.FileName
# Hub height needs to be given explicitely
wtgObj.Hubheight = wtg.DefHubHeight
# Needed to handle None values when communicating with zeep
nan_to_skipvalue(wtgObj)
# Set everything back into the wtg object
obj_wtg_service.SetWtgObject(wtgObj)

decibel_shadow_zvi_calculation.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
import numpy as np
from windproapi import WindProApi, nan_to_skipvalue
from windproapi import compare_objects
from windproapi.utils import get_windpro_sample_path
from windproapi import compare_objects


def get_obj_from_list(objs, variable, value):
    l_obj = []
    for o in objs:
        if o[variable] == value:
            l_obj.append(o)
    if len(l_obj) == 0:
        raise ValueError(f'Could not find {value} in any {variable}.')
    elif len(l_obj) == 1:
        return l_obj[0]
    else:
        raise ValueError(f'More than one variable {variable} has value {value}.')


_windproapi = WindProApi()
_windproapi.start_windpro_random_port()

# This folder needs to exist on your machine
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'Environmental')
os.makedirs(working_dir, exist_ok=True)
project_path = os.path.join(working_dir, 'Environmental.w41p')

# Get to all services
online_data_service = _windproapi.get_service('OnlineDataService')
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
calculation_service = _windproapi.get_service('CalculationService')
obj_wtg_service = _windproapi.get_service('ObjWtgService')
windpro_service = _windproapi.get_service('WindproService')
calc_ZVI_service = _windproapi.get_service('CalcZVIService')
wtg_explorer_service = _windproapi.get_service('WtgExplorerService')
obj_elevation_grid_service = _windproapi.get_service('ObjElevationGridService')
calc_decibel_service = _windproapi.get_service('CalcDecibelService')
obj_NSA_service = _windproapi.get_service('ObjNSAService')
calc_shadow_service = _windproapi.get_service('CalcShadowService')
factory = _windproapi.get_factory('WindproService')

# Position of the project
lng = 13
lat = 57

# Wind Farm locations in m from the site center
wtg_easting = [-100 + (i % 5) * 1000 for i in range(20)]
wtg_northing = [-4000 + np.floor(i / 5) * 1000 + (i % 5) * 100 for i in range(20)]

# Make a new project
project_service.NewProject(lng=lng, lat=lat, filename=project_path)

# Setting the project coordinate system to UTM
utm_epsg = project_service.GetUtmWgs84EpsgForProject()
project_service.SetProjectCoorEPSG(epsg=utm_epsg)

# Map data #
# Adding a layer for terrain data
layer_terrain = objects_service.AddLayer(layerName='Terrain data', parentFolderHandle=0)
objects_service.SetEditLayer(handle=layer_terrain)

# Getting elevation data sets
terrain_data_type = 'ODSTerrainGrid'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
terrain_services = online_data_service.GetServices(dataType=terrain_data_type)
print(terrain_services)
# Choosing a specific dataset and downloading the data
terrain_service_id = 'SWE50_Grid'
terrain_handle = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                        implId=terrain_service_id,
                                                        userDesc='Terrain data grid',
                                                        lat=lat,
                                                        lon=lng,
                                                        width=20000,
                                                        height=20000,
                                                        useAsTin=True)

# Adding forest layers
forest_layer = objects_service.AddLayer(layerName='Forest data', parentFolderHandle=0)
terrain_data_type = 'ODSObjHeightsGrid'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
available_data = online_data_service.GetServices(dataType=terrain_data_type)
handle_forest = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                       implId='SLU_FOREST_TREEHEIGHTS_ServiceID',
                                                       userDesc='Forest Data',
                                                       lat=lat,
                                                       lon=lng,
                                                       width=10000,
                                                       height=10000,
                                                       useAsTin=False)

# Adding wind turbines
windfarm_layer = objects_service.AddLayer(layerName='Wind Farm', parentFolderHandle=0)
# Seaching for Vestas turbines. 0 indicate to take all not an actual filtering
wtgs = wtg_explorer_service.GetWtgsWithFilter(manufactor='VESTAS',
                                              minRatedPower=0,
                                              maxRatedPower=0,
                                              minHubHeight=0,
                                              maxHubHeight=0,
                                              minRotorDiameter=0,
                                              maxRotorDiameter=0)
wtg = [w for w in wtgs if 'VESTAS V90 2000 90.0 !O!' == w.DataName][0]

# Converting coordinate systems
utm_zone = project_service.GetUtmWgs84EpsgForProject()
# Converting from latitude/longitude using this for coordinate transformations
utm_center = windpro_service.ConvertCoorEPSG(x=lng,
                                             y=lat,
                                             inEpsg=4326,
                                             toEpsg=utm_zone)
# Converted coordinates are equivalent to result from ConvertCoorEPSG
print('Easting: {:.1f}m, Northing: {:.1f}m'.format(utm_center.X, utm_center.Y))

for i, i_east, i_northing in zip(range(len(wtg_easting)), wtg_easting, wtg_northing):
    position = windpro_service.ConvertCoorEPSG(x=utm_center.X + i_east,
                                               y=utm_center.Y + i_northing,
                                               inEpsg=utm_zone,
                                               toEpsg=4326)
    print('Longitude: {:.3f}, Latitude: {:.3f}'.format(position.X, position.Y))
    new_obj = objects_service.AddObject(apiObjType='NewWTG',
                                        lat=position.Y,
                                        lng=position.X,
                                        userDesc='New WTG obj')
    wtgObj = obj_wtg_service.GetWtgObject(new_obj.Handle)
    wtgObj.Filename = wtg.FileName
    wtgObj.Hubheight = wtg.DefHubHeight
    wtgObj.UserDescription = 'Turbine' + str(i + 1).zfill(2)
    wtgObj.UserLabel = 'Turbine ' + str(i + 1).zfill(2)
    nan_to_skipvalue(wtgObj)
    res = obj_wtg_service.SetWtgObject(wtgObj)

wtgids = factory.TApiWtgIds()
for w in objects_service.GetObjects(apiObjType='NewWTG'):
    dummy = factory.TApiWtgId()
    dummy.Handle = w.Handle
    dummy.Rowindex = 0
    wtgids['TApiWtgId'].append(dummy)

# ZVI calculation
zvi_handle = calculation_service.CreateEmpty(calcType='CalcZVI')

# Open calculation and start setting properties
calculation_service.OpenCalcForEdit(handle=zvi_handle)
calc_zvi = calc_ZVI_service.GetZVICalc()
calc_zvi.Name = 'Script Generated'

# Changing the center of the calculation
calc_zvi.Center.Lat = lat + 0.01
calc_zvi.Center.Lng = lng
calc_zvi.CenterType = 'ApiZVICtUser'

# Setting the size of the calculation area
calc_zvi.Width = 20000
calc_zvi.Height = 20000
calc_zvi.Step = 250

# Adding all existing turbines to the calculation

# Adding the handle for the elevation grid and the forest height map that is to be used
grid_handles_int = factory.TApiObjectHandles()
grid_handles_int.int.append(terrain_handle)
grid_handles_int.int.append(handle_forest)
calc_zvi.GridHandles = grid_handles_int

# Adding all wind turbines
calc_zvi.NewWtgs = wtgids

nan_to_skipvalue(calc_zvi)
calc_ZVI_service.SetZVICalc(calc_zvi)
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=zvi_handle)

# Noise calculations
noise_layer = objects_service.AddLayer(layerName='Noise', parentFolderHandle=0)

# Adding a noise sensitive area that is the neighbouring city
lng_noise = 13.0637
lat_noise = 56.95564
new_obj = objects_service.AddObject(apiObjType='NSA',
                                    lat=lat_noise,
                                    lng=lng_noise,
                                    userDesc='Script added')
nsa_obj = obj_NSA_service.GetNSAObject(new_obj.Handle)

# Adding more points to make it into an area
for lat, lng in zip([56.9637, 56.9582, 56.9417], [13.0680, 13.0891, 13.0733]):
    point = factory.TApiGP()
    point.Lat = lat
    point.Lng = lng
    nsa_obj.Points.TApiGP.append(point)

nsa_obj.FreeDefinable = True
nsa_obj.KindOfDemand = 'kodAbsolute'
nsa_obj.NoiseDemandNew = 43.0
nan_to_skipvalue(nsa_obj)
obj_NSA_service.SetNSAObject(nsa_obj)

# Compare this object to a newly generated object
# This is useful as there are many integers that define the behaviour of the NSA objects
new_obj = objects_service.AddObject(apiObjType='NSA',
                                    lat=lat_noise+0.01,
                                    lng=lng_noise,
                                    userDesc='default script added')
nsa_obj_def = obj_NSA_service.GetNSAObject(new_obj.Handle)
nan_to_skipvalue(nsa_obj_def)
print(compare_objects(nsa_obj_def, nsa_obj))
objects_service.DeleteObject(new_obj.Handle)

# Noise calcualtion generated empty
noise_handle = calculation_service.CreateEmpty(calcType='CalcNoise')

# It is possible to change parameters in the calculation
calculation_service.OpenCalcForEdit(handle=noise_handle)

calc_noise = calc_decibel_service.GetDecibelCalc()
calc_noise.Name = 'Script generated'

# Adding all turbines turbines
calc_noise.NewWtgs = wtgids

# Adding all noise sensitice areas
nsas = factory.TApiWtgIds()
for obj in objects_service.GetObjects(apiObjType='NSA'):
    nsa = factory.TApiWtgId()
    nsa.Handle = obj.Handle
    nsa.Rowindex = -1
    nsas.TApiWtgId.append(nsa)
calc_noise.NSAs = nsas

nan_to_skipvalue(calc_noise)
calc_decibel_service.SetDecibelCalc(calc_noise)
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=noise_handle)

# When setting a calculation type many value of the calculation will automatically change
calculation_service.OpenCalcForEdit(handle=noise_handle)
calc_noise_edited = calc_decibel_service.GetDecibelCalc()

# It is possible to set another calculation type.
# This is only donw by numbers, so it is necessary to try it out in windPRO GUI and use that numeric
# 5 stands for 'Swedish, Jan 2002, Land'
calc_noise_edited.DecibelCalcType = 5
nan_to_skipvalue(calc_noise_edited)
calc_decibel_service.SetDecibelCalc(calc_noise_edited)
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=noise_handle)

# Shadow calculations
# Adding shadow receptors
shadow_layer = objects_service.AddLayer(layerName='Shadow', parentFolderHandle=0)

# Adding two shadow receptors representable for the two near by towns
# Keeping them to default settings fro simplicity
objects_service.AddObject(apiObjType='Shadow', lat=56.971983, lng=13.072742, userDesc='House 1')
objects_service.AddObject(apiObjType='Shadow', lat=56.965227, lng=13.063942, userDesc='House 2')
objects_service.AddObject(apiObjType='Shadow', lat=56.973484, lng=13.061792, userDesc='House 3')

# Creading shadow calcution
shadow_handle = calculation_service.CreateEmpty(calcType='CalcShadow')
calculation_service.OpenCalcForEdit(handle=shadow_handle)
calc_shadow = calc_shadow_service.GetShadowCalc()

# Add settings to a map as well
calc_shadow.Name = 'Script Generated'
calc_shadow.MapCalc = True
calc_shadow.WidthWest = 1000
calc_shadow.WidthEast = 7000
calc_shadow.HeightSouth = 7000
calc_shadow.HeightNorth = 1000

# Adding all shadow receptors to the calculation
receptors = factory.TApiWtgIds()
for obj in objects_service.GetObjects(apiObjType='Shadow'):
    receptor = factory.TApiWtgId()
    receptor.Handle = obj.Handle
    receptor.Rowindex = -1
    receptors.TApiWtgId.append(receptor)
calc_shadow.Receptors = receptors

# Adding all wtg objects
calc_shadow.NewWtgs = wtgids

nan_to_skipvalue(calc_shadow)
calc_shadow_service.SetShadowCalc(calc_shadow)
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=shadow_handle)

mcp_calculation.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
import numpy as np
from windproapi import WindProApi
from windproapi.utils import get_windpro_sample_path
from windproapi import nan_to_skipvalue


def find_closest_height_handle(meteo_object, measurement_height):
    # Determining which height is closest to the selection height
    meteo_height_diffs = []
    meteo_heights = []
    meteo_height_handles = []
    for temp in meteo_object.Heights.TApiMeteoHeight:
        meteo_height_diffs.append(temp.Height - measurement_height)
        meteo_heights.append(temp.Height)
        meteo_height_handles.append(temp.Handle)

    index_min = np.argmin(abs(np.array(meteo_height_diffs)))
    meteo_height_handle = meteo_height_handles[index_min]
    return meteo_height_handle


# Opening windPRO
_windproapi = WindProApi()

# Project file
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'New Salem', '4.1')
project_path = os.path.join(working_dir, 'New Salem.w41p')
_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')
calc_MCP_service = _windproapi.get_service('CalcMCPService')
obj_meteo_service = _windproapi.get_service('ObjMeteoService')
calc_statgen_service = _windproapi.get_service('CalcStatgenService')
obj_site_data_service = _windproapi.get_service('ObjSiteDataService')
factory = _windproapi.get_factory('WindproService')

# Open project file
project_service.LoadFromFile(filename=project_path)

# Making a new MCP calculation
mcp_handle = calculation_service.CreateEmpty(calcType='CalcMCP')
calculation_service.OpenCalcForEdit(handle=mcp_handle)
mcp_calc = calc_MCP_service.GetMCPCalc()

# There is not much in the calculation yet without making a new MCP session
print(mcp_calc)
calc_MCP_service.AddSession()

# With the new session much more information in .Session
mcp_calc = calc_MCP_service.GetMCPCalc()
mcp_calc.Name = 'Generate by scripting'
print(mcp_calc)
mcp_session_handle = mcp_calc.Sessions.TApiMcpSession[0].Handle

# Adding meteorological data
meteo_objs = objects_service.GetObjects(apiObjType='MeteoObjectData')
obj_measurement = [d for d in meteo_objs if d.UserDescription == 'New Salem South'][0]
obj_reference = [d for d in meteo_objs if 'EmdWrf' in d.UserDescription][0]

meteo_measurement = obj_meteo_service.GetMeteoObject(handle=obj_measurement.Handle)
meteo_reference = obj_meteo_service.GetMeteoObject(handle=obj_reference.Handle)

# Getting the handles for the correct heights inside the meteo object
internal_measurement_handle = find_closest_height_handle(meteo_measurement, 60)
internal_reference_handle = find_closest_height_handle(meteo_reference, 60)

ses = mcp_calc.Sessions.TApiMcpSession[0]
ses.Enabled = True
ses.MeasureMeteoHandle = meteo_measurement.Handle
ses.MeasureMeteoUID = internal_measurement_handle
ses.ReferenceMeteoHandle = meteo_reference.Handle
ses.ReferenceMeteoUID = internal_reference_handle

# Site data object for making wind statistics
site_objs = objects_service.GetObjects(apiObjType='SiteData')
sitedata = [d for d in site_objs if d.UserDescription == 'for STATGEN'][0]

# Check that the site data object has the right purpose. As this is for generating wind statistics it needs to be
# SdPurpStatgen
if obj_site_data_service.GetSiteDataObject(sitedata.Handle).Purpose == 'SdPurpStatgen':
    print('Correct purpose for added site data object.')
else:
    raise ValueError('Incorrect purpose for site data objects.')
ses.SGSiteDataHandle = sitedata.Handle

# Adding all MCP models
template_models = calc_MCP_service.GetTemplateModels()
print(template_models)
# All template models have the same handle. Need to use SetMCPCalc to have windPRO assign correct handles
ses.Models.TApiMCPModel = template_models
nan_to_skipvalue(mcp_calc)
calc_MCP_service.SetMCPCalc(mcp_calc)

# Checking the handles for the models.
mcp_calc = calc_MCP_service.GetMCPCalc()
for ses in mcp_calc.Sessions.TApiMcpSession[0].Models.TApiMCPModel:
    print('method: {}, handle:{}'.format(ses.Method, ses.Handle))

# Checking some statistics available in MCP
correlation = calc_MCP_service.GetCorrelationModelInputData(sessionHandle=mcp_session_handle,
                                                            Averaging='aiMonth')
print('Correlation between reference and long term data. Monthly averaged.')
print(correlation)

single_MK_test = calc_MCP_service.GetMeasureAndReference(sessionHandle=mcp_session_handle,
                                                         StartMonth='ApiJan')
print('Information on the long term variability.')
print(single_MK_test)

# Training and testing data. Using 24h alternating slicing
calc_MCP_service.TrainAndTest(mcp_session_handle, 'Api24Hours')

# Getting updated statistics on wind speed and energy for hourly averaging
statistics_24h_speed = calc_MCP_service.UpdateStatistics(mcp_session_handle, 'saHour', 'satWindSpeed',
                                                        'residualsDefault')

for ds in statistics_24h_speed:
    print(f'{ds.Method:>25}: WS measured concurrent {ds.MeasuredMeanWindSpeedConcurrent:2.3f}, WS predicted concurrent '
          f'{ds.PredictedMeanWindSpeedConcurrent:2.3f}, WS long term {ds.LongTermMeanWindSpeed:2.3f}')

# Setting to 3 (the neural network model)
mcp_calc.Sessions.TApiMcpSession[0].Handle = 3

# Close and calculate
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=mcp_calc.Handle)

# Using MCP to make a new longer time series in the meteoobject for the measurement
# For this the calculationg needs to be open, since this calculation is started from within the GUI of the calculation
# in windPRO
calculation_service.OpenCalcForEdit(handle=mcp_handle)
mcp_calc = calc_MCP_service.GetMCPCalc()
calc_MCP_service.PredictToMeteo(sessionHandle=mcp_calc.Sessions.TApiMcpSession[0].Handle,
                                modelHandle=3,
                                inMeasMeteo=True,
                                newName='Scripting Long Term Corrected')

# Making wind statistics
calc_MCP_service.PredictToWindStat(sessionHandle=mcp_calc.Sessions.TApiMcpSession[0].Handle,
                                   modelHandle=3,
                                   wsFnName=os.path.join(working_dir, 'wind_stat_MCP.wws'))
calculation_service.CloseCalc(save=True)

park_calculation.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
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()

working_dir = os.path.join(get_windpro_sample_path('4.1'), 'New Salem', '4.1')
project_path = os.path.join(working_dir, 'New Salem.w41p')

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

park_calculation_marginal_AEP.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

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('4.1'), 'New Salem', '4.1')
project_path = os.path.join(working_dir, 'New Salem.w41p')

_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)
# park_calc.UseCurtailment = True
park_calc.TimevarData.Aggregate = 0
nan_to_skipvalue(park_calc)
calc_park_service.SetParkCalc(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], encoding='ISO-8859-1')
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}, encoding = 'ISO-8859-1')

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

park_calculation_resource.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
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()

working_dir = os.path.join(get_windpro_sample_path('4.1'), 'New Salem', '4.1')
project_path = os.path.join(working_dir, 'New Salem.w41p')

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

# Opening calculation
calculation_service.OpenCalcForEdit(handle=handle_park)
park_calc = calc_park_service.GetParkCalc()

park_calc.Name = 'resource based from scripting'
park_calc.ParkCalcType = 'ParkTypeResource'

# Making right datatype for setting rsf files
rsf_file_names = factory.TApiStringArray()
rsf_file_names.string = os.path.join(working_dir, 'New Salem_Res_100_Hub_100.0_50.0_80.0_0.rsf')
park_calc.StatData.RSFilenames = rsf_file_names
nan_to_skipvalue(park_calc)
calc_park_service.SetParkCalc(park_calc)

calculation_service.CloseCalc(save=True)
calculation_service.Calculate(park_calc.Handle)

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

park_curtailment.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
import numpy as np
from windproapi import WindProApi, nan_to_skipvalue
from windproapi import compare_objects
from windproapi.utils import get_windpro_sample_path
from datetime import datetime

def get_obj_from_list(objs, variable, value):
    l_obj = []
    for o in objs:
        if o[variable] == value:
            l_obj.append(o)
    if len(l_obj) == 0:
        raise ValueError(f'Could not find {value} in any {variable}.')
    elif len(l_obj) == 1:
        return l_obj[0]
    else:
        raise ValueError(f'More than one variable {variable} has value {value}.')


_windproapi = WindProApi()
_windproapi.start_windpro_random_port()

# This folder needs to exist on your machine
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'ParkCurtailment')
os.makedirs(working_dir, exist_ok=True)
project_path = os.path.join(working_dir, 'ParkCurtailment.w41p')

# Get to all services
online_data_service = _windproapi.get_service('OnlineDataService')
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
calculation_service = _windproapi.get_service('CalculationService')
obj_wtg_service = _windproapi.get_service('ObjWtgService')
windpro_service = _windproapi.get_service('WindproService')
calc_ZVI_service = _windproapi.get_service('CalcZVIService')
wtg_explorer_service = _windproapi.get_service('WtgExplorerService')
obj_elevation_grid_service = _windproapi.get_service('ObjElevationGridService')
calc_decibel_service = _windproapi.get_service('CalcDecibelService')
obj_NSA_service = _windproapi.get_service('ObjNSAService')
calc_shadow_service = _windproapi.get_service('CalcShadowService')
calc_park_service = _windproapi.get_service('CalcParkService')
obj_site_data_service = _windproapi.get_service('ObjSiteDataService')
scaler_service = _windproapi.get_service('ScalerService')
factory = _windproapi.get_factory('WindproService')

# Position of the project
lng = 13
lat = 57

# Wind Farm locations in m from the site center
wtg_easting = [2500 + (i % 5) * 500 for i in range(20)]
wtg_northing = [-4000 + np.floor(i / 5) * 500 + (i % 5) * 100 for i in range(20)]

# Make a new project
project_service.NewProject(lng=lng, lat=lat, filename=project_path)

# Setting the project coordinate system to UTM
utm_epsg = project_service.GetUtmWgs84EpsgForProject()
project_service.SetProjectCoorEPSG(epsg=utm_epsg)

# Map data #
# Adding a layer for terrain data
layer_terrain = objects_service.AddLayer(layerName='Terrain data', parentFolderHandle=0)
objects_service.SetEditLayer(handle=layer_terrain)

# Getting elevation data sets
terrain_data_type = 'ODSTerrainGrid'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
terrain_services = online_data_service.GetServices(dataType=terrain_data_type)
print(terrain_services)
# Choosing a specific dataset and downloading the data
terrain_service_id = 'SWE50_Grid'
terrain_handle = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                        implId=terrain_service_id,
                                                        userDesc='Terrain data grid',
                                                        lat=lat,
                                                        lon=lng,
                                                        width=20000,
                                                        height=20000,
                                                        useAsTin=True)

# Adding forest layers
forest_layer = objects_service.AddLayer(layerName='Forest data', parentFolderHandle=0)
terrain_data_type = 'ODSObjHeightsGrid'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
available_data = online_data_service.GetServices(dataType=terrain_data_type)
handle_forest = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                       implId='SLU_FOREST_TREEHEIGHTS_ServiceID',
                                                       userDesc='Forest Data',
                                                       lat=lat,
                                                       lon=lng,
                                                       width=10000,
                                                       height=10000,
                                                       useAsTin=False)

# Adding roughness layer
riughness_layer = objects_service.AddLayer(layerName='Forest data', parentFolderHandle=0)
roughness_data_type = 'ODSRoughnessGridToLine'
online_data_service.PrepareService(dataType=roughness_data_type, lat=lat, lon=lng)
roughness_service = online_data_service.GetServices(dataType=roughness_data_type)
roughness_handle = online_data_service.DownloadRoughnessData(dataType=roughness_data_type,
                                                             implId='DataService_Rou_grid_GlobCover',
                                                             userDesc='Roughness data',
                                                             lat=lat,
                                                             lon=lng,
                                                             width=30000,
                                                             height=30000)

meteo_layer = objects_service.AddLayer(layerName='Meteo', parentFolderHandle=0)
# Getting data for an observed wind climate. This is form the list of above
dataType = 'ODSClimate'
online_data_service.PrepareService(dataType=dataType, lat=lat, lon=lng)
# The GetService returns a list with dertails about all available data.
meteo_services = online_data_service.GetServices(dataType=dataType)
# Use the service ID to download the data
print(meteo_services)
meteo_handles = online_data_service.DownloadMeteoData(implId='sicerra',
                                                      lat=lat,
                                                      lon=lng,
                                                      maxDist=30000,
                                                      numPoints=1,
                                                      fromYear=2020,
                                                      toYear=2020)

# Adding a site data object
siteData = objects_service.AddObject(apiObjType='SiteData',
                                     lat=lat,
                                     lng=lng,
                                     userDesc='STATGEN SiteData from scripting')
obj_site_data_service.SetPurpose(handle=siteData.Handle, purpose='SdPurpStatgen')
obj_site_data_service.TerrainLinkElevationAndRoughnessLine(handle=siteData.Handle,
                                                           elevHandle=terrain_handle,
                                                           rouLineHandle=roughness_handle)




# Adding wind turbines
windfarm_layer = objects_service.AddLayer(layerName='Wind Farm', parentFolderHandle=0)
# Seaching for Vestas turbines. 0 indicate to take all not an actual filtering
wtgs = wtg_explorer_service.GetWtgsWithFilter(manufactor='VESTAS',
                                              minRatedPower=0,
                                              maxRatedPower=0,
                                              minHubHeight=0,
                                              maxHubHeight=0,
                                              minRotorDiameter=0,
                                              maxRotorDiameter=0)
wtg = [w for w in wtgs if 'VESTAS V90 2000 90.0 !O!' == w.DataName][0]



# Converting coordinate systems
utm_zone = project_service.GetUtmWgs84EpsgForProject()
# Converting from latitude/longitude using this for coordinate transformations
utm_center = windpro_service.ConvertCoorEPSG(x=lng,
                                             y=lat,
                                             inEpsg=4326,
                                             toEpsg=utm_zone)
# Converted coordinates are equivalent to result from ConvertCoorEPSG
print('Easting: {:.1f}m, Northing: {:.1f}m'.format(utm_center.X, utm_center.Y))

for i, i_east, i_northing in zip(range(len(wtg_easting)), wtg_easting, wtg_northing):
    position = windpro_service.ConvertCoorEPSG(x=utm_center.X + i_east,
                                               y=utm_center.Y + i_northing,
                                               inEpsg=utm_zone,
                                               toEpsg=4326)
    print('Longitude: {:.3f}, Latitude: {:.3f}'.format(position.X, position.Y))
    new_obj = objects_service.AddObject(apiObjType='NewWTG',
                                        lat=position.Y,
                                        lng=position.X,
                                        userDesc='New WTG obj')
    wtgObj = obj_wtg_service.GetWtgObject(new_obj.Handle)
    wtgObj.Filename = wtg.FileName
    wtgObj.Hubheight = wtg.DefHubHeight
    wtgObj.UserDescription = 'Turbine' + str(i + 1).zfill(2)
    wtgObj.UserLabel = 'Turbine ' + str(i + 1).zfill(2)
    nan_to_skipvalue(wtgObj)
    res = obj_wtg_service.SetWtgObject(wtgObj)

wtgids = factory.TApiWtgIds()
for w in objects_service.GetObjects(apiObjType='NewWTG'):
    dummy = factory.TApiWtgId()
    dummy.Handle = w.Handle
    dummy.Rowindex = 0
    wtgids['TApiWtgId'].append(dummy)


# Adding two shadow receptors representable for the two near by towns
# Keeping them to default settings fro simplicity
objects_service.AddObject(apiObjType='Shadow', lat=56.971983, lng=13.072742, userDesc='House 1')
objects_service.AddObject(apiObjType='Shadow', lat=56.965227, lng=13.063942, userDesc='House 2')
objects_service.AddObject(apiObjType='Shadow', lat=56.973484, lng=13.061792, userDesc='House 3')

# Creading shadow calcution
shadow_handle = calculation_service.CreateEmpty(calcType='CalcShadow')
calculation_service.OpenCalcForEdit(handle=shadow_handle)
calc_shadow = calc_shadow_service.GetShadowCalc()

# Add settings to a map as well
calc_shadow.Name = 'Script Generated'
calc_shadow.MapCalc = True
calc_shadow.WidthWest = 1000
calc_shadow.WidthEast = 7000
calc_shadow.HeightSouth = 7000
calc_shadow.HeightNorth = 1000

# Adding all shadow receptors to the calculation
receptors = factory.TApiWtgIds()
for obj in objects_service.GetObjects(apiObjType='Shadow'):
    receptor = factory.TApiWtgId()
    receptor.Handle = obj.Handle
    receptor.Rowindex = -1
    receptors.TApiWtgId.append(receptor)
calc_shadow.Receptors = receptors

# Adding all wtg objects
calc_shadow.NewWtgs = wtgids

nan_to_skipvalue(calc_shadow)
calc_shadow_service.SetShadowCalc(calc_shadow)
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=shadow_handle)

# Saving the shadow calculations
file_shadow_periods = os.path.join(working_dir, 'shadow_calendar', 'shadow_period.txt')
os.makedirs(os.path.dirname(file_shadow_periods), exist_ok=True)
calculation_service.ResultToFile(shadow_handle, 'Periods per WTG', file_shadow_periods)

# Turbine in the shadow caluculation dictionary
wtg_dict = {objects_service.GetObjectFromHandle(d['Handle'])['UserDescription']:d['Handle'] for d in calc_shadow.NewWtgs.TApiWtgId}

# Reading the shadow calculations
# You might need to change the format here to match what your file is
import pandas as pd
df = pd.read_csv(file_shadow_periods, sep='\t')
df['From'] = pd.to_datetime(df['From'], format='mixed') #, format='%Y-%m-%d %H:%M:%S'
df['To'] = pd.to_datetime(df['To'], format='mixed') #, format='Y-%m-%d %H:%M:%S')

# Function for changing the date time into format for windPRO (same as excel)
def excel_date(date1):
    temp = datetime(1899, 12, 30)    # Note, not 31st Dec but 30th!
    delta = date1 - temp
    return float(delta.days) + (float(delta.seconds) / 86400)

# Looping through the
for unique_wtg_str in df['WTG'].unique():
    handle_temp = None
    for key in wtg_dict:
        if key in unique_wtg_str:
            if handle_temp is None:
                handle_temp = wtg_dict[key]
            else:
                ValueError(f'Turbine {unique_wtg_str} in shadow result to file not unique.')

    df_temp = df[df['WTG']==unique_wtg_str]
    wtgReducedMode = obj_wtg_service.GetWtgObject(handle_temp)

    # Looping through all curtailment conditions
    TApiWtgCurtailments = factory.TApiWtgCurtailments()
    for i, row in df_temp.iterrows():
        newCurtailment = obj_wtg_service.GetCurtailmentTemplate('ApiCTOther')
        newCurtailment.Name = f'Shadow from {row["From"]} to {row["To"]}'

        # Making conditions
        condition = factory.TApiWtgCurtailmentCondition()
        condition.CurtailmentCondition = 'ApiCCDateTime'
        # Using the excel date time definition
        condition.ConditionFrom = excel_date(row['From'])
        condition.ConditionTo = excel_date(row['To'])

        TApiWtgCurtailmentConditions = factory.TApiWtgCurtailmentConditions()
        TApiWtgCurtailmentConditions.TApiWtgCurtailmentCondition.append(condition)

        newCurtailment.CurtailmentConditions = TApiWtgCurtailmentConditions
        TApiWtgCurtailments.TApiWtgCurtailment.append(newCurtailment)
    wtgReducedMode.Curtailment = TApiWtgCurtailments

    nan_to_skipvalue(wtgReducedMode)
    obj_wtg_service.SetWtgObject(wtgReducedMode)

# 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 = 'ParkTypeTimeVaryingMeasure'
park_calc.WakeModelType = 'ParkWakeModelNOJensenPark2'
park_calc.Name = 'With Curtailment{}'.format(datetime.today().strftime('%Y-%m-%d %H:%M:%S'))

# Turn on Curtailment
park_calc.UseCurtailment = True

# Meteo objects setups
TApiScalerMetItems = factory.TApiScalerMetItems()
TApiScalerMetItem = factory.TApiScalerMetItem()
MeteoSignalUIDs = factory.TApiObjectHandles()
MeteoShearSignalUIDs = factory.TApiObjectHandles()
MeteoSignalUIDs.int.append(1)
MeteoSignalUIDs.int.append(2)
MeteoShearSignalUIDs.int.append(1)
MeteoShearSignalUIDs.int.append(2)
TApiScalerMetItem.MeteoHandle = meteo_handles[0]
TApiScalerMetItem.MeteoSignalUIDs = MeteoSignalUIDs
TApiScalerMetItem.MeteoShearSignalUIDs = MeteoShearSignalUIDs
TApiScalerMetItems.TApiScalerMetItem.append(TApiScalerMetItem)
park_calc.TimevarData.ScalingData.ScalerMetItems = TApiScalerMetItems

# Scaling setup
default_scalars = scaler_service.GetScalers()
mast_scaler = get_obj_from_list(default_scalars, 'Name', 'EMD Default Measurement Mast Scaler')
park_calc.TimevarData.ScalingData.ScalerHandle = mast_scaler.Handle
park_calc.TimevarData.ScalingData.MaxWeight = 1.0
park_calc.TimevarData.ScalingData.DistancePower = 2.0

# 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)

statgen_resgen_calculation.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
import numpy as np
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

_windproapi = WindProApi()
_windproapi.start_windpro_random_port()

# This folder needs to exist on your machine
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'Resources')
os.makedirs(working_dir, exist_ok=True)
project_path = os.path.join(working_dir, 'Resources.w41p')

# Get to all services
online_data_service = _windproapi.get_service('OnlineDataService')
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
calculation_service = _windproapi.get_service('CalculationService')
calc_statgen_service = _windproapi.get_service('CalcStatgenService')
calc_park_service = _windproapi.get_service('CalcParkService')
obj_site_data_service = _windproapi.get_service('ObjSiteDataService')
obj_meteo_service = _windproapi.get_service('ObjMeteoService')
obj_wtg_service = _windproapi.get_service('ObjWtgService')
calc_resource_service = _windproapi.get_service('CalcResourceService')
obj_wtg_areas_service = _windproapi.get_service('ObjWtgAreasService')
obj_elevation_grid_service = _windproapi.get_service('ObjElevationGridService')
windpro_service = _windproapi.get_service('WindproService')
factory = _windproapi.get_factory('WindproService')

# Position of the site center.
lng = 13
lat = 56.8

# Extend of the resgen around in m
east_west_resgen = 10_000
south_north_resgen = 10_000
# Heights used in the resource calculation
heights_resgen = [50, 100]
# resolution of the resource calculation
resolution = 100.0
# measurement height of the mast to be used for the resource calculation
measurement_height = 110.0
# Make a new project
project_service.NewProject(lng=lng, lat=lat, filename=project_path)

# Setting the project coordinate system to UTM
utm_epsg = project_service.GetUtmWgs84EpsgForProject()
project_service.SetProjectCoorEPSG(epsg=utm_epsg)

# Map data #
# Adding a layer for terrain data
terrain_layer = objects_service.AddLayer(layerName='Terrain data', parentFolderHandle=0)
objects_service.SetEditLayer(handle=terrain_layer)

# Getting elevation data sets
terrain_data_type = 'ODSTerrainGrid'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
terrain_services = online_data_service.GetServices(dataType=terrain_data_type)
print(terrain_services)
# Choosing a specific dataset and downloading the data
terrain_service_id = 'SWE50_Grid'
terrain_handle = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                        implId=terrain_service_id,
                                                        userDesc='Terrain data grid',
                                                        lat=lat,
                                                        lon=lng,
                                                        width=30000,
                                                        height=30000,
                                                        useAsTin=True)

# Getting roughness dataset sets
roughness_data_type = 'ODSRoughnessGridToLine'
online_data_service.PrepareService(dataType=roughness_data_type, lat=lat, lon=lng)
roughness_service = online_data_service.GetServices(dataType=roughness_data_type)
print(roughness_service)
# Choosing a specific roughness dataset and downloading it
roughness_service_id = 'DataService_Rou_grid_GlobCover'
roughness_handle = online_data_service.DownloadRoughnessData(dataType=roughness_data_type,
                                                             implId=roughness_service_id,
                                                             userDesc='Roughness data',
                                                             lat=lat,
                                                             lon=lng,
                                                             width=30000,
                                                             height=30000)

# Adding forest layers
forest_layer = objects_service.AddLayer(layerName='Forest data', parentFolderHandle=0)
terrain_data_type = 'ODSObjHeightsGrid'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
available_data = online_data_service.GetServices(dataType=terrain_data_type)
handle_forest = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                       implId='SLU_FOREST_TREEHEIGHTS_ServiceID',
                                                       userDesc='Forest Data',
                                                       lat=lat,
                                                       lon=lng,
                                                       width=30000,
                                                       height=30000,
                                                       useAsTin=False)
elevation_grid_obj = obj_elevation_grid_service.GetElevationGridObject(handle=handle_forest)

# Meteorological data #
# making a new layer for meteodata
meteo_layer = objects_service.AddLayer(layerName='Reference meteo data', parentFolderHandle=0)
objects_service.SetEditLayer(handle=terrain_layer)

# Getting climate data sets
# It is pretended that meso scale data can be used as measurement for simplicity of the script
meteo_data_type = 'ODSClimate'
online_data_service.PrepareService(dataType=meteo_data_type, lat=lat, lon=lng)
meteo_services = online_data_service.GetServices(dataType=meteo_data_type)
print(meteo_services)
# Selecting a specific dataset and downloading it
meteo_service_id = 'siEra5Basic'
meteo_handles = online_data_service.DownloadMeteoData(implId=meteo_service_id,
                                                      lat=lat,
                                                      lon=lng,
                                                      maxDist=30000,
                                                      numPoints=1,
                                                      fromYear=2014,
                                                      toYear=2015)
# Using the Meteo Service to get the
meteo_obj_era5 = obj_meteo_service.GetMeteoObject(handle=meteo_handles[0])

# Determining which height is closest to the selection height
meteo_height_diffs = []
meteo_heights = []
meteo_height_handles = []
for temp in meteo_obj_era5.Heights.TApiMeteoHeight:
    meteo_height_diffs.append(temp.Height - measurement_height)
    meteo_heights.append(temp.Height)
    meteo_height_handles.append(temp.Handle)

index_min = np.argmin(abs(np.array(meteo_height_diffs)))
print('Closest height to {}m foud at {}m.'.format(measurement_height, meteo_heights[index_min]))
meteo_height_handle = meteo_height_handles[index_min]

# Flow modelling #
# Addign a layer of
site_data_layer = objects_service.AddLayer(layerName='Site data objects', parentFolderHandle=0)
objects_service.SetEditLayer(handle=site_data_layer)

# Adding a site data object
obj_site_data_statgen = objects_service.AddObject(apiObjType='SiteData',
                                                  lat=lat,
                                                  lng=lng,
                                                  userDesc='STATGEN for resource')
obj_site_data_service.SetPurpose(handle=obj_site_data_statgen.Handle, purpose='SdPurpStatgen')
# Connecting elevation and roughness data and
obj_site_data_service.TerrainLinkElevationAndRoughnessLine(handle=obj_site_data_statgen.Handle,
                                                           elevHandle=terrain_handle,
                                                           rouLineHandle=roughness_handle)
# Getting an update of the changes that have been made to the object back to python
obj_site_data_statgen = obj_site_data_service.GetSiteDataObject(obj_site_data_statgen.Handle)

# Making wind statistics
# Creating a new statgen calculation calculation and starting to edit it
wws_file = os.path.join(working_dir, 'wind_stat.wws')
statgen_handle = calculation_service.CreateEmpty(calcType='CalcStatgen')
calculation_service.OpenCalcForEdit(handle=statgen_handle)

# Getting the statgen calculation object and adding information on
statgen = calc_statgen_service.GetStatgenCalc()
statgen.Filename = wws_file
statgen.Name = 'Statgen for test'
statgen.StatgenType = 'StatgenSitedata'
statgen.SiteDataHandle = obj_site_data_statgen.Handle
statgen.MeteoHandle = meteo_obj_era5.Handle
statgen.MeteoHeiHandle = meteo_height_handle
nan_to_skipvalue(statgen)
calc_statgen_service.SetStatgenCalc(statgen)
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=statgen_handle)

# Reading generalized wind statistics for illustration
wind_stats = calc_statgen_service.GetWindstatDataFromFile(wws_file)

# Adding a site data object
obj_site_data_resource = objects_service.AddObject(apiObjType='SiteData',
                                                   lat=lat,
                                                   lng=lng,
                                                   userDesc='STATGEN for resource')
obj_site_data_service.SetPurpose(handle=obj_site_data_resource.Handle, purpose='SdPurpResource')
# Connecting elevation and roughness data and
obj_site_data_service.TerrainLinkElevationAndRoughnessLine(handle=obj_site_data_resource.Handle,
                                                           elevHandle=terrain_handle,
                                                           rouLineHandle=roughness_handle)
# Getting an update of the changes that have been made to the object back to python
obj_site_data_resource = obj_site_data_service.GetSiteDataObject(obj_site_data_resource.Handle)
# Setting wind statistigcs
wind_stats_files = factory.TApiSdWindstatFiles()
wind_stats_file = factory.TApiSdWindStatFile()
wind_stats_file.Filename = wws_file
wind_stats_files.TApiSdWindStatFile.append(wind_stats_file)
obj_site_data_resource.WindStatFiles = wind_stats_files
# Setting data back
nan_to_skipvalue(obj_site_data_resource)
obj_site_data_service.SetSiteDataObject(obj_site_data_resource)
obj_site_data_resource = obj_site_data_service.GetSiteDataObject(obj_site_data_resource.Handle)
print(obj_site_data_resource)

# Wind turbine area #
wtg_area_layer = objects_service.AddLayer(layerName='WTG Areas', parentFolderHandle=0)
# Making new WTG area object and getting its parameters
obj = objects_service.AddObject(apiObjType='WTGareas',
                                lat=lat,
                                lng=lng,
                                userDesc='Area for resgen')
wtg_areas_object = obj_wtg_areas_service.GetWtgAreasObject(obj.Handle)

# Paramter holding multiple WTG areas in a formate understandable for windPRO
wtg_areas_object.AreaList = factory.TApiObjWtgAreaList()

# Coordinate transformations
utm_zone = project_service.GetUtmWgs84EpsgForProject()
utm_coord_center = windpro_service.ConvertCoorEPSG(x=lng,
                                                   y=lat,
                                                   inEpsg=4326,
                                                   toEpsg=utm_zone)

# Calculate the coordinates of the corners of the wtg area
coord_wtg_areas = []
temp = [[east_west_resgen / 2, south_north_resgen / 2],
        [-1 * east_west_resgen / 2, south_north_resgen / 2],
        [-1 * east_west_resgen / 2, -1 * south_north_resgen / 2],
        [east_west_resgen / 2, -1 * south_north_resgen / 2]]
for coord in temp:
    coord_wtg_areas.append(windpro_service.ConvertCoorEPSG(x=utm_coord_center.X + coord[0],
                                                           y=utm_coord_center.Y + coord[1],
                                                           inEpsg=utm_zone,
                                                           toEpsg=4326))

# Adding data to the wind turbine area object
wtg_area = factory.TApiObjWtgArea()
wtg_area.Name = 'resgen'
# For the style of the line. See tns:TApiBrushStyle
wtg_area.Hatching = 'ApiBsClear'
# This will be the input to the wtg_are.Points.
wtg_area.Points = factory.TApiGPs()
for coord in coord_wtg_areas:
    newGP = factory.TApiGP()
    newGP.Lat = coord.Y
    newGP.Lng = coord.X
    wtg_area.Points.TApiGP.append(newGP)
# We can add e.g. a buffer zone
wtg_area.BufferZone = 500.0

# Appending the WTG area to the WTG areas. There can in principle be more than one.
nan_to_skipvalue(wtg_area)
wtg_areas_object.AreaList.TApiObjWtgArea.append(wtg_area)

# Giving data back to windPRO
nan_to_skipvalue(wtg_areas_object)
obj_wtg_areas_service.SetWtgAreasObject(wtg_areas_object)

# Adding the resgen calculation #
resoureceHandle = calculation_service.CreateEmpty(calcType='CalcResource')
calculation_service.OpenCalcForEdit(handle=resoureceHandle)
resource_calc = calc_resource_service.GetResourceCalc()

resource_calc.Name = 'Automate Resource Calculation'
resource_calc.ResourceType = 'ResourceTypeSiteData'
resource_calc.SiteDataHandle = obj_site_data_resource.Handle
resource_calc.Resolution = resolution

# setting heights
hub_heights = factory.TApiDoubleArray()
for h in heights_resgen:
    hub_heights.double.append(h)
resource_calc.HubHeights = hub_heights

# Setting areas. Need to be a special datatype.
wtg_obj_handles = factory.TApiObjectHandles()
wtg_obj_handles.int.append(wtg_areas_object.Handle)
resource_calc.WTGAreaHandles = wtg_obj_handles

# Setting resource calculation so that it is ready
resource_calc.Filename = os.path.join(working_dir, 'resource.rsf')
nan_to_skipvalue(resource_calc)
calc_resource_service.SetResourceCalc(resource_calc)

resource_calc = calc_resource_service.GetResourceCalc()
print(resource_calc)

# Calculate resoureces
calculation_service.CloseCalc(save=True)
calculation_service.Calculate(handle=resource_calc.Handle)

trix_calculation.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
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('4.1'), 'Trix')
project_path = os.path.join(working_dir, 'scripring_trix.w41p')

_windproapi.start_windpro_random_port()

# Services
online_data_service = _windproapi.get_service('OnlineDataService')
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
calculation_service = _windproapi.get_service('CalculationService')
calc_trix_service = _windproapi.get_service('CalcTRIXService')
obj_wtg_service = _windproapi.get_service('ObjWtgService')
wtg_explorer_service = _windproapi.get_service('WtgExplorerService')
obj_elevation_grid_service = _windproapi.get_service('ObjElevationGridService')
obj_line_service = _windproapi.get_service('ObjLineService')
factory = _windproapi.get_factory('WindproService')
# Position of the site center.
lng = 10.3
lat = 51.9

# Make a new project
project_service.NewProject(lng=lng, lat=lat, filename=project_path)

# Getting elevation data sets
terrain_data_type = 'ODSTerrainGrid'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
terrain_services = online_data_service.GetServices(dataType=terrain_data_type)
print(terrain_services)
# Choosing a specific dataset and downloading the data
terrain_service_id = 'XDGM200_Grid'
terrain_handle = online_data_service.DownloadHeightData(dataType=terrain_data_type,
                                                        implId=terrain_service_id,
                                                        userDesc='Terrain data grid',
                                                        lat=lat,
                                                        lon=lng,
                                                        width=30000,
                                                        height=30000,
                                                        useAsTin=True)

# Adding some meteo objects
dataType = 'ODSClimate'
online_data_service.PrepareService(dataType=dataType, lat=lat, lon=lng)
meteo_services = online_data_service.GetServices(dataType=dataType)
meteo_handles = online_data_service.DownloadMeteoData(implId='siEra5Basic',
                                                      lat=lat,
                                                      lon=lng,
                                                      maxDist=30000,
                                                      numPoints=1,
                                                      fromYear=2020,
                                                      toYear=2020)

wtg_catalogue_entry = wtg_explorer_service.GetWtgFromUID('{AA13428B-42E2-4160-9BC1-9A6B65EA41BF}')
wtg_from_file = wtg_explorer_service.GetWtgFromFile(wtg_catalogue_entry.FileName, True)

# Adding turbines positions
for i in range(5):
    new_obj = objects_service.AddObject(apiObjType='NewWTG',
                                        lat=lat-i/100,
                                        lng=lng,
                                        userDesc='New WTG obj')
    wtgObj = obj_wtg_service.GetWtgObject(new_obj.Handle)
    wtgObj.Filename = wtg_catalogue_entry.FileName
    wtgObj.UserLabel = f'Scripting added {i}'
    wtgObj.UserDescription = f'Scripting added {i}'
    nan_to_skipvalue(wtgObj)
    obj_wtg_service.SetWtgObject(wtgObj)

handle_calc_trix = calculation_service.CreateEmpty('CalcTRIX')
calculation_service.OpenCalcForEdit(handle=handle_calc_trix)
calc_trix = calc_trix_service.GetTRIXCalc()

# showing properties of calculation.
# By default all turbines and meteoobjects are included
print(calc_trix)

calc_trix.Name = 'Script generation with DGM200 Deutschland'
calculation_service.CloseCalc(True)
calculation_service.Calculate(handle=handle_calc_trix)


# Adding another elvation grid and calculating with that one.
surface_data_type = 'ODSSurfaceGrid'
online_data_service.PrepareService(dataType=surface_data_type, lat=lat, lon=lng)
surface_services = online_data_service.GetServices(dataType=surface_data_type)

# Choosing a specific dataset and downloading the data
surface_service_id = 'Global_TandemX90_Grid'
surface_handle = online_data_service.DownloadHeightData(dataType=surface_data_type,
                                                        implId=surface_service_id,
                                                        userDesc='Terrain data grid',
                                                        lat=lat,
                                                        lon=lng,
                                                        width=30000,
                                                        height=30000,
                                                        useAsTin=True)

# Getting information of the object
grid_obj = obj_elevation_grid_service.GetElevationGridObject(handle=surface_handle)
grid_obj.DefaultHC = True
grid_obj.UserDescription = 'Surface Elevation Copernicus'
grid_obj.UserLabel = 'Surface Elevation Copernicus'
nan_to_skipvalue(grid_obj)
obj_elevation_grid_service.SetElevationGridObject(apiElevationGridobj=grid_obj)


handle_calc_trix = calculation_service.CreateEmpty('CalcTRIX')
calculation_service.OpenCalcForEdit(handle=handle_calc_trix)
calc_trix = calc_trix_service.GetTRIXCalc()

# showing properties of calculation.
# By default all turbines and meteoobjects are included
print(calc_trix)

calc_trix.Name = 'Script generation with Copernicus DSM'
calculation_service.CloseCalc(True)
calculation_service.Calculate(handle=handle_calc_trix)

area_object.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'New Salem', '4.1')
project_path = os.path.join(working_dir, 'New Salem.w41p')

_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_area_service = _windproapi.get_service('ObjAreaService')
online_data_service = _windproapi.get_service('OnlineDataService')
windpro_service = _windproapi.get_service('WindproService')
factory = _windproapi.get_factory('WindproService')

# Loading New Salem project
project_service.LoadFromFile(filename=project_path)

# Downloading area data from online data
roughness_layer = objects_service.AddLayer(layerName='New Roughness Area', parentFolderHandle=0)
# Finding the site center latitude and longitude
site_center_obj = objects_service.GetObjects(apiObjType='SiteCenter')[0]
lat = site_center_obj.Lat
lng = site_center_obj.Lng

# Preparing download and
terrain_data_type = 'ODSRoughnessArea'
online_data_service.PrepareService(dataType=terrain_data_type, lat=lat, lon=lng)
terrain_services = online_data_service.GetServices(dataType=terrain_data_type)
print(terrain_services)
roughness_service_id = 'DataService_Rou_GlobCover'
roughness_handle = online_data_service.DownloadRoughnessData(dataType=terrain_data_type,
                                                             implId=roughness_service_id,
                                                             userDesc='Script added roughness',
                                                             lat=lat,
                                                             lon=lng,
                                                             width=30000,
                                                             height=30000)

# Loading information from the roughness file in ¨
area_obj = obj_area_service.GetAreaObject(roughness_handle)

# Setting roughness classes up for all items by 0.5
for item in area_obj.LTyp.LTypItems.TApiLTypItem:
    item.Rou += 0.5
    item.Name = 'increase roughness class by 0.5 from ' + item.Name
nan_to_skipvalue(area_obj)
obj_area_service.SetAreaObject(area_obj)

roughness_layer = objects_service.AddLayer(layerName='New Roughness Area imported from shape file', parentFolderHandle=0)

shape_file_path = os.path.join(os.path.dirname(__file__), '../data/shape_file/new_salem_roughness.shp')
coord_sys = windpro_service.GetCoorSysFromEPSG(32614)

obj = objects_service.AddObject(apiObjType='AreaObj',
                                  lat = area_obj.Lat,
                                  lng = area_obj.Lng,
                                  userDesc = 'Imported from shape file')

# Setting the purpose of the new object to be roughness
area_obj = obj_area_service.GetAreaObject(handle=obj.Handle)
area_obj_purposes = factory.TApiAreaObjPurposes()
area_obj_purposes.TApiAreaObjPurpose.append('ApiapRoughness')
area_obj.Purposes = area_obj_purposes
area_obj.Filename = os.path.join(working_dir, 'imported_from_shp.w2r')
area_obj['RouBack'] = 1
nan_to_skipvalue(area_obj)
obj_area_service.SetAreaObject(area_obj)

res = obj_area_service.ImportFromSHPFile(handle=area_obj.Handle,
                                         fn=shape_file_path,
                                         DataColumn=2,
                                         coorsys=coord_sys,
                                         LayerNewName='',
                                         LayerExiName='',
                                         RadiusOrWidth=50000,
                                         LineAsPolygon=False)

# Adding a new background roughness
# The Id needs to be -111111111 which will change in the future
area_obj = obj_area_service.GetAreaObject(handle=obj.Handle)
TApiLTypItem = factory.TApiLTypItem()
TApiLTypItem.Rou = 0.01
TApiLTypItem.Id = -111111111
TApiLTypItem.Name = 'BACKGROUND'
TApiLTypItem.IsBackRou = True
area_obj.LTyp.LTypItems.TApiLTypItem.append(TApiLTypItem)
nan_to_skipvalue(area_obj)
obj_area_service.SetAreaObject(area_obj)

ctrlpoint_object.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'ObjCtrlPointService')
os.makedirs(working_dir, exist_ok=True)
project_path = os.path.join(working_dir, 'ObjCtrlPointService.w41p')
_windproapi.start_windpro_random_port()

# Project service for making a new project
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_ctrl_point_service = _windproapi.get_service('ObjCtrlPointService')

# Project path and location
lng = 10.
lat = 55.

# Making a new empty project and saving it
project_service.NewProject(lng=lng, lat=lat, filename=project_path)

# Make new control point object
obj = objects_service.AddObject(apiObjType='CtrlPoint',
                                lat=55.,
                                lng=10.,
                                userDesc='New CtrlPoint')

# Get object with more detail
ctrl_point_obj = obj_ctrl_point_service.GetCtrlPointObject(obj.Handle)

print(ctrl_point_obj)

# Modify object
ctrl_point_obj.UncertaintyHorz = 1000
ctrl_point_obj.UncertaintyVert = 1000
ctrl_point_obj.CtrlPointType = 'ApicptUncertainty'
ctrl_point_obj.Color.Green = 0

# Sending changes back to windPRO
nan_to_skipvalue(ctrl_point_obj)
obj_ctrl_point_service.SetCtrlPointObject(ctrl_point_obj)

elevationgrid_object.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'ObjElevationGridService')
os.makedirs(working_dir, exist_ok=True)
project_path = os.path.join(working_dir, 'ObjElevationGridService.w41p')

_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_elevation_grid_service = _windproapi.get_service('ObjElevationGridService')
online_data_service = _windproapi.get_service('OnlineDataService')
obj_line_service = _windproapi.get_service('ObjLineService')

lat, lng = 52.738533, 12.405603
project_service.NewProject(lng=lng, lat=lat, filename=working_dir)

# Downloading elevation grid data from online data
online_data_service.PrepareService(dataType='ODSTerrainGrid', lat=lat, lon=lng)
handle = online_data_service.DownloadHeightData(dataType='ODSTerrainGrid',
                                                implId='DEUBRA05_Grid',
                                                userDesc='German Brandenburg Elevation Model',
                                                lat=lat,
                                                lon=lng,
                                                width=3_000,
                                                height=3_000,
                                                useAsTin=False)

# Getting information of the object
grid_obj = obj_elevation_grid_service.GetElevationGridObject(handle=handle)
print(grid_obj)

# Settings used later for conversion: No single hill tops and troughs. connects lines that are not closed.
grid_obj.GridToHcSetup.Items.TApiGridToHCSetupItem[0].AddMonoAreas = True
grid_obj.GridToHcSetup.Items.TApiGridToHCSetupItem[0].ConnectLines = True
# Changing the height contour lines distance to 2.0m
grid_obj.GridToHcSetup.DefaultEquidistance = 2.0
nan_to_skipvalue(grid_obj)
obj_elevation_grid_service.SetElevationGridObject(grid_obj)

# Path by name of layer and converting
temp_path = os.path.join(working_dir, 'converted_grid_to_line.wpo')
obj_elevation_grid_service.ConvertToLines(handle=handle, filename=temp_path)

# Making a new line object and adding the previously generated file to it
new_handle = objects_service.AddObject(apiObjType='HCData',
                                       lat=grid_obj.Lat,
                                       lng=grid_obj.Lng,
                                       userDesc=grid_obj.UserDescription).Handle
line_obj = obj_line_service.GetLineObject(new_handle)
line_obj.Filename = temp_path
nan_to_skipvalue(line_obj)
res = obj_line_service.SetLineObject(line_obj)

line_object.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'Ebeltoft - Denmark', '4.1')
project_path = os.path.join(working_dir, 'DEMO - Ebeltoft, DK.w41p')

_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_line_service = _windproapi.get_service('ObjLineService')

# Loading project
project_service.LoadFromFile(filename=project_path)

objs = objects_service.GetObjects(apiObjType='HCData')

# Loading are object specific data
area_obj = obj_line_service.GetLineObject(objs[0].Handle)
print(area_obj)

meteo_object.py

import time
"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'ObjMeteoService')
os.makedirs(working_dir, exist_ok=True)
testdata_path = os.path.join(os.path.dirname(__file__), '../data')
_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_meteo_service = _windproapi.get_service('ObjMeteoService')
factory = _windproapi.get_factory('WindproService')

# Making a new projects and adding meteo objects
project_path = os.path.join(working_dir, 'ObjMeteoService.w41p')
lng = -101.402205
lat = 46.732303

project_service.NewProject(lng=lng, lat=lat, filename=project_path)
time.sleep(1)

# Creating a new meteo object and importing a previous export file
new_obj = objects_service.AddObject(apiObjType='MeteoObjectData',
                                    lat=lat,
                                    lng=lng,
                                    userDesc='New Meteo obj')
exp_file = os.path.join(testdata_path, 'meteo_export', 'newsalem_export_single_height.csv')

# Import returns True if it completed correctly
res = obj_meteo_service.ImportFromWpExportFile(handle=new_obj.Handle, importFilename=exp_file)
# Getting the meteo object from the generic object.
meteo_obj = obj_meteo_service.GetMeteoObject(handle=new_obj.Handle)
print(meteo_obj)

# Creating a new meteoobject from a mesores file
# No information on the location is necessary
mesoFn = os.path.join(testdata_path, 'mesores', 'new_salem_01-01-2020;01-02-2020.mesores')
# Mesores files can contain multiple poitns thereore a list of handles is returned
mesoHandles = obj_meteo_service.CreateFromMesores(mesoResFilename=mesoFn)
meteo_obj = obj_meteo_service.GetMeteoObject(handle=mesoHandles[0])
print(meteo_obj)

# Exporting data to windPRO format
# Heights need to be identified by their index
int_array = factory.TROArray_System_Integer_([1, 2])
meto_export_path = os.path.join(working_dir, 'meto_exp_test.txt')
obj_meteo_service.ExportMeteoObjectToFile(handle=mesoHandles[0],
                                          heightUids=int_array,
                                          exportFilename=meto_export_path)

# Deleting and reimporting object
objects_service.DeleteObject(mesoHandles[0])

new_obj = objects_service.AddObject(apiObjType='MeteoObjectData',
                                    lat=lat,
                                    lng=lng,
                                    userDesc='Reimported mesodata obj')
obj_meteo_service.ImportFromWpExportFile(handle=new_obj.Handle, importFilename=meto_export_path)

meteo_obj = obj_meteo_service.GetMeteoObject(handle=new_obj.Handle)
print(meteo_obj)

# Getting Weibull statistics
exp_file = os.path.join(working_dir, 'newsalem_statistics.tab')
obj_meteo_service.SaveFreqTableToTab(meteo_obj.Handle, 1, exp_file)

# Importing data from csv with an import filter
# make a new object
new_obj = objects_service.AddObject(apiObjType='MeteoObjectData',
                                    lat=lat + 0.05,
                                    lng=lng,
                                    userDesc='Mast from New Salem')
exp_file = os.path.join(testdata_path, 'meteo_export', 'newsalem_export_single_height.csv')

# Getting meteo object
meteo_mast = obj_meteo_service.GetMeteoObject(handle=new_obj.Handle)

# Meteo Import file needs to be set up
meteo_import = factory.TApiMeteoImportSetup()
import_file = os.path.join(testdata_path, 'meteo', 'import_filter.wls')
meteo_import.LoggerSetupFile = import_file
meteo_import.DontUseInSetup = False
meteo_import.IsOnlineData = False

# For the import setup we need infomration on the folder structure and where the data is located.
# Multiple folders could be included, in this case it is only one
folder_items = factory.TApiFileFolderItems()
folder_item = factory.TApiFileFolderItem()
folder_item.FFType = factory.TFileFolderListType('fftFile')
folder_item.FFName = os.path.join(testdata_path, 'meteo', 'New_Salem_2_South_60m_FINAL.csv')
folder_item.FFMask = None
folder_item.FFInclSubFolder = False
folder_item.FFTreatFolderAsZip = False
nan_to_skipvalue(folder_item)
folder_items.TApiFileFolderItem.append(folder_item)

# Add Folder structure to serach paths
meteo_import.SearchPathes = folder_items

# Add import setup to meteo object
nan_to_skipvalue(meteo_import)
obj_meteo_service.AddImportSetup(handle=new_obj.Handle, importSetup=meteo_import, loggerSetupFn=import_file)

# Automatically create heights. Needs to be done before loading data to determine what data should be loaded in.
obj_meteo_service.AutoCreateFromImportSetup(handle=meteo_mast.Handle)

# Load the actual data
obj_meteo_service.LoadAllData(handle=meteo_mast.Handle)

nsa_object.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from pprint import pprint
from windproapi.utils import get_windpro_sample_path
from windproapi import nan_to_skipvalue
from windproapi import WindProApi
from windproapi import compare_objects
from copy import deepcopy

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'Ebeltoft - Denmark', '4.1')
project_path = os.path.join(working_dir, 'DEMO - Ebeltoft, DK.w41p')

_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_NSA_service = _windproapi.get_service('ObjNSAService')
factory = _windproapi.get_factory('WindproService')

# Loading project
project_service.LoadFromFile(filename=project_path)

# Getting all Noise Sensitive Area objects
objs = objects_service.GetObjects(apiObjType='NSA')

# Loading are object specific data
nsa_obj = obj_NSA_service.GetNSAObject(objs[0].Handle)

# Changing the shape of the noise sensitive area
nsa_obj.Points.TApiGP[0].Lat = nsa_obj.Points.TApiGP[0].Lat + 0.03

# Changing the noise demand. Caution, this will not work as this object as a predefined noise level level set!
nan_to_skipvalue(nsa_obj)
obj_NSA_service.SetNSAObject(nsa_obj)

# Comparing object that was set with what is in windPRO
nsa_obj_wp = obj_NSA_service.GetNSAObject(objs[0].Handle)
compare_objects(nsa_obj_wp, nsa_obj)

# Adding a new noise recepors
# Set those with a constant freely defined noise constraint

# Single noise receptor
new_obj = objects_service.AddObject(apiObjType='NSA',
                                    lat=56.17,
                                    lng=10.66,
                                    userDesc='Script Added Point')
nsa_obj = obj_NSA_service.GetNSAObject(new_obj.Handle)
nsa_obj_default = deepcopy(nsa_obj)

# Changing noise level to a user defined absolute noise
nsa_obj.FreeDefinable = True
nsa_obj.KindOfDemand = 'kodAbsolute'
nsa_obj.NoiseDemandNew = 37
nan_to_skipvalue(nsa_obj)
obj_NSA_service.SetNSAObject(nsa_obj)

nsa_obj = obj_NSA_service.GetNSAObject(nsa_obj.Handle)
print(compare_objects(nsa_obj, nsa_obj_default))

# Adding NSA with area
new_obj = objects_service.AddObject(apiObjType='NSA',
                                    lat=56.17,
                                    lng=10.67,
                                    userDesc='Script Added Area')
nsa_obj = obj_NSA_service.GetNSAObject(new_obj.Handle)

# Adding points to the area
for i_lat, i_lng in zip([56.172, 56.172, 56.162, 56.162], [10.67, 10.68, 10.68, 10.675]):
    point = factory.TApiGP()
    point.Lat = i_lat
    point.Lng = i_lng
    nsa_obj.Points.TApiGP.append(point)

nsa_obj.FreeDefinable = True
nsa_obj.KindOfDemand = 'kodAbsolute'
nsa_obj.NoiseDemandNew = 42
nan_to_skipvalue(nsa_obj)
obj_NSA_service.SetNSAObject(nsa_obj)

# Changing the country
new_obj = objects_service.AddObject(apiObjType='NSA',
                                    lat=56.27,
                                    lng=10.66,
                                    userDesc='Script Added Point, change country')


nsa_obj = obj_NSA_service.GetNSAObject(new_obj.Handle)
nsa_obj_default = deepcopy(nsa_obj)

# Changing noise level to a user defined absolute noise
nsa_obj.PreDefinedCountry = 1
nsa_obj.PreDefinedType = 1
nsa_obj.PreDefined = 2
# nsa_obj.KindOfDemand = 'kodAmbient'#'kodAbsolute'
nan_to_skipvalue(nsa_obj)

obj_NSA_service.SetNSAObject(nsa_obj)

nsa_obj = obj_NSA_service.GetNSAObject(nsa_obj.Handle)
pprint(compare_objects(nsa_obj, nsa_obj_default))

obstacle_object.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'Ebeltoft - Denmark', '4.1')
project_path = os.path.join(working_dir, 'DEMO - Ebeltoft, DK.w41p')

_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_obstacle_service = _windproapi.get_service('ObjObstacleService')

# Loading project
project_service.LoadFromFile(filename=project_path)

objs = objects_service.GetObjects(apiObjType='Obstacle')

# Loading are object specific data
obstacle_obj = obj_obstacle_service.GetObstacleObject(objs[0].Handle)

# Changing the depth and angle
obstacle_obj.Depth = 300
obstacle_obj.Angle = 16

nan_to_skipvalue(obstacle_obj)
obj_obstacle_service.SetObstacleObject(obstacle_obj)

radar_object.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'ObjRadarService')
os.makedirs(working_dir, exist_ok=True)
_windproapi.start_windpro_random_port()

# Project service for making a new project
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_radar_service = _windproapi.get_service('ObjRadarService')

# Project path and location
project_path = os.path.join(working_dir, 'ObjRadarService.w41p')
lng = 10.
lat = 55.

# Making a new empty project and saving it
project_service.NewProject(lng=lng, lat=lat, filename=project_path)

# Making new radar object and getting its parameters
obj = objects_service.AddObject(apiObjType='RadarObject',
                                lat=54.93,
                                lng=9.95,
                                userDesc='new radar object')
radar_object = obj_radar_service.GetRadarObject(obj.Handle)

# Setting some parameter
radar_object.RadarHeight = 25
radar_object.Angle = 90

nan_to_skipvalue(radar_object)
obj_radar_service.SetRadarObject(radar_object)

ruler_object.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'ObjRulerService')
os.makedirs(working_dir, exist_ok=True)
_windproapi.start_windpro_random_port()

# Project service for making a new project
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_ruler_service = _windproapi.get_service('ObjRulerService')

# Project path and location
project_path = os.path.join(working_dir, 'test.w41p')
lng = 10.
lat = 55.

# Making a new empty project and saving it
project_service.NewProject(lng=lng, lat=lat, filename=project_path)

# Making new radar object and getting its parameters
obj = objects_service.AddObject(apiObjType='Ruler',
                                lat=lat,
                                lng=lng,
                                userDesc='Length')
ruler_object = obj_ruler_service.GetRulerObject(obj.Handle)

# Setting some parameter
ruler_object.Angle = 120
ruler_object.Count = 5
ruler_object.Distance = 1000
ruler_object.LabelsOnTicks = True

nan_to_skipvalue(ruler_object)
obj_ruler_service.SetRulerObject(ruler_object)

shadow_object.py

import time
"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'Ebeltoft - Denmark', '4.1')
project_path = os.path.join(working_dir, 'DEMO - Ebeltoft, DK.w41p')

_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_shadow_service = _windproapi.get_service('ObjShadowService')

# Loading project
project_service.LoadFromFile(filename=project_path)

objs = objects_service.GetObjects(apiObjType='Shadow')

# Loading are object specific data
shadow_obj = obj_shadow_service.GetShadowObject(objs[0].Handle)

# Changing height of window and height above ground
shadow_obj.Height = 2
shadow_obj.HeightAboveGround = 4.0

# Changing the direction of the window. Watch out, this is in the same coordinate system as the wind direction whereas
# it is in degree from south in windPRO
shadow_obj.AngleShadow = 210

nan_to_skipvalue(shadow_obj)
obj_shadow_service.SetShadowObject(shadow_obj)

shape_object.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'ObjShapeService')
os.makedirs(working_dir, exist_ok=True)
_windproapi.start_windpro_random_port()

# Project service for making a new project
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_shape_service = _windproapi.get_service('ObjShapeService')

# Project path and location
project_path = os.path.join(working_dir, 'ObjShapeService.w41p')
lng = 10.
lat = 55.

# Making a new empty project and saving it
project_service.NewProject(lng=lng, lat=lat, filename=project_path)

# Making new radar object and getting its parameters
obj = objects_service.AddObject(apiObjType='Shape',
                                lat=lat,
                                lng=lng,
                                userDesc='new shape')
shape_object = obj_shape_service.GetShapeObject(obj.Handle)

# Setting some parameter
# shape_object.MapShapeType = 'ApiCircle'
shape_object.Height = 4000
shape_object.Width = 1000

nan_to_skipvalue(shape_object)
obj_shape_service.SetShapeObject(shape_object)

sitedata_object.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'New Salem', '4.1')
project_path = os.path.join(working_dir, 'New Salem.w41p')

_windproapi.start_windpro_random_port()

# Services
obj_wtg_service = _windproapi.get_service('ObjWtgService')
obj_site_data_service = _windproapi.get_service('ObjSiteDataService')
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
calculation_service = _windproapi.get_service('CalculationService')
obj_line_service = _windproapi.get_service('ObjLineService')

# Loading New Salem project
project_service.LoadFromFile(filename=project_path)

objs = objects_service.GetObjects(apiObjType='SiteData')

# Getting the second site data object
siteDataObj = obj_site_data_service.GetSiteDataObject(objs[1].Handle)
print(siteDataObj)

# Creating a new statgen calculation calculation and starting to edit it
# Adding a site data object
siteData = objects_service.AddObject(apiObjType='SiteData',
                                     lat=46.8,
                                     lng=-101.65,
                                     userDesc='STATGEN SiteData from scripting')

# Setting the purpose. See documentation
# 'SdPurpUndef' not defined
# 'SdPurpAtlas' ATLAS service
# 'SdPurpWasp' for WAsP calculation
# 'SdPurpStatgen' for statistical wind climate STATGEN
# 'SdPurpResource' for wind resource map
# 'SdPurpCFD' for CFD calculation

obj_site_data_service.SetPurpose(handle=siteData.Handle, purpose='SdPurpStatgen')

# Connecting elevation and roughness data and elevation data.
# The HC data is general line data. See ObjLineService for more on this service.
hcdata = objects_service.GetObjects(apiObjType='HCData')
for obj in objects_service.GetObjects(apiObjType='HCData'):
    print(obj_line_service.GetLineObject(obj.Handle))

# Linking the orography data to the sitedata object
obj_site_data_service.TerrainLinkElevationAndRoughnessLine(handle=siteData.Handle,
                                                           elevHandle=hcdata[0].Handle,
                                                           rouLineHandle=hcdata[1].Handle)

# Getting the site data object in python to inspect the properties
siteData = obj_site_data_service.GetSiteDataObject(siteData.Handle)
print(siteData)

# Limiting the size of the maps
siteData.MapRouLimit = 10_000
siteData.MapOroLimit = 3_000

nan_to_skipvalue(siteData)
obj_site_data_service.SetSiteDataObject(siteData)

# Exporting the map data connected with this object. All 0 for corners means full size.
obj_site_data_service.ExportCombinedMapFile(handle=siteData.Handle,
                                            lowerLeftLat=0.,
                                            lowerLeftLng=0.,
                                            upperRightLat=0.,
                                            upperRightLng=0.,
                                            filename=os.path.join(working_dir, 'testtgs.map'))

text_object.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'ObjTextService')
os.makedirs(working_dir, exist_ok=True)
_windproapi.start_windpro_random_port()

# Project service for making a new project
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_text_service = _windproapi.get_service('ObjTextService')

# Project path and location
project_path = os.path.join(working_dir, 'ObjTextService.w41p')
lng = 10.
lat = 55.

# Making a new empty project and saving it
project_service.NewProject(lng=lng, lat=lat, filename=project_path)

# Making new radar object and getting its parameters
obj = objects_service.AddObject(apiObjType='UsrTextData',
                                lat=lat,
                                lng=lng,
                                userDesc='Description, script generated')
text_object = obj_text_service.GetTextObject(obj.Handle)

# Setting some parameter
text_object.Text = 'Hello world'
text_object.BackColor.Red = 124
text_object.BackColor.Green = 212

nan_to_skipvalue(text_object)
obj_text_service.SetTextObject(text_object)

wtgareas_object.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'ObjWtgAreasService')
os.makedirs(working_dir, exist_ok=True)
_windproapi.start_windpro_random_port()

# Services
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
obj_wtg_areas_aervice = _windproapi.get_service('ObjWtgAreasService')

# For more complex datatypes
factory = _windproapi.get_factory('WindproService')

# Project path and location
project_path = os.path.join(working_dir, 'ObjWtgAreasService.w41p')
lng = 11.2
lat = 56.6

# Making a new empty project and saving it
project_service.NewProject(lng=lng, lat=lat, filename=project_path)

# Making new WTG area object and getting its parameters
obj = objects_service.AddObject(apiObjType='WTGareas',
                                lat=lat,
                                lng=lng,
                                userDesc='New are for wind farm')
wtg_areas_object = obj_wtg_areas_aervice.GetWtgAreasObject(obj.Handle)

# Paramter holding multiple WTG areas in a formate understandable for windPRO
wtg_areas_object.AreaList = factory.TApiObjWtgAreaList()

# This is to make a new WTG area
# It needs some information to be set into windPRO. This is a minimal example.
wtg_area = factory.TApiObjWtgArea()
wtg_area.Name = 'New area'
# For the style of the line. See tns:TApiBrushStyle
wtg_area.Hatching = 'ApiBsClear'
# We need a list of points that define the area
print(wtg_area.Points)

lats = [56.5, 56.58, 56.68, 56.68, 56.70, 56.70, 56.60, 56.56]
lngs = [11.2, 11.20, 11.17, 11.19, 11.18, 11.21, 11.26, 11.33]
# This will be the input to the wtg_are.Points.
# Making a list of points that are appended here
wtg_area.Points = factory.TApiGPs()
for i_lat, i_lng in zip(lats, lngs):
    newGP = factory.TApiGP()
    newGP.Lat = i_lat
    newGP.Lng = i_lng
    wtg_area.Points.TApiGP.append(newGP)

# We can add e.g. a buffer zone
wtg_area.BufferZone = 500.0

# We can add minum and maximum number o fturbines
wtg_area.CountDemand = True
wtg_area.MinCount = 20
wtg_area.MaxCount = 60

# We can add a minimum and maximum power
wtg_area.EffectDemand = True
wtg_area.MinEffect = 50_000
wtg_area.MaxEffect = 200_000

# Appending the WTG area to the WTG areas. There can in principle be more than one.
wtg_areas_object.AreaList.TApiObjWtgArea.append(wtg_area)

# Giving data back to windPRO
nan_to_skipvalue(wtg_areas_object)
obj_wtg_areas_aervice.SetWtgAreasObject(wtg_areas_object)

wtg_areas_object = obj_wtg_areas_aervice.GetWtgAreasObject(obj.Handle)
print(wtg_areas_object)

wtg_object.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'New Salem', '4.1')
project_path = os.path.join(working_dir, 'New Salem.w41p')

_windproapi.start_windpro_random_port()

# Services
obj_wtg_service = _windproapi.get_service('ObjWtgService')
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
wtg_explorer_service = _windproapi.get_service('WtgExplorerService')
calculation_service = _windproapi.get_service('CalculationService')
calc_park_service = _windproapi.get_service('CalcParkService')
factory = _windproapi.get_factory('WindproService')

# Loading New Salem project
project_service.LoadFromFile(filename=project_path)

# Getting wind turbine generators already present
objs = objects_service.GetObjects(apiObjType='NewWTG')
wtg_obj = obj_wtg_service.GetWtgObject(objs[0].Handle)
print(wtg_obj)

# Adding a new wtg object
# Making a new object
new_obj = objects_service.AddObject(apiObjType='NewWTG',
                                    lat=wtg_obj.Lat + 0.01,
                                    lng=wtg_obj.Lng + 0.01,
                                    userDesc='New WTG obj')
wtg_obj = obj_wtg_service.GetWtgObject(new_obj.Handle)
wtg_obj.UserLabel = 'New WTG obj'

path_to_WTG = os.path.join(os.path.dirname(__file__), '../data/SIEMENS SWT-2.3_test.wtg')
wtg_obj.Filename = path_to_WTG
# Hub height needs to be given explicitely
wtg_obj.Hubheight = 92.6
# Needed to handle None values when communicating with zeep
nan_to_skipvalue(wtg_obj)
# Set everything back into the wtg object
obj_wtg_service.SetWtgObject(wtg_obj)

## Adding new turbines
# Getting all Vestas turbines from turbine catalogue between 2000kw and 2500kW and rotor diameter between 90m and 110m
list_possible_wtgs = wtg_explorer_service.GetWtgsWithFilter(manufactor='VESTAS',
                                                            minRatedPower=3000,
                                                            maxRatedPower=3500,
                                                            minHubHeight=0,
                                                            maxHubHeight=0,
                                                            minRotorDiameter=120,
                                                            maxRotorDiameter=150)
wtg_alternative = list_possible_wtgs[0]

wtg_details = wtg_explorer_service.GetWtgFromFile(filename=wtg_alternative.FileName,
                                                  details=True)

# Finding all objects in the WTG's layer
vestas_wtg_layer = [o for o in objects_service.GetLayers() if o.Name=='WTG\'s'][0]
objects_service.AddLayer(layerName=wtg_details.DataName, parentFolderHandle=0)

# Looping over all turbines the WTG's layer and making new wtgs with this wtg type
vestas_wtg_handles = []
for handle in vestas_wtg_layer.ObjectHandles.int:
    obj = objects_service.GetObjectFromHandle(handle)
    if obj.ApiObjType == 'NewWTG':
        new_obj = objects_service.AddObject(apiObjType='NewWTG',
                                            lat=obj.Lat,
                                            lng=obj.Lng,
                                            userDesc='New WTG obj')
        vestas_wtg_handles.append(new_obj.Handle)
        # Getting object and modifying data
        wtg_obj = obj_wtg_service.GetWtgObject(handle=new_obj.Handle)
        wtg_obj.UserLabel = 'Vestas ' + obj.UserDescription
        wtg_obj.UserDescription = 'Vestas ' + obj.UserDescription
        wtg_obj.Hubheight = wtg_details.DefHubHeight
        wtg_obj.Filename = wtg_details.FileName
        nan_to_skipvalue(wtg_obj)
        obj_wtg_service.SetWtgObject(wtg_obj)

## Cloning a park calculation and making a new calculation with all the Vestas turbines
# Getting all PARK calculations in
all_park_calcs = calculation_service.GetCalcs(calcType='CalcPark')
vestas_park_handle = calculation_service.Clone(handle=all_park_calcs[1].Handle)

calculation_service.OpenCalcForEdit(handle=vestas_park_handle)
park_calc = calc_park_service.GetParkCalc()

park_calc.Name = 'Vestas turbines'

wtgids = factory.TApiWtgIds()
for handle in vestas_wtg_handles:
    dummy = factory.TApiWtgId()
    dummy.Handle = 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=vestas_park_handle)

## Choosing one of the turbine and calculating with all power curves (noice reduced modes)
# Turbine handle to choose:
noise_reduction_turbine_handle = vestas_wtg_handles[0]
noise_reduction_turbine_user_descr = objects_service.GetObjectFromHandle(noise_reduction_turbine_handle).UserDescription
print(f'Reducing noise mode for {noise_reduction_turbine_user_descr}')

# Looking through all curves and getting power curves
list_power_curve_details = []
for entry in wtg_details.DetailDatas.TApiWtgDetailData:
    if entry.DetailType == 'WTGDTPowerCurve' and not entry.Invalid:
        list_power_curve_details.append(entry)

# Looping over all power curves found
for entry in list_power_curve_details:
    print(f'Power curve: {entry.Name}')

    # Changing power curve for wtg object
    wtg_obj = obj_wtg_service.GetWtgObject(noise_reduction_turbine_handle)
    wtg_obj.Powercurve = entry.UniqueID
    wtg_obj.UseDefault = False
    nan_to_skipvalue(wtg_obj)
    obj_wtg_service.SetWtgObject(wtg_obj)

    # Making a new PARK calculation for each power curve
    cloned_park_handle = calculation_service.Clone(handle=vestas_park_handle)
    calculation_service.OpenCalcForEdit(handle=cloned_park_handle)
    park_calc = calc_park_service.GetParkCalc()

    park_calc.Name = 'Vestas turbines PC: ' + entry.Name

    nan_to_skipvalue(park_calc)
    calc_park_service.SetParkCalc(park_calc)
    calculation_service.CloseCalc(save=True)
    calculation_service.Calculate(handle=cloned_park_handle)

    try:
        import pandas as pd

        # Exporting results from the wind farm
        path_result = os.path.join(working_dir, f'park_result_vestas_{entry.Name}.csv')
        calculation_service.ResultToFile(handle=cloned_park_handle, id='Park result', filename=path_result)

        # 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}, encoding = 'ISO-8859-1')

        print('Farm AEP: {:.2f} mWh/y'.format(df['Result'].sum()))
    except:
        print('Could not read results file.')

# Changing turbine back to default power curves
# Changing power curve for wtg object
wtg_obj = obj_wtg_service.GetWtgObject(noise_reduction_turbine_handle)
wtg_obj.UseDefault = True
nan_to_skipvalue(wtg_obj)
obj_wtg_service.SetWtgObject(wtg_obj)

wtg_object_curtailment.py

"""
Copyright 2024 EMD International
License for this script: MIT https://opensource.org/license/mit/
License for windPRO commercial software: https://www.emd-international.com/contact-us/general-terms-conditions-sale/
"""

import os
from datetime import datetime
from windproapi.utils import get_windpro_sample_path
from windproapi import WindProApi
from windproapi import nan_to_skipvalue

# Opening windPRO
_windproapi = WindProApi()
working_dir = os.path.join(get_windpro_sample_path('4.1'), 'New Salem', '4.1')
project_path = os.path.join(working_dir, 'New Salem.w41p')

_windproapi.start_windpro_random_port()

# Services
obj_wtg_service = _windproapi.get_service('ObjWtgService')
project_service = _windproapi.get_service('ProjectService')
objects_service = _windproapi.get_service('ObjectsService')
wtg_explorer_service = _windproapi.get_service('WtgExplorerService')
calculation_service = _windproapi.get_service('CalculationService')
calc_park_service = _windproapi.get_service('CalcParkService')
factory = _windproapi.get_factory('WindproService')

# Loading New Salem project
project_service.LoadFromFile(filename=project_path)

# Getting wind turbine generators already present
objs = objects_service.GetObjects(apiObjType='NewWTG')
wtg_obj = obj_wtg_service.GetWtgObject(objs[0].Handle)
print(wtg_obj)


## Curtailment helper functions
# Function for changing the date time into format for windPRO (same as excel)
def excel_date(date1):
    temp = datetime(1899, 12, 30)    # Note, not 31st Dec but 30th!
    delta = date1 - temp
    return float(delta.days) + (float(delta.seconds) / 86400)

# For chaning curtailments by type
def change_curtailment(curtailment, curtailment_type, condition_from, condition_to):
    for c in curtailment.CurtailmentConditions.TApiWtgCurtailmentCondition:
        if c.CurtailmentCondition == curtailment_type:
            c.ConditionFrom = condition_from
            c.ConditionTo = condition_to
            return
    raise KeyError(f'Could not find {curtailment_type}.')

def delete_curtailment(curtailment, curtailment_type):
    for i, c in enumerate(curtailment.CurtailmentConditions.TApiWtgCurtailmentCondition):
        if c.CurtailmentCondition == curtailment_type:
            curtailment.CurtailmentConditions.TApiWtgCurtailmentCondition.pop(i)
            return
    raise KeyError(f'Could not find {curtailment_type}.')

## Adding predefined curtailment
# Curtailment template for birds
newCurtailment = obj_wtg_service.GetCurtailmentTemplate('ApiCTBirds')
# Adjusting curtailment
newCurtailment.Name = 'Bird from default'
delete_curtailment(newCurtailment, 'ApiCCTemperature')
delete_curtailment(newCurtailment, 'ApiCCWindDirection')
delete_curtailment(newCurtailment, 'ApiCCWindSpeed')
# Time of day is set by fraction of 24 h values
change_curtailment(newCurtailment, 'ApiCCTime', 5/24, 7/24)
# Dates need to be set as days since 30th December 1899 like in excel. The year does not matter but needs to be given.
change_curtailment(newCurtailment, 'ApiCCDate', excel_date(datetime(2020, 3, 1)), excel_date(datetime(2020, 5, 1)))
# Times before sunset and sunrise are set by integer values.
# A list is available from the wind scripting api
print(obj_wtg_service.GetSunriseAndSunsetValues())
# using '0:45h before sunset: 19955', '0:45h after sunset: 20045'
change_curtailment(newCurtailment, 'ApiCCSunriseSunset', 19955, 20045)

wtg_obj = obj_wtg_service.GetWtgObject(objs[0].Handle)
# Adding curtailment list if None is present
if wtg_obj.Curtailment is None:
    TApiWtgCurtailments = factory.TApiWtgCurtailments()
    wtg_obj.Curtailment = TApiWtgCurtailments

wtg_obj.Curtailment.TApiWtgCurtailment.append(newCurtailment)
nan_to_skipvalue(wtg_obj)
obj_wtg_service.SetWtgObject(wtg_obj)

## Adding Curtailment with reduction in the mode
# Finding a power curve
print(wtg_explorer_service.GetWtgFromFile(filename=wtg_obj.Filename, details=True))

# Using similar settings as above
change_curtailment(newCurtailment, 'ApiCCDate', excel_date(datetime(2020, 8, 1)), excel_date(datetime(2020, 10, 1)))
newCurtailment.OperationMode = 'ApiCOMPowerCurve'
newCurtailment.ActionUID = '{6B20F289-1E7F-430D-AD71-C3A2D617ED38}'
newCurtailment.Name = 'Scripting reduced power curve'

wtg_obj = obj_wtg_service.GetWtgObject(objs[0].Handle)
# Adding curtailment list if None is present
if wtg_obj.Curtailment is None:
    TApiWtgCurtailments = factory.TApiWtgCurtailments()
    wtg_obj.Curtailment = TApiWtgCurtailments

wtg_obj.Curtailment.TApiWtgCurtailment.append(newCurtailment)
nan_to_skipvalue(wtg_obj)
obj_wtg_service.SetWtgObject(wtg_obj)

## Adding freely defined curtailment
newCurtailment = obj_wtg_service.GetCurtailmentTemplate('ApiCTOther')
newCurtailment.Name = f'Freely defined from Scripting'

# Making conditions
condition = factory.TApiWtgCurtailmentCondition()
condition.CurtailmentCondition = 'ApiCCDateTime'
condition.ConditionFrom = excel_date(datetime(2024, 8, 1))
condition.ConditionTo = excel_date(datetime(2020, 8, 31))

TApiWtgCurtailmentConditions = factory.TApiWtgCurtailmentConditions()
TApiWtgCurtailmentConditions.TApiWtgCurtailmentCondition.append(condition)

newCurtailment.CurtailmentConditions = TApiWtgCurtailmentConditions

wtg_obj = obj_wtg_service.GetWtgObject(objs[0].Handle)
# Adding curtailment list if None is present
if wtg_obj.Curtailment is None:
    TApiWtgCurtailments = factory.TApiWtgCurtailments()
    wtg_obj.Curtailment = TApiWtgCurtailments

wtg_obj.Curtailment.TApiWtgCurtailment.append(newCurtailment)
nan_to_skipvalue(wtg_obj)
obj_wtg_service.SetWtgObject(wtg_obj)