Skip to main content

An API for working with Bladed 5.

Project description

Bladed 5 Python application API - dnv-bladed-api

A package to:

  • Automate the Bladed 5 application natively from Python, in an idiomatic and performant way (currently Windows only)
  • Create input models in memory and/or as JSON files, with typing support in the IDE, and runtime verification, to ensure inputs are constructed in the correct format.

Simulation results can be processed using the related dnv-bladed-results package.

Visit https://mysoftware.dnv.com/knowledge-centre/bladed-5 for more information.

Prerequisites

  • Python 3.9 or above
  • Bladed 5 application installed with a valid licence, in order to use the BladedClient class - currently only available on Windows

Installation

The package is available on PyPI and can be installed using pip:

pip install dnv-bladed-api==5.0.*

The major and minor version numbers should match the version of Bladed you are using, while the patch version can be left as a wildcard to get the latest patch release.

Tutorials and further documentation can be found at: https://mysoftware.dnv.com/knowledge-centre/bladed-5

Usage

Access all application functions with a BladedClient object on Windows

import dnv_bladed_api

# Create a client object once at the start of a script
bladed = dnv_bladed_api.BladedClient()

All of the application commands available via the Bladed CLI are also available via a BladedClient object in Python.

Create a single client object for use in your script, to invoke multiple commands as required.

Running simulations on Windows

Invoke the simulate function on the client object to run a simulation.

bladed.simulate('path/to/analysis.json', output_dir='path/to/results', log_to_console=True, verbosity='warning')

Explore all available parameters via the doc string.

An BladedCommandError is raised if the simulation does not complete successfully.

Converting Bladed 4 project files on Windows

Invoke the convert_bladed4 function on the client object to convert a Bladed 4 project to a Bladed 5 JSON model. The same behavior and restrictions apply to this function as they do to the CLI command.

This function always returns a result object, that describes the status of the conversion; it never raises an error. So the result object should be inspected to understand the outcome.

The converter takes an optimistic approach, and will produce an output file when at all possible, even if there were issues encountered with some of the models.

More severe issues, that will likely prevent the output file from yielding a successful simulation, are recorded as errors.

It's likely that errors or warnings are produced during the conversion (even though an output file is produced), that you should understand before proceeding to use the output file.

result = bladed.convert_bladed4('path/to/project.prj', output_dir='converted-projects/')

if result.conversion_aborted:
  print("Conversion was aborted. check logs for details.")
elif result.converted_with_errors:
  print("Conversion completed with errors that must be considered. check logs for details.")
elif result.converted_with_warnings:
  print("Conversion completed with warnings that should be considered. check logs for details.")
else:
  print("Conversion completed successfully with no issues.")

Resolve distributed input files on Windows

Bladed 5 JSON models can be divided and distributed across multiple physical files (using the $insert keyword). They can then be 'resolved' or re-combined into a single model for processing when required.

The BladedClient object provides several different ways to perform the resolve operation, depending on your use case.

A. Resolve a distributed model into a Python object

The most common case for Python scripting is when a distributed model needs to be resolved into a single Python object, and then some analysis or visualisation performed on the full model in Python code.

The resolve_to_object function should be used in this case:

import dnv_bladed_api.models as models

resolved_blade = bladed.resolve_to_object('path/to/blade.json', cls=models.simulate.Blade)

NOTE: The cls parameter is required in this case, to specify which type of model object is expected to be created.

The less common case of resolving an object that contains distributed references, into another object, can be performed with the same resolve_to_object function, by providing a model object as the source parameter:

tower = models.simulate.Tower.from_file('./path/to/tower.json')
resolved_tower = bladed.resolve_to_object(tower)

B. Resolve a distributed model directly to a file

The resolve_to_file function is also available, to output the resolved model directly to a file.

This can be invoked in two ways:

To resolve a distributed model stored in a file:

import dnv_bladed_api.models as models

bladed.resolve_to_file('path/to/blade.json', 'path/to/resolved_blade.json')

To resolve a distributed model in a python object:

distributed_tower = models.simulate.Tower.from_file('./path/to/tower.json')
bladed.resolve_to_file(distributed_tower, './path/to/resolved_tower.json')

Validate models on Windows

Invoke the 'validate' application command via the client object.

Validate a model object:

import dnv_bladed_api.models as models

tower = models.simulate.Tower.from_file('path/to/tower.json')

# raises an error if fails validation
bladed.validate(tower)

# returns a result object for inspection
if not bladed.check_validity(tower).is_valid:
    print("Tower is invalid")

Validate a JSON file directly:

import dnv_bladed_api.models as models

# raises an error if fails validation
bladed.validate('path/to/tower.json')

# returns a result object for inspection
if not bladed.check_validity('path/to/tower.json').is_valid:
    print("Tower is invalid")

Create input models

import dnv_bladed_api.models as models

There are hundreds of model classes in the Bladed Simulate schema.

The root model is BladedAnalysis; this is the root input required by the 'simulate' command.

The same capabilities are available for all model classes; each one can be read and written to JSON individually, as demonstrated below.

Load a full Bladed JSON analysis model from file

analysis = models.simulate.BladedAnalysis.from_file('/path/to/analysis.json')

This will perform some validation of the input to ensure the structure adheres to the input schema.

Save a model to a JSON file

analysis.to_file('/path/to/file.json')

The JSON file can then be opened in VS Code, and will automatically receive validation, doc-string and auto-complete support against the Bladed JSON Schema.

Load a model from a JSON string

analysis = models.simulate.BladedAnalysis.from_json(json_str)

This will perform some validation of the input to ensure the structure adheres to the input schema.

Render a model as a JSON string

json_str = analysis.to_json()

Create a new model object in code

Create a new instance of the BladedAnalysis model object:

analysis = models.simulate.BladedAnalysis()

A model object can be created with an empty initialiser as shown above, or by specifying some or all of the child models as keyword arguments:

beam = models.simulate.LidarBeam(
    MountingPosition=models.simulate.LidarMountingPosition(
        X=1,
        Y=2,
        Z=3
    )
)

Modify a model object in code

If a model object is already loaded, properties can be modified as required:

analysis.Constants.AirCharacteristics.Density = 1.23

Manipulate the turbine assembly

Access existing component definitions:

# Access a known existing component by it's key, ensuring the correct type
blade = analysis.Turbine.ComponentLibrary.Component_as_Blade('blade')

# Iterate over all component entries...
for key, component in analysis.Turbine.ComponentLibrary.items():
    print(f"Component key: {key}, Component type: {component.ModelType}")

Access existing nodes in the Assembly tree using string and integer accessors:

blade_node = analysis.Turbine.Assembly['Hub']['PitchSystem_1'][0]

# or
blade_node = analysis.Turbine.Assembly['Hub']['PitchSystem_1']['Blade']

# or
blade_nodes = [ps_node[0] for node_name, ps_node in analysis.Turbine.Assembly['Hub'].items()]

Add new nodes and component definitions:

analysis.Turbine.ComponentLibrary['MyHub'] = models.IndependentPitchHub()
analysis.Turbine.ComponentLibrary['MyPitchSystem'] = models.PitchSystem()
analysis.Turbine.ComponentLibrary['MyBlade'] = models.Blade()

hub_node = models.simulate.AssemblyNode(
    Definition = "ComponentLibrary.MyHub"
)
for i in range(1, 4):
    blade_node = models.simulate.AssemblyNode(
        Definition = "ComponentLibrary.MyBlade"
    )
    ps_node = models.simulate.AssemblyNode(
        Definition = "ComponentLibrary.MyPitchSystem"
    )
    ps_node[f'Blade'] = blade_node
    hub_node[f'PitchSystem_{i}'] = ps_node

analysis.Turbine.Assembly['Hub'] = hub_node

Change a model to an alternative choice

Some model properties can be set to one of a number of different model types, to allow a choice between different options available in the calculation.

The property must simply be set to an object of one of the valid types. The valid types available are included in the doc strings, and the schema documentation available at https://mysoftware.dnv.com/knowledge-centre/bladed-5.

The example below is for dynamic stall. The detail of setting the specific properties on each model is omitted for brevity:

analysis.Settings.Aerodynamics.DynamicStall = models.simulate.OyeModel()

# or
analysis.Settings.Aerodynamics.DynamicStall = models.simulate.IAGModel()

# or
analysis.Settings.Aerodynamics.DynamicStall = models.simulate.CompressibleBeddoesLeishmanModel()

# or
analysis.Settings.Aerodynamics.DynamicStall = models.simulate.IncompressibleBeddoesLeishmanModel()

Working with type checking tooling

If using type checking development tooling, there are helper methods available to access typed references to values that could be one of several types.

For example, to obtain a reference to the DynamicStall value, the following method can be used. An error will be raised at run time if the type specifier cannot be honoured by actual objects.

The oye variable will receive a type of 'OyeModel' by the type engine, and at runtime is assured to receive an object of that type, or an error is raised.

oye = analysis.Settings.Aerodynamics.DynamicStall_as_OyeModel

Additionally, a reference to the union of all possible types can be obtained using the 'as' methods (this raises an error if the object is specified with an 'insert'):

dynamic_stall = analysis.Settings.Aerodynamics.DynamicStall_as_inline

Similar methods are available for libraries and arrays.

# Get the tower object as a specific reference from the library
tower = analysis.Turbine.ComponentLibrary.Component_as_Tower('tower')

# Get a Tower Can by index, as a reference to a specific type
first_simple_can = tower.Cans_element_as_SimpleTowerCan(0)

# or process tower cans using a union of all possible types
for i, can in enumerate(tower.Cans_as_inline):
    can.CanHeight = i * 10

Working with distributed files ($insert)

The API can be used to:

  • Separate out a JSON file into multiple distributed files
  • Read in individual files that have been distributed
  • Resolve distributed files back into a single object (using the resolve methods on the BladedClient class, see above)

Given the following JSON:

{
    "SteadyCalculation": {
        "$type": "AerodynamicInformation",
        ...
    },
    "Constants": {
        "AccelerationDueToGravity": 9.8100004196167,
        ...
    }
}
  1. Read in the document from a file:

    analysis = models.simulate.BladedAnalysis.from_file(analysis_file)
    
  2. Extract objects to separate files, and record the path reference in the owning object:

    analysis.SteadyCalculation_as_inline.extract_to_insert_from_file('steady-calc/aero_info.json', analysis_file)
    
    assert analysis.SteadyCalculation.is_insert == True
    
    analysis.Constants.extract_to_insert_from_file('constants.json', analysis_file)
    
    assert analysis.Constants.is_insert == True
    

    These operations will write new JSON files relative to the directory of analysis_file, containing the JSON representation of the respective object.

    i.e.

    In the directory of analysis_file:

    • steady-calc/aero_info.json : Will contain the full JSON representation of the AerodynamicInformationCalculation model object.

    • constants.json : Will contain the full JSON representation of the Constants model object.

  3. Write out the updated analysis object to file, that now contains '$insert' references:

    analysis.to_file(analysis_file)
    

    The following JSON will be written:

    {
        "SteadyCalculation": {
            "$insert": "steady-calc/aero-info.json"
        },
        "Constants": {
            "$insert": "constants.json"
        }
    }
    
  4. Read in a JSON document that contains inserts:

    distributed_analysis = models.simulate.BladedAnalysis.from_file(analysis_file)
    
    # Test and inspect
    assert distributed_analysis.SteadyCalculation.is_insert == True
    print(distributed_analysis.SteadyCalculation.insert)
    
    # Attempting to treat the now distributed model object as 'in-line', will yield an error
    try:
        steady_calc = distributed_analysis.SteadyCalculation_as_inline
    except ValueError as e:
        print(e)
    
    # Update the insert location
    distributed_analysis.SteadyCalculation.insert = 'new-dir/aero-info.json'
    

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dnv_bladed_api-5.0.0.tar.gz (272.0 kB view details)

Uploaded Source

File details

Details for the file dnv_bladed_api-5.0.0.tar.gz.

File metadata

  • Download URL: dnv_bladed_api-5.0.0.tar.gz
  • Upload date:
  • Size: 272.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.20

File hashes

Hashes for dnv_bladed_api-5.0.0.tar.gz
Algorithm Hash digest
SHA256 cd9ee1ce69dd028479e8d3a5eda099dbdf3fd64d0794ede7dcb824e6b88e4427
MD5 bc6b389ce8c7e9b475c0eb75a5ec8634
BLAKE2b-256 59d8400f402f98a00d4c115d44e945d50e53d45184fd9daf73b352fada7d4bc2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page