pyLOM.RL#

Module contents#

class pyLOM.RL.BaseSolver[source]#

Bases: ABC

Base class for all solvers. It defines the interface for the solvers.

abstract __call__(shape)[source]#

Compute the reward for the given shape. :param shape: Shape to compute the reward for.

Returns:

Reward for the shape.

Return type:

float

class pyLOM.RL.XFoilSolver(alpha: float, mach: float, Reynolds: float = None, check_selfintersection: bool = False, xfoil_params: dict = {})[source]#

Bases: BaseSolver

XFoil solver for airfoil analysis. It uses the XFoil library to compute the lift and drag coefficients. Under the hood, it uses the XFoil class from aerosandbox. Requires XFoil to be on installed your machine; XFoil is available here: RobotLocomotion/xfoil And xfoil to be in your PATH. You can check this by running which xfoil in your terminal.

Parameters:
  • alpha (float) – Angle of attack in degrees.

  • mach (float) – Mach number.

  • Reynolds (float, optional) – Reynolds number. If not provided, inviscid mode is used. Default is None.

  • check_selfintersection (bool, optional) – If True, check for self-intersection of the airfoil when computing lift/drag. If the airfoil self-intersects, XFoil will return NON_CONVERGED_REWARD. Default is False.

  • xfoil_params (dict, optional) – Additional parameters that are passed to the XFoil class. For example, you can set xfoil_params={“xfoil_repanel”: True} to repanel the airfoil. Default is {}.

class pyLOM.RL.NeuralFoilSolver(alpha: float, Reynolds: float, model_size: str = 'xxxlarge')[source]#

Bases: BaseSolver

NeuralFoil solver for airfoil analysis. It uses the NeuralFoil library to compute the lift and drag coefficients.

Parameters:
  • alpha (float) – Angle of attack in degrees.

  • Reynolds (float) – Reynolds number.

  • model_size (str, optional) – Size of the model. Default is “xxxlarge”. Other options are “xxsmall”, “xsmall”, “small”, “medium”, “large”, “xlarge”, “xxlarge”, “xxxlarge”.

class pyLOM.RL.DummySolver[source]#

Bases: BaseSolver

Dummy solver for airfoil analysis. It returns a constant reward for all airfoils. This is useful for testing purposes.

class pyLOM.RL.AerosandboxWingSolver(velocity, alpha, atmosphere, model_size='xxxlarge')[source]#

Bases: BaseSolver

Aerosandbox solver for wing analysis. It uses the AeroBuildup component from the Aerosandbox library to compute the lift and drag coefficients. For more details, see https://aerosandbox.readthedocs.io/en/master/autoapi/aerosandbox/index.html#aerosandbox.AeroBuildup :param velocity: The flight velocity, expressed as a true airspeed. [m/s] :type velocity: float :param alpha: Angle of attack in degrees. :type alpha: float :param atmosphere: The atmosphere model to use. :type atmosphere: asb.Atmosphere :param model_size: Size of the model. Default is xxxlarge. Other options are “xxsmall”, “xsmall”, “small”, “medium”, “large”, “xlarge”, “xxlarge”. :type model_size: str, optional

class pyLOM.RL.AVLSolver(velocity, alpha, atmosphere)[source]#

Bases: BaseSolver

AVL solver for wing analysis. It uses the AVL (Athena Vortex Lattice) method to compute the lift and drag coefficients. To use this solver, you need to have AVL executable installed and available in your PATH, see https://web.mit.edu/drela/Public/web/avl/ :param velocity: The flight velocity, expressed as a true airspeed. [m/s] :type velocity: float :param alpha: Angle of attack in degrees. :type alpha: float :param atmosphere: The atmosphere model to use. :type atmosphere: asb.Atmosphere

class pyLOM.RL.BaseParameterizer[source]#

Bases: ABC

Base class for all parameterizers. It defines the interface for the parameterizers.

abstract get_shape_from_params(params: ndarray) Airfoil[source]#

Create a shape from the given parameters.

Parameters:

params (np.ndarray) – Parameters to create the shape.

Returns:

Created shape.

Return type:

asb.Airfoil

abstract get_optimizable_bounds() Tuple[List, List][source]#

Get the bounds of the optimizable parameters.

Returns:

Lower and upper bounds of the parameters.

Return type:

Tuple[List, List]

abstract get_params_from_shape(shapea: Airfoil) ndarray[source]#

Get the parameters from the given shape. :param shape: Shape to get the parameters from. :type shape: asb.Airfoil

Returns:

Parameters of the shape.

Return type:

np.ndarray

abstract generate_random_params(seed=None) ndarray[source]#

Generate random parameters within the bounds.

Parameters:

seed (int, optional) – Seed for the random number generator. Default is None.

Returns:

Random parameters within the bounds.

Return type:

np.ndarray

class pyLOM.RL.AirfoilCSTParametrizer(upper_surface_bounds: Tuple[List, List], lower_surface_bounds: Tuple[List, List], TE_thickness_bounds: Tuple[List, List], leading_edge_weight: Tuple[List, List])[source]#

Bases: BaseParameterizer

CST parameterization of airfoils. The CST method is a way to parameterize airfoils using a set of control points. Depending on the number of points in upper and lower surfaces, the number of parameters will be different.

Parameters:
  • upper_surface_bounds (Tuple[List, List]) – Bounds for the upper surface parameters. The first list has the lower bounds and the second list has the upper bounds.The length of the lists should be equal to the number of parameters for the upper surface.

  • lower_surface_bounds (Tuple[List, List]) – Bounds for the lower surface parameters. The first list has the lower bounds and the second list has the upper bounds.The length of the lists should be equal to the number of parameters for the lower surface.

  • TE_thickness_bounds (Tuple[float, float]) – Bounds for the TE thickness. The first value is the lower bound and the second value is the upper bound.

  • leading_edge_weight (Tuple[float, float]) – Bounds for the leading edge weight. The first value is the lower bound and the second value is the upper bound.

naca_4digit_airfoils = ['naca0006', 'naca0009', 'naca0012', 'naca0015', 'naca0018', 'naca1408', 'naca1410', 'naca1412', 'naca2412', 'naca2415', 'naca4412', 'naca4415', 'naca4420', 'naca6412', 'naca6415', 'naca7421', 'naca8409', 'naca8412', 'naca8415', 'naca9421']#
get_shape_from_params(params)[source]#

Create a shape from the given parameters.

Parameters:

params (np.ndarray) – Parameters to create the shape.

Returns:

Created shape.

Return type:

asb.Airfoil

get_params_from_shape(airfoil: Airfoil) ndarray[source]#

Get the parameters from the given shape. :param shape: Shape to get the parameters from. :type shape: asb.Airfoil

Returns:

Parameters of the shape.

Return type:

np.ndarray

generate_random_params(source='naca', seed=None) ndarray[source]#

Generate random parameters within the bounds.

If source is “naca”, generate a random NACA 4-digit airfoil. If source is “uiuc”, generate random airfoil from the UIUC dataset.

Parameters:
  • source (str) – Source of the airfoil. Can be “naca” or “uiuc”. Default is “naca”.

  • seed (int, optional) – Seed for the random number generator. Default is None.

Returns:

Random parameters for an airfoil.

Return type:

np.ndarray

get_optimizable_bounds() Tuple[List, List][source]#

Get the bounds of the parameters.

Returns:

Lower and upper bounds of the parameters. The first list has the lower bounds and the second list has the upper bounds.

Return type:

Tuple[List, List]

class pyLOM.RL.WingParameterizer(airfoil: Airfoil, chord_bounds: Tuple[List], twist_bounds: Tuple[List], span_bounds: Tuple[List], sweep_bounds: Tuple[List], dihedral_bounds: Tuple[List])[source]#

Bases: BaseParameterizer

__init__(airfoil: Airfoil, chord_bounds: Tuple[List], twist_bounds: Tuple[List], span_bounds: Tuple[List], sweep_bounds: Tuple[List], dihedral_bounds: Tuple[List])[source]#

Initialize the WingParametrizerDust class.

Parameters:
  • airfoil (asb.Airfoil) – The airfoil to be used for the wing sections.

  • chord (The bounds for the)

  • twist

  • span (and a region of the wing is created for each)

  • sweep

  • lists (and dihedral parameters are tuples with two)

  • same (the first list is the lower bound and the second list is the upper bound. If the upper and lower bounds are the)

  • pair (the parameter will not be considered for optimization. A section of the wing is created for each chord and twist)

  • span

  • sweep

  • triplet (and dihedral)

  • degrees. (the number of sections should be one more than the number of regions. The angles need to be in)

get_shape_from_params(params)[source]#

Create a shape from the given parameters.

Parameters:

params (np.ndarray) – Parameters to create the shape.

Returns:

Created shape.

Return type:

asb.Airfoil

get_params_from_shape(wing)[source]#

Get the parameters from the given shape. :param shape: Shape to get the parameters from. :type shape: asb.Airfoil

Returns:

Parameters of the shape.

Return type:

np.ndarray

generate_random_params(seed=None)[source]#

Generate random parameters within the bounds.

Parameters:

seed (int, optional) – Seed for the random number generator. Default is None.

Returns:

Random parameters within the bounds.

Return type:

np.ndarray

get_optimizable_bounds()[source]#

Get the bounds of the optimizable parameters.

Returns:

Lower and upper bounds of the parameters.

Return type:

Tuple[List, List]

pyLOM.RL.create_env(solver_name, parameterizer=None, operating_conditions=None, num_envs=1, episode_max_length=64, thickness_penalization_factor=0, initial_seed=None)[source]#

Create a reinforcement learning environment for shape optimization. Using these environments, the RL agents have been trained in https://arxiv.org/pdf/2505.02634.

Parameters:
  • solver_name (str) – Name of the solver to use.

  • parameterizer – Parameterizer object that defines the shape to optimize (if None, a default will be used)

  • num_envs (int) – Number of parallel environments to create. If greater than 1, you need to wrap the code in if __name__ == "__main__": to avoid issues with multiprocessing. Ref: https://stable-baselines3.readthedocs.io/en/master/guide/vec_envs.html. Default is 1.

  • episode_max_length (int) – Maximum episode length. Default is 64.

  • operating_conditions (Optional[AirfoilOperatingConditions]) – Operating conditions for the solver. Default is None.

  • thickness_penalization_factor (float) – Penalty factor for thickness changes. Default is 0.

  • initial_seed (Optional[int]) – Initial random seed. Default is None.

Returns:

The created environment

Return type:

gym.Env

class pyLOM.RL.AirfoilOperatingConditions(alpha: float = 2, Reynolds: float = 1000000.0, mach: float = 0.5)[source]#

Bases: object

Configuration for airfoil operating conditions.

This class encapsulates the physical parameters used in airfoil simulations, including angle of attack, Reynolds number, and Mach number.

Parameters:
  • alpha (float) – Angle of attack in degrees. Default is 2 degrees.

  • reynolds (float) – Reynolds number for the simulation. Default is 1e6.

  • mach (float) – Mach number for the simulation. Default is 0.5.

alpha: float = 2#
Reynolds: float = 1000000.0#
mach: float = 0.5#
class pyLOM.RL.AirfoilParameterizerConfig(n_weights_per_side: int = 8, leading_edge_weight_bounds: Tuple[float, float] = (-0.05, 0.75), te_thickness_bounds: Tuple[float, float] = (0.0005, 0.01), upper_edge_bounds: Tuple[float, float] = (-1.5, 1.25), lower_edge_bounds: Tuple[float, float] = (-0.75, 1.5))[source]#

Bases: object

Configuration for airfoil parameterization.

This class defines the parameters used to create an airfoil shape using the Class-Shape Transformation (CST) method. It controls the number of control points and their bounds for both upper and lower surfaces.

Parameters:
  • n_weights_per_side (int) – Number of control points per surface side. Default is 8.

  • leading_edge_weight_bounds (Tuple[float, float]) – Min and max values for the leading edge weight parameter. Default is (-0.05, 0.75).

  • te_thickness_bounds (Tuple[float, float]) – Min and max values for trailing edge thickness. Default is (0.0005, 0.01).

  • upper_edge_bounds (Tuple[float, float]) – Min and max values for upper surface control points. Default is (-1.5, 1.25).

  • lower_edge_bounds (Tuple[float, float]) – Min and max values for lower surface control points. Default is (-0.75, 1.5).

n_weights_per_side: int = 8#
leading_edge_weight_bounds: Tuple[float, float] = (-0.05, 0.75)#
te_thickness_bounds: Tuple[float, float] = (0.0005, 0.01)#
upper_edge_bounds: Tuple[float, float] = (-1.5, 1.25)#
lower_edge_bounds: Tuple[float, float] = (-0.75, 1.5)#
create_parameterizer()[source]#

Creates an AirfoilCSTParametrizer based on this configuration.

This method instantiates a new AirfoilCSTParametrizer using the current configuration settings. The parameterizer can then be used to generate airfoil shapes for optimization.

Returns:

A configured airfoil parameterizer instance.

Return type:

AirfoilCSTParametrizer

class pyLOM.RL.SolverFactory[source]#

Bases: object

Factory for creating solver instances.

This class provides methods to create appropriate solver instances based on their name and configuration parameters. It acts as a factory that abstracts the details of solver instantiation.

static create_solver(solver_name: str, conditions: AirfoilOperatingConditions | None = None) BaseSolver[source]#

Create a solver by name.

This method determines the type of solver (airfoil or wing) based on the name and delegates creation to the appropriate specialized factory method.

Parameters:
  • solver_name (str) – The name of the solver to create.

  • conditions (Optional[AirfoilOperatingConditions]) – Configuration parameters for the solver. If None, default values will be used.

Returns:

The created solver instance.

Return type:

BaseSolver

Raises:

ValueError – If the solver name is not recognized.

static create_airfoil_solver(solver_name: str, conditions: AirfoilOperatingConditions | None = None) BaseSolver[source]#

Create an airfoil solver instance.

This method creates and returns an airfoil solver instance based on the specified name and configured with the given conditions.

Parameters:
  • solver_name (str) – The name of the airfoil solver to create.

  • conditions (Optional[AirfoilOperatingConditions]) – Configuration parameters for the solver. If None, default values will be used.

Returns:

The created airfoil solver instance.

Return type:

BaseSolver

Raises:

ValueError – If the solver name is not recognized as a valid airfoil solver.

static create_wing_solver(solver_name: str, conditions: WingOperatingConditions | None = None)[source]#

Create a wing solver

class pyLOM.RL.WingOperatingConditions(velocity: float = 150, altitude: float = 500, alpha: float = 2)[source]#

Bases: object

Configuration for wing operating conditions

Parameters:
  • velocity (float) – Flight velocity in m/s. Default is 150 m/s.

  • altitude (float) – Altitude in meters. Default is 500 m.

  • alpha (float) – Angle of attack in degrees. Default is 2.

velocity: float = 150#
altitude: float = 500#
alpha: float = 2#
class pyLOM.RL.WingParameterizerConfig(airfoil_name: str = 'naca0012', chord_bounds: ~typing.List[~typing.List[float]] = <factory>, twist_bounds: ~typing.List[~typing.List[float]] = <factory>, span_bounds: ~typing.List[~typing.List[float]] = <factory>, sweep_bounds: ~typing.List[~typing.List[float]] = <factory>, dihedral_bounds: ~typing.List[~typing.List[float]] = <factory>)[source]#

Bases: object

Configuration for wing parameterization

This class defines the parameters used to create a wing shape, including airfoil type, chord, twist, span, sweep, and dihedral angles. It provides methods to create a WingParameterizer instance based on these settings.

Parameters:
  • airfoil_name (str) – Name of an airfoil from the UIUC airfoil dataset to use for the wing. If a custom asb.Airfoil wants to me used, pleas create the parameterizere directly with pyLOM.RL.WingParameterizer. Default is “naca0012”.

  • chord_bounds (List[List[float]]) – Bounds for the chord length. Default is [[0.75, 0.45], [1.25, 0.75]].

  • twist_bounds (List[List[float]]) – Bounds for the twist angle in degrees. Default is [[-2, -2], [2, 2]].

  • span_bounds (List[List[float]]) – Bounds for the span length. Default is [[1.5], [2]].

  • sweep_bounds (List[List[float]]) – Bounds for the sweep angle in degrees. Default is [[-5], [15]].

  • dihedral_bounds (List[List[float]]) – Bounds for the dihedral angle in degrees. Default is [[-2], [7]].

airfoil_name: str = 'naca0012'#
chord_bounds: List[List[float]]#
twist_bounds: List[List[float]]#
span_bounds: List[List[float]]#
sweep_bounds: List[List[float]]#
dihedral_bounds: List[List[float]]#
create_parameterizer()[source]#

Creates a WingParameterizer based on this configuration

pyLOM.RL.run_episode(rl_model, env, initial_shape=None, seed=None, keep_unconverged=False)[source]#

Runs a single episode of the RL agent in the given environment.

Parameters:
  • rl_model – The RL model to evaluate.

  • env – The environment in which to run the episode.

  • initial_shape – An initial airfoil shape to start the episode with. Default: None, which means a random shape will be generated.

  • seed – A seed for reproducibility. Default: None.

  • keep_unconverged – If True, keeps the last state and reward even if the episode was truncated. Default: False.

Returns:

A list of cumulative rewards at each step. states: A list of airfoil shapes at each step.

Return type:

rewards

pyLOM.RL.evaluate_airfoil_agent(agent, env, num_episodes=200, save_path=None)[source]#

Evaluates an RL agent on a set of airfoils from the UIUC airfoil database and prints a summary of the results.

Parameters:
  • agent – The RL agent to evaluate.

  • env – The environment in which to run the episodes.

  • num_episodes – The number of airfoils to evaluate. Default: 200.

  • save_path – If provided, saves the results to a CSV file at this path. Default: None.

Returns:

A list with the lists of cumulative rewards for each airfoil optimization. states: A list with lists of airfoil shapes for each step of the optimization of each airfoil.

Return type:

all_rewards

pyLOM.RL.evaluate_airfoil_agent_whole_uiuc(agent, env, save_path=None)[source]#

Evaluates an RL agent on all airfoils in the UIUC airfoil database and prints a summary of the results. :param agent: The RL agent to evaluate. :param env: The environment in which to run the episodes. :param save_path: If provided, saves the results to a CSV file at this path. Default: None.

Returns:

A list with the lists of cumulative rewards for each airfoil optimization. states: A list with lists of airfoil shapes for each step of the optimization of each airfoil.

Return type:

all_rewards

pyLOM.RL.evaluate_airfoil_agent_whole_uiuc_mpi(agent, env, save_results_path)[source]#

Evaluates an RL agent on all airfoils in the UIUC airfoil database using MPI for parallel processing and saves the results to a CSV file. If this function is used on a script, it should be run with mpiexec -n <num_processes> python <script_name>.py.

Parameters:
  • agent – The RL agent to evaluate.

  • env – The environment in which to run the episodes.

  • save_results_path – If provided, saves the results to a CSV file at this path.

pyLOM.RL.create_airfoil_optimization_progress_plot(airfoils, rewards, airfoil_name='Airfoil Shape', save_path=None)[source]#

Create a plot showing the evolution of airfoil shapes and their corresponding lift-to-drag ratios.

Parameters:
  • airfoils (List[asb.Airfoil]) – List of airfoil objects representing the evolution.

  • rewards (List[float]) – List of lift-to-drag ratios corresponding to each airfoil.

  • airfoil_name (str) – Name of the airfoil for the plot title. Default: 'Airfoil Shape'.

  • save_path (Optional[str]) – Path to save the plot. If None, the plot will not be saved. Default: None.

class pyLOM.RL.AirfoilEvolutionAnimation(*args, **kwargs)[source]#

Bases: object

Generate an animation showing the evolution of airfoils and their corresponding lift-to-drag ratios. Manim is required to run this animation, and it is recommended to install it with conda install -c conda-forge manim.

Properties:
  • airfoils: List of airfoil objects representing the evolution.

  • rewards: List of lift-to-drag ratios corresponding to each airfoil.

  • run_time: Duration of the animation in seconds. Default: 5.

  • title: Title of the animation. Default: "Airfoil evolution".

Examples

To use in a notebook:

>>> import manim
>>> from pyLOM.RL import AirfoilEvolutionAnimation
>>> %%manim -qm -v WARNING AirfoilEvolution
>>> AirfoilEvolutionAnimation.airfoils = airfoils
>>> AirfoilEvolutionAnimation.rewards = rewards
>>> AirfoilEvolutionAnimation.title = "NACA0012 Evolution"

To use it in a script:

>>> from pyLOM.RL import AirfoilEvolutionAnimation
>>> from manim import *
>>> from main import config
>>> # config.format = "gif"  # Change output format to GIF
>>> config.output_file = "airfoil_evolution.mp4"  # Change output file name
>>> config.quality = "production_quality"  # Set quality to maximum (Full HD)
>>> animation = AirfoilEvolutionAnimation()
>>> animation.airfoils = airfoils
>>> animation.rewards = rewards
>>> animation.title = "Airfoil Evolution"
>>> animation.render()
class pyLOM.RL.WingEvolutionAnimation(*args, **kwargs)[source]#

Bases: object

Generate an animation showing the evolution of wings and their corresponding lift-to-drag ratios. Manim is required to run this animation, and it is recommended to install it with conda install -c conda-forge manim.

Properties:
  • wings: List of wing objects representing the evolution.

  • rewards: List of lift-to-drag ratios corresponding to each airplane.

  • run_time_per_update: Duration of each update in seconds. Default: 0.25.

Examples

To use in a notebook:

>>> import manim
>>> from pyLOM.RL import WingEvolutionAnimation

on a separete cell, define the wings and rewards:

>>> %%manim -qm -v WARNING AirfoilEvolution
>>> WingEvolutionAnimation.wings = wings
>>> WingEvolutionAnimation.rewards = rewards
>>> WingEvolutionAnimation.run_time_per_update = 0.1
pyLOM.RL.SB3_PPO#

alias of PPO