Skip to content

YAML parsing debug#73

Draft
kbonney wants to merge 9 commits into
sandialabs:mainfrom
kbonney:yaml-read-debug
Draft

YAML parsing debug#73
kbonney wants to merge 9 commits into
sandialabs:mainfrom
kbonney:yaml-read-debug

Conversation

@kbonney

@kbonney kbonney commented Mar 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

The goal of this PR is to resolve bugs in the yaml_to_blade() function. Currently, there are observed differences in the total weight of blades compared to their design documents.

yaml_to_blade() changes

  • Blade length: remove np.ceil and redundant hub-radius arithmetic. Uses exact YAML span value
  • Corrected density vs drydensity calculations. Now "rho" is assigned to density (cured density) and drydensity is assigned "area_density_dry" if it exists, otherwise nan. These updates are documented in the Material class docstring.
  • Spar cap attributes: use _lp / _hp suffixes to avoid LP overwriting HP (or vice versa)
  • Airfoil winding: replace arctan(y/x) gradient check with the shoelace signed-area formula. Avoids divide-by-zero near x = 0
  • fabricangle: replace finally: pass with except (KeyError, TypeError): cur_comp.fabricangle = 0
  • ValueErrors in _add_components: add missing raise to all four TE/LE reinforcement error sites
  • yaml.FullLoader: replace deprecated yaml.Loader
  • ispan aliasing: definition.ispan = definition.span.copy() instead of direct assignment
  • Import cleanup: remove unused from scipy.stats import mode

BOM changes

  • Now computes two weights: dryweight and curedweight based on drydensity and density respectively using the following formulas:
                            "dryweight": mat.drydensity * regionarea,
                            "curedweight": mat.density * mat.layerthickness * mm_to_m * regionarea
  • BOM now has the top level attributes total_dryweight and total_curedweight, which takes the sum across all bom entries.

Example blade files cleanup

  • Removed duplicate files
  • Renamed files for clarity on their contents
  • Updated tests/example files with new filenames

yaml_to_blade_v2 Added a new version of yaml parser that supports WindIO ontology version 2
The v2 ontology introduces several structural and naming changes from v1:

  • Twist units: twist is now in degrees; v1 stored radians
  • Anchor indirection: start_nd_arc/end_nd_arc on layers and webs can be anchor references ({anchor: {name, handle}}) instead of inline {grid, values} dicts; anchors may live at structure.anchors or nested under individual webs[i].anchors
  • Spar cap geometry: spar caps define their chordwise extent via nd_arc (normalized arc fraction 0–1) rather than explicit width/offset; HP and LP sides are identified by _PS/_SS name suffixes instead of a side field
  • section_offset_y: chord offset is now absolute meters from the leading edge rather than a normalized fraction; the reader divides by chord to convert
  • rthick: relative thickness key renamed from relative_thickness
  • Airfoil list format: outer shape airfoils are a list of {name, spanwise_position} dicts rather than a flat mapping
  • Web separation: web laminate layers now live in structure.layers alongside all other layers; structure.webs is purely geometric
  • TE/LE band width: band width is stored on the anchor definition rather than on the layer itself. Requires interpolation to full blade span, since only several span points are provided
  • Reader organization

The reader is structured as a flat sequence of focused functions called from the top-level yaml_to_blade_v2:

  • _build_anchor_map / _resolve_structure_anchors: flattens anchor references to inline {grid, values} before any other processing
  • _add_stations_v2: builds span grid, adds airfoil cross-sections, and computes aerocenter from per-station data collected in the loop
  • _add_spanwise_properties_v2: interpolates all continuous spanwise quantities (twist, chord, thickness, chordoffset, prebend, sweep) onto the full span grid
  • update_internal_structure_v2: expands each layer's field grids to the full normalized span
  • _set_sparcap_arc_positions: writes nd_arc bounds to definition for use by keypoints.generate; distinct from _assign_extents_to_comp which sets Component keypoint labels
  • _reinforcement_halfwidth_mm: shared helper used for both TE and LE bands; sums anchor widths across matched layers and returns half-width in mm for definition.teband/leband
  • _build_component_dict: single-pass construction of all Component objects, with _assign_extents_to_comp dispatching extent labels from the layer name
  • _validate_components: post-build check that expected component counts are present
  • _add_materials is reused directly from the v1 reader as the material schema is unchanged.

The V2 format also required updates to keypoints.py

  • Spar cap keypoints (keypoints.py): KeyPoints.generate previously assumed sparcapwidth_* and sparcapoffset_* were always populated and used them to place the b/c keypoints as z ± 0.5 * width. These attributes do not exist in v2, which instead provides sparcap_start/end_nd_arc_hp/lp. We now check which set of attributes are populated and compute b/c keypoints based on whether we are in v1 or v1. , When scwidth is None an else branch converts nd_arc fractions directly to arc-length positions using geometry.arclength and the per-surface sign convention (HP: (nd - 0.5) / 0.5, LP: (0.5 - nd) / 0.5).

Tests and documentation

na

Acknowledgement

By contributing to this software project, I acknowledge that I have reviewed the software quality assurance guidelines and that my contributions are submitted under the Revised BSD License.

@kbonney kbonney marked this pull request as draft March 25, 2026 17:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant