- Use typed Google syntax for all docstrings
- Include comprehensive Args, Returns, and Raises sections
- Provide clear descriptions with mathematical formulations where applicable
- Example:
def compute_velocity_induced_bound_vortex(
self, XVP: np.ndarray, gamma: float, core_radius_fraction: float
) -> np.ndarray:
"""Calculate velocity induced by bound vortex filament using Vatistas core model.
Vortex core correction from: Rick Damiani et al. "A vortex step method for nonlinear
airfoil polar data as implemented in KiteAeroDyn".
Args:
XVP (np.ndarray): Evaluation point coordinates.
gamma (float): Vortex strength (circulation).
core_radius_fraction (float): Core radius as fraction of filament length.
Returns:
np.ndarray: Induced velocity vector [vx, vy, vz].
Raises:
ValueError: If core_radius_fraction is negative.
"""- Use type hints for all function parameters and return values
- Import from
typingmodule when needed:List,Tuple,Dict,Optional,Union - Use
np.ndarrayfor NumPy arrays - Example:
def solve(self, body_aero: BodyAerodynamics, gamma_distribution: np.ndarray = None) -> dict:
- Use
snake_casefor variable and function names - Variable names should be descriptive and self-documenting
- Use full words rather than abbreviations when possible
- Examples:
gamma_distribution,compute_aerodynamic_quantities,alpha_array
- Variable name of path to a file ends with
_path - Variable name of a directory ends with
_dir - Examples:
polar_file_path,airfoil_data_dir,nf_airfoil_data_dir
- Use
CamelCasefor class names - Class names should be nouns that clearly describe the entity
- Examples:
BodyAerodynamics,AirfoilAerodynamics,SemiInfiniteFilament
- Use
UPPER_SNAKE_CASEfor module-level constants - Examples:
MAX_ITERATIONS,DEFAULT_TOLERANCE
- Use leading underscore
_for private/internal class attributes - Examples:
_alpha0,_nu,_panel_polar_data
- Use
@dataclassfor simple data containers with automatic__init__ - Use
@propertyfor computed attributes and getters - Use
@classmethodfor alternative constructors - Use
@staticmethodfor utility functions that don't need class state - Example:
@dataclass
class Wing:
n_panels: int
spanwise_panel_distribution: str = "uniform"
sections: List[Section] = field(default_factory=list)
@classmethod
def from_yaml_entry(cls, config: dict) -> 'Wing':
"""Alternative constructor from configuration."""
...
@property
def span(self) -> float:
"""Calculate wing span."""
...
@staticmethod
def compute_distance(point1: np.ndarray, point2: np.ndarray) -> float:
"""Utility function for distance calculation."""
...- Group methods logically within classes:
__init__and class methods- Properties (getters/setters)
- Public methods
- Private methods (prefixed with
_)
- Use factory methods for complex object creation
- Prevent direct instantiation when appropriate
- Example:
class AirfoilAerodynamics:
def __init__(self):
raise RuntimeError("Use AirfoilAerodynamics.from_yaml_entry(...) to instantiate.")
@classmethod
def from_yaml_entry(cls, airfoil_type: str, airfoil_params: dict, ...) -> 'AirfoilAerodynamics':
"""Create instance from configuration parameters."""
...- Use f-strings for string formatting
- Examples:
f"Normalized error at iteration {i}: {normalized_error}" - For logging:
logging.info(f"Converged after {iterations} iterations")
- Use specific exception types:
ValueError,TypeError,FileNotFoundError - Provide descriptive error messages
- Example:
if n_panels < 1:
raise ValueError("Number of panels must be positive")
if not file_path.exists():
raise FileNotFoundError(f"Airfoil file not found: {file_path}")- Validate inputs early in functions
- Use clear error messages that guide the user
- Example:
if aerodynamic_model_type not in ["VSM", "LLT"]:
raise ValueError(f"Invalid model type: {aerodynamic_model_type}. Use 'VSM' or 'LLT'.")- Standard library imports first
- Third-party imports second
- Local imports last
- Use absolute imports for internal modules
- Example:
from abc import ABC, abstractmethod
import numpy as np
import logging
from pathlib import Path
from VSM.core.Filament import BoundFilament
from . import jit_cross, jit_norm, jit_dot- Use JIT-compiled utility functions for performance-critical operations
- Import from utils module:
jit_cross,jit_norm,jit_dot - Example usage:
distance = jit_norm(vector)
- Document units in docstrings and variable names where appropriate
- Examples:
velocity_ms(m/s),angle_rad(radians),length_m(meters)
- Use descriptive suffixes for arrays:
_arrayfor vector/array of values (one per panel)_distributionfor spanwise distributions_normfor magnitude/norm of vector quantity_unitfor unit vectors (normalized direction)
- Document coordinate system conventions clearly
- Use consistent naming:
x_airf,y_airf,z_airffor local reference frames - Examples:
LE_point,TE_point,control_point,aerodynamic_center
- Use vectorized operations instead of loops where possible
- Pre-allocate arrays with known sizes
- Use in-place operations when memory is a concern
- Example:
# Good: vectorized operation
alpha_array = np.arctan(v_normal_array / v_tangential_array)
# Avoid: element-wise loop
alpha_array = [np.arctan(v_n / v_t) for v_n, v_t in zip(v_normal_array, v_tangential_array)]- Use
np.zeros()ornp.empty()for pre-allocation - Minimize temporary array creation in loops
- Reuse objects when possible (e.g., filament updates in Wake class)
- Use appropriate logging levels:
logging.debug()for detailed diagnostic informationlogging.info()for general informationlogging.warning()for warnings that don't stop executionlogging.error()for errors that might cause problems
- Include iteration numbers and convergence metrics
- Log important state changes and method calls
- Example:
logging.debug(f"Normalized error at iteration {i}: {normalized_error}")
logging.info(f"Converged (non_linear: broyden1)")
logging.warning(f"NOT Converged after {self.max_iterations} iterations")- Check array shapes and dimensions
- Validate ranges for physical parameters
- Example:
if len(va_distribution) != len(panels):
raise ValueError("Velocity distribution length mismatch")
if alpha_range[0] >= alpha_range[1]:
raise ValueError("Invalid alpha range: min must be less than max")- Handle edge cases gracefully
- Provide fallback options when possible
- Check for division by zero and other numerical issues
- Example:
reference_error = max(abs(gamma_new)) if max(abs(gamma_new)) != 0 else 1e-4
direction = direction / max(jit_norm(direction), 1e-12) # Avoid division by zero- Provide sensible defaults for optional parameters
- Document the reasoning behind default choices
- Example:
def __init__(
self,
aerodynamic_model_type: str = "VSM", # Most accurate method
max_iterations: int = 5000, # Conservative limit
allowed_error: float = 1e-6, # Engineering accuracy
relaxation_factor: float = 0.01, # Stable convergence
):- Use dictionaries for grouped parameters
- Support both direct instantiation and configuration-based creation
- Example:
artificial_damping: dict = {"k2": 0.1, "k4": 0.0}
stall_params = {
"is_smooth_circulation": False,
"smoothness_factor": 0.08,
"is_artificial_damping": False
}- Use sparingly for complex algorithms or non-obvious code
- Explain the "why" not the "what"
- Example:
# Apply under-relaxation for stability
gamma_new = (1 - relaxation_factor) * gamma + relaxation_factor * gamma_new
# Oseen parameter for viscous diffusion
self._alpha0 = 1.25643- Use clear section headers for major code blocks
- Example:
###########################
## GETTER FUNCTIONS
###########################
###########################
## SETTER FUNCTIONS
###########################
###########################
## CALCULATE FUNCTIONS # All these return something
###########################- Keep related functionality together
- Separate abstract base classes from concrete implementations
- Use clear inheritance hierarchies
- Example:
Filament(ABC) →BoundFilament,SemiInfiniteFilament
- Minimize circular imports
- Use relative imports within packages:
from . import jit_cross - Keep external dependencies to minimum and document requirements
This style guide ensures consistency, readability, and maintainability across the VSM codebase while following Python best practices and scientific computing conventions.