Release Notes#

This page documents all changes to PyWAsP across releases. PyWAsP follows semantic versioning: breaking changes increment the major version, new features increment the minor version, and bug fixes increment the patch version. Changes that can break existing code, including those a minor release makes to functions and classes marked as experimental, are listed under Breaking changes.

Changelog#

All major changes are listed here.

2.1.0 (2026-10-01)#

PyWAsP 2.1 contains breaking changes, and many results change. Read Upgrading from 2.0 before you upgrade.

Highlights#

  • WAsP 12.10 defaults: PyWAsP reproduces WAsP 12.10 with its default settings and is tested against WAsP 12.10 results.

  • Obstacles: TopographyMap accepts an obstacle_map of polygons with a height and an optional porosity, such as buildings or tree rows, and get_site_effects returns the shelter they give.

  • More accurate wind farm AEP: potential_aep and wind_farm_flow_map integrate gross AEP and wind speeds over the Weibull distribution and reproduce the WAsP PARK2 reference results to 1e-4–1e-5 relative accuracy. PARK1 and several flow map errors are fixed.

  • Wind directions relative to grid north: align_direction_crs, wwc_rotate and tswc_rotate rotate wind climates between direction frames. The downscale and predict functions rotate from the wind climate’s frame to the output points, and the generalize* functions can rotate to true north.

  • No side effects on import: import pywasp neither downloads data nor contacts the license server. The new pywasp mesoclimate command downloads the global mesoclimate files, and the license is checked at the first licensed calculation.

  • Faster: Many calculations run faster than in 2.0.

  • macOS on Apple Silicon: Experimental conda packages for osx-arm64 are now available.

Upgrading from 2.0#

Most code runs unchanged, but many results change. Check the following:

  • Requirements: PyWAsP 2.1 needs Python 3.12 or later and windkit 2.2, which has breaking changes of its own; read its Upgrading from 2.1.

  • Reproducing 2.0 results: pass gen_heights=[10, 25, 50, 100, 250] to generalize and set conf.climate[111] = 1 / 3 on your Config.

  • Mesoclimate files: seed them with pywasp mesoclimate download (or pywasp.download_mesoclimate()) instead of python -c 'import pywasp'. Catch pywasp.MesoclimateMissingError where you caught EOFError or FileNotFoundError from the air density and mesoclimate functions.

  • Errors: catch pywasp.PywaspError for bad input to potential_aep, wind_farm_flow_map and gross_aep: a wind climate missing a required variable, an air density missing everywhere, a wind_turbines of the wrong length, an unknown air_density_correction, or a ws_upper_limit that leaves nothing to simulate.

  • Elevation rasters with NaN: fill them with windkit.fill_elevation_nodata before constructing a TopographyMap, and catch PywaspError instead of ValueError. Re-save a .topo file that holds such a raster.

  • Output shapes: a scalar input gives a scalar output, and an input with a scalar height coordinate, such as a raster, keeps it. PyWAsP 2.0 added a size-one point or height dimension. Compare results by dimension name, not position.

  • Mesoclimates: select a single height of a multi-height mesoclimate before a Wind Atlas calculation, or use point data with height(point). A supplied mesoclimate, and the z0meso and slfmeso of the site effects, must not differ between points at the same horizontal location.

  • AEP totals and deficits: a yearly total is missing when any of its sectors is. To keep the 2.0 gross_aep total, use ds["gross_aep_sector"].sum("sector", skipna=True, min_count=1). To keep the 2.0 all-sector deficits, use xr.where(ds["gross_aep_sector"] == 0.0, 0.0, ds["wdfreq"] * ds["potential_aep_deficit_sector"]).sum("sector", skipna=False), and the same with wspd_sector and wspd_deficit_sector.

  • Missing air density: a location whose air_density is NaN gets no AEP. Fill the air density in, pass the air_density argument, or use air_density_correction="none".

  • Net AEP: net_aep compounds losses, so net AEP from a loss table with more than one loss is slightly higher than in 2.0.

  • Removed arguments and outputs: drop the shear argument of potential_aep and wind_farm_flow_map (pass the later arguments of wind_farm_flow_map by keyword), selections on the mode coordinate of their results, the ierror variable of get_return_wind, which raises CoreError instead, and engine="fortran" in interpolate_gwc.

  • Meridian convergence: a meridian_convergence variable in a binned wind climate is ignored. For generalized wind climates downloaded from the Global Wind Atlas, or made with earlier PyWAsP versions, read the migration guide in tutorial 9.

New features#

  • TopographyMap accepts an obstacle_map of polygons with a height column and an optional porosity column (default 0). get_site_effects then returns the shelter from these obstacles in obstacle_speedups; locations inside an obstacle and below its height get NaN, with a warning. save and load keep the obstacle map, and get_site_effects_rose raises when one is set.

  • Added align_direction_crs, which rotates the wind directions of a TSWC, BWC, WWC or GWC between coordinate reference systems. The source frame comes from source_wind_dir_crs or the wind_dir_crs attribute.

  • Added wwc_rotate and tswc_rotate, which rotate a Weibull or time series wind climate by a given angle.

  • The generalize* functions gained rotate_to_true_north (default False), which rotates wind directions to true north with the site’s meridian convergence. The downscale and predict functions gained align_direction_crs (default True), which rotates from the GWC’s direction frame to the output points.

  • bwc_from_tswc(..., wind_dir_crs=...) rotates true-north directions to the grid north of a projected CRS before binning, unless the TSWC’s wind_dir_crs already names that CRS. The generalize and predict functions read wind_dir_crs, warn when it is absent, and reject a BWC with a geographic direction frame.

  • Added the pywasp mesoclimate download, status and path commands, and pywasp.download_mesoclimate(), pywasp.mesoclimate_status() and pywasp.mesoclimate_path(), which download, list and locate the global mesoclimate files.

  • predict_bwc, predict_bwc_from_site_effects and downscale_from_geostrophic_and_site_effects_to_bwc gained n_wsbins_out (default 100), the number of bins in the output wind speed histogram, and allow_truncation, which turns the error for a truncated histogram into a warning and requires add_met=False.

  • potential_aep and wind_farm_flow_map warn when the simulated wind speeds stop short of where the turbine still produces, usually because ws_upper_limit is too low.

  • potential_aep and wind_farm_flow_map no longer need turbulence_intensity for the built-in wind farm models ("PARK1", "PARK2_onshore" and "PARK2_offshore"), which don’t use it. The effective turbulence intensity outputs are then left out.

  • The Wind Atlas functions accept a scalar mesoclimate, such as get_climate(...).isel(point=0), and a mesoclimate with one point per horizontal location, in the order the locations first appear.

  • get_spec_corr_fac accepts point, stacked_point, scalar and single_point time series, not only cuboid.

  • baroclinicity_histogram gained percentile, the wind speed percentile used for finalization.

  • New exceptions pywasp.MesoclimateMissingError, LicenseConfigError, LicenseServerUnreachableError and PointsLimitError.

  • Experimental conda packages for macOS on Apple Silicon (osx-arm64).

Bug fixes#

  • Gross AEP from potential_aep and wind_farm_flow_map was integrated over the simulated wind speed bins, which smeared the turbine’s cut-in and cut-out. Yearly totals were 0.06 % high at the default ws_stepsize, sectors were off by up to 1.2 %, and wake losses were inflated by about 4 % of their value. Potential AEP is unchanged.

  • Waked AEP misplaced power curve transitions where an upwind turbine cutting in or out makes the effective wind speed non-monotonic, shifting sector AEP by up to about 0.3 %.

  • wspd, wspd_eff and turbulence_intensity_eff from potential_aep and wind_farm_flow_map drifted with ws_stepsize and ws_upper_limit. They are integrated over the Weibull distribution like the AEPs, so wspd is the Weibull mean.

  • potential_aep with wind_farm_model="PARK1" reported roughly a third of the AEP; it now lands within 0.2 % of PARK2 on a site total. PARK1 also runs with n_cpu_pywake greater than 1.

  • An even n_subsector crashed. With n_subsector > 1 and site_interp_method="linear", a subsector took its speed-up and turbulence intensity from a neighbouring sector instead of its parent sector.

  • wind_farm_flow_map rotated the whole wake field half a sector away from the wind rose at the default settings.

  • wind_farm_flow_map gave an output point halfway between two wind climate points the wind speeds of one and the Weibull parameters of the other. site_interp_method and site_interp_bounds now apply to the wind climate too.

  • wind_farm_flow_map with air_density_correction="infer" corrected one power curve to the site’s mean air density, so its gross_aep differed from gross_aep by up to 2 %. Each output point’s power curve is now corrected to its own air density.

  • wind_farm_flow_map mapped a farm of several turbine models as if all were one of them; it now raises PywaspError, so use potential_aep for a mixed farm. With air_density_correction="infer" it used the first model in wtg instead of the one wind_turbines.wtg_key names.

  • wind_farm_flow_map no longer adds turbulence_intensity to the wind climate it is given. An explicit turbulence_intensity now replaces the wind climate’s in potential_aep and wind_farm_flow_map, as their warning said.

  • A location whose air_density is NaN had its power curve corrected to the last density in the WTG file, reporting an AEP about 8 % too high. Its AEP is now missing, with a warning, and a flow map leaves such cells out of its mean density. PywaspError is raised when no location has an air density.

  • A sector with a Weibull A of 0 contributes nothing to the AEP and wind speeds, instead of making the whole location undefined. A negative A or a non-positive k marks the sector as missing instead of raising, and a very peaked distribution no longer overflows.

  • potential_aep_deficit_sector is defined for a sector of zero frequency.

  • gross_aep with air_density_correction="none" no longer needs or reads the air density.

  • gross_aep(..., use_sectors=False) raised AlignmentError for scalar, stacked_point and cuboid wind climates.

  • Invalid input to potential_aep, wind_farm_flow_map and gross_aep raises a PywaspError naming the problem (TypeError for a wtg of the wrong type), instead of AttributeError, KeyError, UnboundLocalError or an error inside py_wake. gross_aep returned results for only as many locations as there were turbines.

  • net_aep and px_aep results carry their own metadata instead of that of potential_aep and net_aep, and net_aep updates history.

  • estimate_sensitivity_factor leaves out locations with a missing sector or zero gross AEP instead of returning an undefined factor, and raises PywaspError if no location is left.

  • predict_bwc, predict_bwc_from_site_effects and downscale_from_geostrophic_and_site_effects_to_bwc silently dropped wind speeds beyond the output histogram, which could also corrupt memory; they now raise. With a non-default n_gbins the histogram was too short, so sectors did not sum to 1.

  • Wind Atlas downscaling with interp_method="nearest", "linear" or "natural" raises when the interpolated GWC does not line up with the sites, instead of using it at the wrong points.

  • The Wind Atlas functions accept a mesoclimate with a size-one height dimension.

  • Climate generalization, downscaling and prediction no longer add flow_sep_height to the displacement a second time. Results with the released configurations, where it is 0, are unchanged.

  • predict_wwc and predict_wwc_from_site_effects sized the input heights by the number of output points, which could crash the WAsP core.

  • interpolate_gwc treats a GWC with one horizontal location and several heights as a single point, keeping its heights and taking the nearest source height for each target, where non-nearest methods failed. It keeps global attributes such as wind_dir_crs.

  • interpolate_gwc failed when output_locs has no height dimension, as a point structure, a single location or a raster with a scalar height does.

  • interpolate_gwc mixed the heights of a point GWC that has the same heights at every location (with "linear", "cubic" or "natural"), and of a multi-height stacked_point GWC with "natural", without a warning. Each source height is now interpolated on its own. A point source with several heights at some locations but not the same heights everywhere raises PywaspError.

  • get_climate returned wrong or NaN values for locations that span the ±180° dateline together with points far from it.

  • Weibull fits could bias k for histograms whose bins are not 1 m/s wide and whose sector mean lies within about one bin of 1 m/s.

  • wwc_rotate raised ValueError for a Dask-chunked wind climate.

  • add_met_fields added a size-one point dimension to a scalar wind climate.

  • bwc_resample_wsbins_like(fit_weibull=False) passes A and k to the WAsP core.

  • bwc_resample_sectors truncated a fractional offset, so offset=0.5 did nothing.

  • bwc_from_tswc keeps the input’s attributes, accepts spatial structures such as raster, and no longer raises AttributeError when it warns about bin overflow.

  • get_return_wind read a point PEWC stored (point, year) in the wrong order, and get_spec_corr_fac killed the Python process for a point time series stored (point, time).

  • get_spec_corr_fac applied a spatially misaligned n to the wrong points; it now raises.

  • stability_histogram, create_histogram_z0 and baroclinicity_histogram failed for stacked_point and cuboid inputs, and stability_histogram and create_histogram_z0 also for raster and single-height inputs with the default finalize=True.

  • create_histogram_z0(finalize=True, landmask=...) returned mean_z0 with a spurious extra dimension for stacked_point and cuboid input.

  • stability_histogram rejected a matching hist and accepted a mismatched one.

  • baroclinicity_histogram, stability_histogram and create_histogram_z0 no longer modify a supplied wv_count, so reusing an accumulator no longer double-counts a chunk. baroclinicity_histogram validates supplied count bins and keeps multi-height baroclinicity fields.

  • Finalized histograms work with process-based Dask Distributed schedulers.

  • stability_histogram and baroclinicity_histogram keep the attributes of the crs coordinate with older xarray and pyproj versions.

  • Polygon land-cover maps lost internal roughness changes at the map edge: an edge shared by two polygons, with both ends on the map boundary, was clipped as the map edge, so get_site_effects and get_rou_rose missed the roughness change behind it. A map of separate regions that does not fill its bounding box is now clipped, and a single-class map sees external_roughness beyond its edge.

  • get_rou_rose with a polygon map computed every point after the first from uninitialised memory, giving results that depended on the other points and occasional crashes. It raises CoreError if polygon conversion fails.

  • TopographyMap.get_rou_rose no longer modifies a supplied elev_rose, and raises when its points don’t match output_locs.

  • Site effects from line maps could hold negative, uninitialised RIX values on repeated calls.

  • RIX at sites outside the elevation raster used swapped bilinear weights.

  • Raster elevation maps with non-square cells used the west_east resolution for both axes, which changes RIX and site effects. Descending axes are supported.

  • Multi-height LINCOM wind-point results keep the terrain elevation two-dimensional, and placeholder site effects carry the right metadata, including a zero flow_sep_height.

  • vector_to_raster no longer always warns about return_lctable/map_type for line roughness maps.

  • A polygon roughness map with the terrain analysis of WAsP before 12.7 raises an error that suggests pw.polygons_to_lines().

  • Invalid license credentials are no longer reported as a connection failure or retried; retries could get an IP address blocked by the license server.

  • The AEP functions, get_air_density and LINCOM create_fourier_space raise WAsP core errors as themselves instead of as license errors.

  • Mesoclimate downloads fall back to the DTU Data mirror when Zenodo fails, which they never did, and raise MesoclimateMissingError when both fail.

  • pywasp configure on Windows no longer crashes when output is redirected or piped, writes the configuration file as UTF-8 so non-ASCII paths work, cancels cleanly on Ctrl+C or closed input, and exits non-zero when cancelled or failed.

  • The pywasp command works from the conda package on Windows, where it failed with “Fatal error in launcher”.

  • create_config_interactively returns the created Settings instead of None.

  • The AEP calculations work with xarray 2026.04.

Breaking changes#

  • PyWAsP requires Python 3.12 or later and windkit 2.2.

  • The default generalization heights are WAsP’s [10, 25, 50, 100, 200] instead of [10, 25, 50, 100, 250], and Weibull fits use WAsP’s setting, which lowers A by about 0.02 %. Results above 100 m change slightly, for example by about 0.2 % in mean wind speed at 150 m.

  • import pywasp no longer fetches the global mesoclimate files. The first calculation that needs them downloads them: with download_prompt = true (the default) after one y/n prompt in a terminal, and without asking where nothing can prompt. With download_global_nc_files = false (or PYWASP_DOWNLOAD__DOWNLOAD_GLOBAL_NC_FILES=False), a declined prompt or a failed download, get_air_density, get_climate and the functions built on them raise pywasp.MesoclimateMissingError instead of EOFError, FileNotFoundError or a requests exception.

  • Scalar spatial inputs give scalar outputs instead of gaining a size-one point dimension, and inputs with a scalar height coordinate, such as a raster, keep it instead of gaining a size-one height dimension. This covers gross_aep, potential_aep (also with wind_turbines), TopographyMap.get_site_effects, get_site_effects_raster, convert_to_classes, wwc_rotate, align_direction_crs, the bwc_resample_* functions, apply_lut, and Wind Atlas downscaling and prediction. Explicitly dimensional single points stay dimensional, and scalar AEP results carry no grid_mapping attribute.

  • Dimension order is not guaranteed, so compare results by label. gross_aep returns point results as (sector, point) instead of (point, sector) and keeps a descending grid axis in the wind climate’s order, where potential_aep sorts it ascending. wwc_rotate and align_direction_crs keep the input’s dimension order, and site effects returned with return_site_effects=True keep the order they were given in.

  • gross_aep raises PywaspError instead of ValueError for a Dask-chunked wind climate.

  • The generalize* functions keep the source height dimension on their wind climate variables, including a single height, as the GeoWC functions do.

  • calc_temp_scale is height-independent; its spatial outputs have no height dimension.

  • Finalized stability_histogram and baroclinicity_histogram fields keep a height dimension for multi-height inputs, and the Wind Atlas functions reject a mesoclimate with more than one independent height. Select a single height, or use point data with height(point).

  • The Wind Atlas functions take height-independent inputs once per horizontal location, so they raise PywaspError for a supplied mesoclimate whose values differ between points at the same location, or for site effects whose z0meso or slfmeso differ there.

  • baroclinicity_histogram(hist=..., finalize=True) requires wv_count, the accumulated wind-vector counts of every chunk added to hist, and raises PywaspError without it. PyWAsP 2.0 used the counts of the final chunk only, which inflated mean_dgdz.

  • TopographyMap rejects an elevation raster with NaN or Inf cells when it is constructed or loaded, raising PywaspError instead of a later ValueError. TopographyMap.elev_map is read-only.

  • TopographyMap.get_site_effects_cfd returns NaN for cfd_speedups, cfd_turnings, cfd_turbulence_intensity and cfd_flow_inclination at heights below or above the CFD volume, instead of values from its second or top level.

  • convert_to_classes requires a raster input and raises WindkitValidationError otherwise.

  • bwc_resample_wsbins_like(fit_weibull=False) requires source and target to have the same spatial structure.

  • interpolate_gwc no longer has engine="fortran"; any value other than "windkit" raises ValueError. method="natural" onto a raster, cuboid or stacked_point target has no replacement.

  • get_return_wind raises CoreError naming the cause when the Gumbel fit fails (fewer than two years, a return_period below one year, or a pewc_max_interval of 0 or less), instead of returning -999 or 0 and an ierror variable, which is removed.

  • A yearly total from gross_aep, potential_aep or wind_farm_flow_map is missing when any of its sectors is, instead of summing the sectors that are present.

  • potential_aep_deficit and wspd_deficit are the deficits of the all-sector values beside them (1 - potential_aep / gross_aep and 1 - wspd_eff / wspd), instead of a frequency-weighted mean of the sector deficits that disagreed with the AEP. A location with no energy in any sector reports them as undefined instead of 0.

  • potential_aep and wind_farm_flow_map no longer return a mode coordinate.

  • The shear argument of potential_aep and wind_farm_flow_map is removed; it had no effect. The positional arguments of wind_farm_flow_map after turbulence_intensity move up one place.

  • The generalize* and predict* functions ignore a meridian_convergence variable in the binned wind climate and compute it from the CRS instead. Results change where it was nonzero.

  • Site effects that were reprojected by hand to another CRS than they were made in may give different results in downscale_from_site_effects.

  • pywasp.wasp.cross_predict reads the site coordinates from the wind climate files instead of the CSV, whose epsg column is optional, and renames its directories: tabfiles/ to wind_climates/, mapfiles/ to maps/, landcovertables/ to landcover_tables/ and netcdf/ to cache/, which holds FlatGeobuf instead of parquet files.

Deprecations#

  • The ws_lower_limit argument of potential_aep and wind_farm_flow_map will be removed in pywasp 3.0, and any value other than 0.0 warns. A float is ignored, since the wind speeds start at 0 m/s; None still starts them at cut-in and has no replacement.

  • Deprecation warnings name the version that deprecated the feature and, where decided, the version that removes it. pywasp 3.0 removes generalize_and_downscale, the getpar and putpar methods of Config.climate and Config.terrain, user_config.check_remaining_runs, and passing a WaspVectorMap to TopographyMap.

  • check_remaining_runs and the meridian_convergence warning emit FutureWarning instead of DeprecationWarning, which Python hides unless it is triggered from __main__.

Changes#

  • License handling is lazy: import pywasp works offline and without a configuration, and the license is checked at the first licensed calculation, with actionable errors when the configuration is missing or invalid.

  • The license tier’s point limit is enforced: a larger calculation raises pywasp.PointsLimitError, so split it up. The limit is cached locally for 24 hours.

  • potential_aep and wind_farm_flow_map apply the WAsP parked-rotor convention: outside its operating range a turbine keeps its stationary thrust coefficient instead of exerting no thrust. Wake results change slightly around cut-in and cut-out.

  • potential_aep and wind_farm_flow_map simulate about a third fewer wind speed cases. AEP is unchanged, and mean wind speeds move by less than 1e-6 relative.

  • Binned wind climate histograms from Dask time series are faster: they skip eager chunk alignment, merge fine-grained time chunks, and reuse the land/sea nearest-neighbour mappings across stability fields.

  • baroclinicity_histogram raises PywaspError for unknown keyword arguments instead of ignoring them.

  • The air density warnings from potential_aep and wind_farm_flow_map point at the calling code, so warnings filters on the caller’s module work.

  • The spatial coordinates of cuboid and stacked_point results from Wind Atlas downscaling carry their CF attributes, and apply_lut keeps its V50 wind attributes.

  • The pip package declares its runtime dependencies, so pip and uv install numpy, xarray, windkit, py_wake and the rest. The conda package uses the same list and lower bounds, and tabulate is no longer a dependency.

  • The installation guide authenticates to the WAsP conda channel with pixi auth login and mamba auth login; a token embedded in the channel URL fails with mamba 2.

  • The documentation has a version switcher, and a page on how AI coding assistants are used in PyWAsP’s development, which sits with the release notes under “Development”.

  • Documentation: the WAsP flow model page documents WAsP 12.10 as the default Config, the user guide and “From WAsP to PyWAsP” have corrected examples, the LINCOM page outlines its workflow, the glossary defines the wind climate abbreviations, and the API reference lists more functions. The user guide and gallery load their data with windkit.load_tutorial_data.

2.0.0 (2026-01-29)#

PyWAsP 2.0.0 contains many bugfixes that have been found since the 1.0 release, and includes better support for the geostrophic wind climate.

Bug fixes#

  • Increased maximum number of iterations to solve the geostrophic drag from the drag law from 10 to 50. This fixed some edge cases with high geostrophic wind shear, where the previous iterations limit was hit.

  • Ensure that frequencies are always positive in bwc_resample_like. Fixes issue where numerical noise could cause them to go below zero. These are obviously unphysical, and resulted in negative moments in some edge cases when interpolating geostrophic wind climates over the whole european domain.

  • Set correct dimensions of hr1, hi1, hi2 in fortran routine used by get_elev_rose to prevent code from hanging.

  • Removed boolean attributes that were added in bwc_from_tswc to allow the result to be written to NetCDF.

  • Bug fix in internal functions of potential_aep when passing wtg in a de-rated or storm mode.

  • Made float into double for polygons_to_lines conversion: in some edge case the routines could create maps with errors, due to lack of precision.

New features#

  • Added new function roughness_to_landcover in pywasp which converts a roughness raster to a landcover map, with optional simplification.

  • stability_histogram now supports all spatial structures instead of only cuboids

  • Updated pw.wasp.Config to use exact WAsP version numbers.

  • Added fill_value argument to interpolate_gwc, which controls the behaviour for points that are outside the convex hull given by the generalized wind climate. For method='nearest' results are also returned outside the convex hull. The function also works correctly for geostrophic wind climates now.

  • You can now use the function downscale_from_site_effects to downscale both a generalized and geostrophic wind climate.

  • New command-line interface: pywasp configure for interactive license setup and pywasp status to check license status and remaining runs.

  • New example: “Generalized wind climate interpolation”

  • New example: “Polygon and change line conversions”

Performance improvements#

  • Performance improvement in potential_aep and wind_farm_flow_map: vectorized power curve operations using Numba with parallel processing, replacing iterative Fortran calls. Also switched from single to double precision for improved numerical accuracy.

  • Function pw.bwc_from_tswc is now twice as fast

Breaking changes#

  • downscale, downscale_from_site_effects, downscale_from_geostrophic_and_site_effects_to_bwc, predict_wwc and predict_bwc all use interp_method="nearest" by default. If you are using more than one observation as input this will change your results. interp_method="cubic" has been removed as option, because it can give unrealistic results for strongly varying gwc’s.

  • downscale_from_geostrophic_and_site_effects_to_wwc is deprecated, and has been made private. You should now use the function downscale_from_site_effects (see above).

  • Revamped pydantic based WAsP configuration, which is still experimental. Renamed it from PywaspConfig to Params (pywasp.wasp.params.Params); updated it to better follow pydantic syntax; removed initialization by WAsP version string, instead have to call the from_par_set class method.

Documentation#

  • New updated PyData sphinx theme

  • Improved Installation documentation with tabs for conda/mamba vs pixi, collapsible troubleshooting sections, and clearer step-by-step instructions.

  • Moved several parts of pywasp docs to windkit docs (where they belong)

1.0.1 (2025-08-05)#

PyWAsP 1.0.1 is a bugfix release to fix an issue with the root_ca_filepath license option.

1.0.0 (2025-06-20)#

PyWAsP 1.0.0 is a major release that introduces a significant number of new features, improvements, and breaking changes aimed at improving the user experience, consistency, and performance. The API has been cleaned up, and many functions have been renamed for clarity. This version also introduces a new configuration system and enhanced energy yield calculation capabilities.

Dependency updates for 1.0#

  • Supports Python 3.11 to 3.13

  • Supports numpy 1.26 to 2.2 (1.0 will be last version to support numpy 1)

  • Supports PyWake 2.6

  • Supports windkit 1.0

Code restructuring and API cleanup#

  • Updated user config: The user configuration has migrated from pywasp.cfg to a pydantic-based settings system. Users must create a pywasp_config.toml file or a .env file, or define environment variables. See the User Configuration documentation for details. A helper function pywasp.user_config.create_config_interactively() can be used to create a new configuration file interactively.

  • API Renaming and Cleanup:

    • The pywasp.config module is renamed to pywasp.user_config.

    • pywasp.wasp.bwc_from_timeseries is renamed to pywasp.wasp.bwc_from_tswc.

    • pywasp.io.rastermap_to_vectormap is renamed to pywasp.io.raster_to_vector.

    • pywasp.io.vectormap_to_rastermap is renamed to pywasp.io.vector_to_raster.

  • Argument Renaming for Consistency:

    • The return_site_factors argument is renamed to return_site_effects in all downscale, predict, and generalize functions.

    • Arguments for the number of wind speed bins (e.g., nwsbins, n_bins) are standardized to n_wsbins.

    • Arguments for the number of sectors (e.g., nsec, nwd) are standardized to n_sectors.

    • The ws_bin_width argument is renamed to wsbin_width.

    • In pywasp.wasp.gross_aep, interpolate is renamed to air_density_correction, and air_density is renamed to reference_air_density.

    • In pywasp.io.vector_to_raster, arguments xmin, ymin, xmax, ymax are replaced by a single bounds tuple.

  • Removed Functionality:

    • The following functions and classes have been removed from the public API: pywasp.io.rastermap_to_waspformat, pywasp.io.vectormap_to_waspformat, pywasp.wasp.wtg_to_pywake, pywasp.lincom.grid_from_wasp_rastermap, pywasp.io.WaspVectorMap, and pywasp.io.WaspRasterMap.

  • Air density correction:

    • In pywasp.wasp.gross_aep, pywasp.wasp.potential_aep, a new air_density_correction argument was added, which defaults to "infer", which tries to infer the best air density correction method based on the WTG’s control system.

    • pywasp.io.raster_to_vector now returns polygons instead of change lines for roughness or landcover maps.

    • Landcover tables are no longer required for pywasp.io.raster_to_vector and pywasp.io.vector_to_raster.

  • Metadata Changes:

    • The _AEP suffix in metadata attributes from AEP functions is now lowercase (_aep).

    • The metadata variable aep_deficit is renamed to potential_aep_deficit.

New features#

New energy yield calculation functions#
  • New function pywasp.wasp.net_aep to calculate the Net Annual Energy Production (AEP) by applying losses from a loss table.

  • New function pywasp.wasp.estimate_sensitivity_factor to estimate the sensitivity factor of a wind effect to AEP.

  • New function pywasp.wasp.px_aep to calculate the AEP level reached with a given probability.

Landcover and I/O#
  • New function pywasp.landcover.convert_to_classes that adds new classes to a landcover raster map using tree heights and leaf area indices.

  • New function pywasp.io.polygons_to_lines that converts polygons to lines, approximately 100x faster than the windkit equivalent.

  • pywasp.wasp.TopographyMap now accepts GeoDataFrames with Polygons.

Full list of changes#

  • New function pywasp.landcover.convert_to_classes that adds new classes to a landcover raster map using tree heights and leaf area indices raster maps.

  • New keyword arugment offset in bwc_resample_sectors that allows rotating a histogram with a specified number of degrees

  • Bug fix in interpolate_gwc: variables “m1”, “m3”, “fgtm” were not always removed. They are private variables that should never be visible to the user.

  • get_air_density can now deal with larger than RAM memory dataset as input. The custom dataset can also be in any projection, whereas before it assumed it was always in latlon

  • Bug fix in get_climate, did not work when bbox crosses dateline”

  • New function polygons_to_lines, that convert polygons to lines similar to the function windkit.poly_to_lines but approximately 100x times faster.

  • In interpolate_gwc for interpolation of single point to many using keyword engine=”windkit”, could give wrong results when the output structure was not of the same spatial structure as the input spatial structure.

  • PyWake is pinned to version 2.6.8 (commit 5562afda7f272ab029bf057617557c5de14416e5).

  • New functionality available, potential_aep and wind_farm_flow_map functions can now correct for site-specific air density conditions.

  • In gross_aep, potential_aep and wind_farm_flow_map, the argument air_density_correction is set to True by default.

  • potential_aep can now take different types of WTGs within the same wind farm group. Following the same structure as gross_aep.

  • TopographyMap now allows input of Polygon dataframes, two extra keyword arguments check_errors and external_roughness determine the behaviour when dealing with these maps. The external roughness length specifies the roughness outside the polygons in the provided map.

Breaking changes#

  • renamed pw.wasp.bwc_from_timeseries to pw.wasp.bwc_from_tswc

  • renamed pw.rastermap_to_vectormap to pw.raster_to_vector

  • renamed pw.vectormap_to_rastermap to pw.vector_to_raster.

  • removed pw.rastermap_to_waspformat from public namespace

  • removed pw.vectormap_to_waspformat from public namespace

  • removed pw.WaspVectorMap from public namespace

  • removed pw.WaspRasterMap from public namespace

  • removed pw.lincom.grid_from_wasp_rastermap from public namespace

  • Argument return_site_factors has been renamed to return_site_effects in the following functions:

    • downscale

    • downscale_from_site_effects

    • downscale_from_geostrophic_and_site_effects_to_bwc

    • downscale_from_geostrophic_and_site_effects_to_wwc

    • predict_bwc

    • predict_bwc_from_site_effects

    • predict_wwc

    • predict_wwc_from_site_effects

    • generalize_from_site_effects_to_geowc

    • generalize

  • Argument nwsbins, nws, n_bins has been renamed to n_wsbins in the following functions:

    • bwc_from_tswc

    • stability_histogram

    • create_histogram_z0

  • Argument nsec, nsecs, nwd, nbins has been renamed to n_sectors in the following functions:

    • bwc_from_tswc

    • stability_histogram

    • create_histogram_z0

    • get_elev_rose

    • get_rou_rose

    • get_site_effects

    • get_site_effects_cfd

    • weibull_fit

    • generalize

    • predict_wwc

    • predict_bwc

  • Argument ws_bin_widthhas been renamed to wsbin_width in the following functions:

    • bwc_from_tswc

    • stability_histogram

    • create_histogram_z0

  • Argument interpolate in gross_aep has been renamed to air_density_correction, air_density has been renamed to reference_air_density.

  • _AEP suffix in attribute names of the metadata from pywasp.wasp.aep, pywasp.wasp.aep_losses.py, pywasp.wasp.aep_uncertainty.py have been changed to _aep

  • Metadata variable name aep_deficit has been renamed to potential_aep_deficit

  • No longer require landcover tables for raster_to_vector and vector_to_raster

  • Replaced arguments xmin,ymin,xmax,ymax in vector_to_raster to bounds (tuple or BBox of same values)

  • raster_to_vector now returns polygons instead of change lines when the input is a roughness or landcover map.

  • Renamed pywasp.config to pywasp.user_config to avoid confusion with the pywasp.wasp.Config class.

  • The “Roaming” AppData dir is now used on Windows. By default the config and app data files are stored in "C:\\Users\\<user_name>\\AppData\\Roaming\\DTU Wind Energy\\pywasp"

  • Removed function pywasp.wasp.wtg_to_pywake (from public namespace)

  • Replaced the gwc interpolation function in pw.wasp.downscale and pw.wasp.downscale_from_site_effects with the new wk.spatial_interpolate_gwc function.

    • This replaces the legacy gwc interpolaters in pywasp

    • With the update only “point” targets are supported for “natural” interpolation, while “nearest”, “linear”, and “cuboid” still works for all spatial structures.

    • Changed the interp_method default to “nearest” instead of “given”.

  • PyWAsP user configuration has migrated to using pydantic settings. This means that your pywasp.cfg will no longer work. You will need to either create a pywasp_config.toml file, a dotenv file, or define environment variables with your configuration settings for PyWAsP to import successfully. You can find the necessary information in the PyWAsP License and User Configuration documentation.

New energy yield calculation functions:#

  • New function pywasp.wasp.net_aep to calculates the Net Annual Energy Production (AEP) by applying the losses from a loss table.

  • New function pywasp.wasp.estimate_sensitivity_factor to estimate the sensitivity factor of wind effect to AEP for a given predictive wind climate and wtg.

  • New function pywasp.wasp.px_aep to calculate the Annual Energy Production (AEP) level reached with a given probability.

0.7.0 (2024-06-04)#

PyWAsP 0.7.0 is a major release with new features, improvements, and bug fixes.

Dependency updates#

  • Following SPEC 0 this release supports python 3.10-3.12 and numpy 1.24-1.26.

  • Windkit =0.8.0 is required

  • PyWake is pinned to version 2.5.0 (commit 36da70b2335321e435194d277511c17d8f012571).

New features#

New climate extrapolation functions#
  • New function pywasp.wasp.predict_wwc to predict a weibull wind climate from a binned wind climate without using a generalized wind climate lookup-table, thus reducing interpolation errors

  • New function pywasp.wasp.predict_bwc, same as above but to predict a binned wind climate instead.

  • New function pywasp.wasp.predict_wwc_from_site_effects, same as pywasp.wasp.predict_wwc but using the outputs of calls to pywasp.wasp.TopographyMap.get_site_effects instead of a mapfile.

  • New function pywasp.wasp.predict_bwc_from_site_effects, same as pywasp.wasp.predict_bwc but using the outputs of calls to pywasp.wasp.get_site_effects instead of a mapfile.

  • New function pywasp.wasp.generalize_from_site_effects, same as pywasp.wasp.generalize but using the output of a call to pywasp.wasp.TopographyMap.get_site_effects instead of a mapfile.

  • New function pywasp.wasp.generalize_from_site_effects_to_geowc, generalize a binned geostrophic wind climate from a site effects dataset.

  • New function pywasp.wasp.downscale_from_geostrophic_and_site_effects_to_bwc, downscale a binned geostrophic wind climate from a site effects dataset and geostrophic binned wind climate

BWC resampling functions added#

  • New function pywasp.wasp.bwc_resample_like, to resample a binned wind climate using a different sector or wind speed bin structure.

  • New function pywasp.wasp.bwc_resample_sectors, to resample a binned wind climate using a different number of sectors.

  • New function pywasp.wasp.bwc_resample_wsbins_like, to resample a binned wind climate using a different wind speed bin structure.

I/O added to TopographyMap and new CFD data-extraction method#
  • New methods .save and .load added to pywasp.wasp.TopographyMap to save and load the topography map to/from a ZipFile Archieve.

  • pywasp.wasp.get_site_effects_cfd also accepts a list of xr.datasets (read by the read_cfdres function in windkit) as input. It will extract the site effects in the order specified in the list.

Support for custom root CA certificates#

  • In case of running pywasp under a proxy or another similar network setup, the filepath to a root CA certificate can be set on pywasp so it can connect to the licensing server.

Improvements#

  • For typical usage pywasp.wasp.TopographyMap.get_site_effects is 2-6 times faster

  • For typical usage pywasp.io.rastermap_to_vectormap is 15-30 times faster

  • For typical usage pywasp.io.vectormap_to_rastermap is 15-30 times faster

  • For typical usage pywasp.io.vectormap_to_waspformat is 2-20 times faster

  • For typical usage pywasp.io.waspformat_to_vectormap is 2-80 times faster

  • For typical usage pywasp.wasp.TopographyMap is >1000 times faster

  • Improved input error checking in pywasp.wasp.generalize()

  • Improved input error checking in pywasp.wasp.generalize(), check that the gen_roughnesses and gen_heights have at least 2 entries, as required by the WAsP core.

  • Improved input error checking in pywasp.wasp.TopographyMap: make sure that all your landcover lines have a corresponding entry in the landcover table.

  • More accurate setting of generalized heights and roughness using the function pywasp.wasp.set_hgts and pywasp.wasp.set_z0s. Now a percentile mapping technique is used when the number of heights or roughnesses exceeds the maximum number of entries allowed in a gwc (5).

  • More descriptive error codes when having problems reading a pywasp config file.

  • TopographyMap now throws an error when the lctable has more than 100 entries. Before this condition was unhandled and could crash the fortran.

  • pywasp.wasp.Config object now is read only and does not do any modifications of the fortran behaviour itself (before it was creating lookup tables and setting some parameters on each update of conf.terrain.)

Deprecations#

  • WaspVectorMap will be deprecated

  • windkit.WindTurbines deprecated for AEP functions. Use a dict of WTG xr.Dataset’s and a wind_turbine xr.Dataset instead.

Changes#

  • pywasp.wasp.generalize now uses pywasp.wasp.generalize_from_site_effects instead of duplicating code

  • pywasp.wasp.interpolate_gwc -> Can now also interpolate geostrophic wind climates. Code from windkit has been implemented in pywasp and can be used by using the argument engine="windkit".

  • pywasp.wasp.TopographyMap.get_site_effects now reports an extra variable flow_sep_height, which can report the height where flow separation is expected based on slopes exceeding some given slope. The flow_sep_height is only active when using conf.terrain[64] == 1.

  • In all the downscale and generalize function an additional variable flow_sep_height can be used. This is added to the displacement height that is used for transformation of the wind climate. If this data array is not present on the site_effects dataset, it is added automatically.

  • Using a point dataset with multiple heights now gives a warning instead of a ValueError, because it is typically used in combination with pywasp.wasp.interpolate_gwc and interp_method=”given” where it is fine to have more than one height.

Breaking changes#

  • pywasp.wasp.get_wasp_down renamed to pywasp.wasp.downscale_from_site_effects

  • Changed order of arguments in pywasp.wasp.generalize_and_downscale to bwc, topo_map, output_locs for consistency with generalize and downscale

  • pywasp.wasp.set_hgts -> returns slightly more accurate generalized roughnesses when height_from and height_to are provided

  • pywasp.wasp.set_z0s -> returns slightly more accurate generalized roughnesses when z0meso_from and z0meso_to are provided

  • pywasp.wasp.calc_temp_scale the output of the dataset now returns the correct name ustar_over_pblh

  • pywasp.wasp.stability_histogram now produces a dataset with the more correct names mean_pblh_scale_land and mean_pblh_scale_sea instead of mean_pblh_land and mean_pblh_sea. See for more details: https://link.springer.com/article/10.1007/s10546-023-00803-3

  • genwc_interp argument in downscale and generalize has been renamed to interp_method

  • pywasp.wasp.gross_aep, pywasp.wasp.potential_aep, and pywasp.wasp.wind_farm_flow_map now uses a dict {"wtg_key1": wtg1, "wtg_key2": wtg2} and a turbines xarray.Dataset instead of a windkit.WindTurbines object. WTGs are mapped to turbines via the wtg_keys present in both the dict and the turbines dataset.

0.6.0 (2023-11-30)#

Dependency updates#

  • Following SPEC 0 this release supports python 3.10-3.12 and numpy 1.22-1.26. Due to build issues with numpy 1.26, neither it nor python 3.12 are supported on Windows.

  • Windkit =0.7.0 is required

  • PyWake is now pinned to version 2.5.0 (commit 36da70b2335321e435194d277511c17d8f012571).

New features#

Support for WAsP CFD results#
  • New function pywasp.wasp.get_site_effects_cfd interpolates the speedups and turnings from a windkit CFD volume xarray.Dataset.

  • pywasp.wasp.generalize, pywasp.wasp.downscale, and pywasp.wasp.generalize_and_downscale now have an extra cfd_volume argument, which can be used to pass in WAsP CFD results that will be used for the site effects.

Breaking changes#

  • pywasp.wasp.wind_farm_flow_map now requires a wk.WindTurbines object, not separate wtg and locations arguments.

  • pywasp.wasp.wind_farm_flow_map requires the new argument output_locs, which should be a “cuboid” xr.Dataset, which is where the flow map will be calculated. output_locs should be covered by the predicted wind climate object.

Changes#

  • Interpolation errors have been eliminated during the downscale step when using pywasp.wasp.generalize_and_downscale, by including the output heights in the generalize step.

  • Update ERA5 mesoclimate netCDF file according to latest revision of Using Observed and Modelled Heat Fluxes for Improved Extrapolation of Wind Distributions.

  • pywasp.wasp.gross_aep and pywasp.wasp.potential_aep can now accept a wk.WindTurbines object to the wtg argument. This allows for different WTGs to be used for different turbine locations.

  • Added interp_method argument to pywasp.wasp.get_climate, “nearest” is used by default

Improvements#

  • Reduction of approximately 50% for calculations of wake-affected AEP.

Bug Fixes#

  • PyWAsP on windows now can use the license.windenergy.dtu.dk licensing server.

  • Interpolation of mean_dgdz and mean_dgdz_dir are now carried out by interpolating the vector components, when a method other than "nearest" is used.

Deprecations#

  • Deprecated options “PARK2_onshore_with_blockage” and “PARK2_offshore_with_blockage” in pywasp.wasp.wind_farm_flow_map and pywasp.wasp.potential_aep. Use, custom PyWake wind_farm_models to build complex models instead.

0.5.2 (2023-06-21)#

Changes#

  • Dependency updates: Python 3.8 & numpy 1.21 no longer supported following NEP 29. Windkit 0.6.3 is now required to support the regulation_type variable for wtg objects.

  • pywasp.wasp.TopographyMap.get_rou_rose now allows to add displacements to the orographic grid, using the new elev_rose argument. If elev_rose is None, a dummy elevation rose is created.

  • pywasp.wasp.interpolate_gwc now returns an interpolated generalized wind climate dataset with height coordinates.

  • Setting download_prompt=False and download_global_nc_files in the pywasp.cfg file, will not prevent any text from showing when importing PyWAsP. Previously it would print a message about how to get the files on each import. If you need the files, you will be prompted to download at that time. See the User Configuration Documentation to see all options of the config file.

Improvements#

  • Update netCDF file with CFSR baroclinicity information. The data is the same but the data variable names are updated. It does not affect the behavior.

  • pywasp.wasp.interpolate_gwc will issue a warning if it is used with datasets with geographical coordinates for “nearest” and “natural” methods.

Bug Fixes#

  • pywasp.io.rastermap_to_vectormap will only raises errors related with the parameter dz when the raster map type is elevation.

  • pywasp.wasp.aep.gross_aep incorrectly assumed most wind turbines were stall regulated. This means that when using the interpolation=True option, that incorrect air density corrections were applied. This has been fixed by an update in WindKit that requires the control_system variable to be defined on a wind turbine generator. This value is now used to apply the correct air density correction to the power curves.

0.5.1 (2023-04-19)#

New Features#

  • pywasp.wasp.get_climate_by_config takes a pywasp.wasp.Config object and returns an xarray.Dataset meso climate object compatible with the profile model set in the config.

Changes#

  • pywasp.wasp.get_climate arguments stab_source and baro_source now both allow for None to be set. This creates a dataset with 0-valued fields for the given source.

Improvements#

  • Update netCDF file with CFSR baroclinicity information to version 3. The data is the same, but the data variable names are updated. This does not affect the behavior.

Bug Fixes#

  • Fixed license check error for new version of DTU License server

  • Corrected import of combine_bwcs in pywasp.wasp.cross_predict.

  • pywasp.wasp.interpolate_gwc now returns an interpolated generalized wind climate dataset with height coordinates. This makes it more robust to different spatial structures of generalized wind climates and output_locs, when used for downscaling.

Deprecations#

  • getpar and putpar methods of pywasp.wasp.config.Config objects are deprecated. Use square brackets instead.