Skip to content

PART 4 - 966 feature request add torsion steel check#968

Open
GerjanDorgelo wants to merge 69 commits into
mainfrom
966-feature-request-add-torsion-steel-check
Open

PART 4 - 966 feature request add torsion steel check#968
GerjanDorgelo wants to merge 69 commits into
mainfrom
966-feature-request-add-torsion-steel-check

Conversation

@GerjanDorgelo
Copy link
Copy Markdown
Contributor

@GerjanDorgelo GerjanDorgelo commented Feb 6, 2026

Description

must be checked after PART 3 has been checked

feature request add torsion steel check. must be checked after Shear has been added as i use features introduced there.
Please note: only features St. Venant torsion. Warping torsion is horrible and needs 3d FEM.

image

As Blueprints uses different coordinates than sectionproperties. see for converstion above.

Fixes #966

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Checklist:

  • I have added tests that prove my fix is effective or that my feature works
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

Release Notes

  • New Features

    • Added steel shear strength checks for class 1/2 and class 3/4 cross-sections
    • Added steel torsion strength validation
    • Added combined torsion-shear analysis capabilities
    • Introduced profile stress calculations under combined loading conditions
    • Enhanced report generation supporting multi-line equation formatting
  • Improvements

    • Refined flange modeling in structural strength formulas for improved accuracy

…istance checks and enhance Report class with equation splitting functionality
…vironments and rename split_on to split_after for clarity
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

This PR adds three new Eurocode 3 steel strength check modules (shear, torsion, combined torsion-shear) for Class 1/2 and 3/4 sections, refactors formula 6.18 sub A to support split-flange geometry, extends the Profile API with stress calculation methods, enhances equation rendering with multi-line support, and includes comprehensive test coverage.

Changes

Cohort / File(s) Summary
New Steel Strength Checks
blueprints/checks/eurocode/steel/strength_shear.py, blueprints/checks/eurocode/steel/strength_torsion.py, blueprints/checks/eurocode/steel/strength_torsion_shear.py
Introduces six public dataclass-based check classes: CheckStrengthShearClass12/34 for plastic/elastic shear strength, CheckStrengthStVenantTorsionClass1234 for torsion, and CheckStrengthTorsionShearClass12/34 for combined torsion-shear. Each provides source_docs(), resistance calculations, unity checks (Formula objects), result comparisons, and detailed reports with conditional messaging for zero loads.
Formula Refactoring
blueprints/codes/eurocode/en_1993_1_1_2005/chapter_6_ultimate_limit_state/formula_6_18_sub_av.py
Refactors Form6Dot18SubARolledIandHSection public signature to split flange parameters from single (b, r, tf) to paired (b1, b2, r1, r2, tf1, tf2) for asymmetric shear area calculations. Updates validation, internal algebra, and LaTeX representation accordingly.
Profile Infrastructure
blueprints/structural_sections/_profile.py
Extends Profile API with calculate_stress() method (maps internal forces to sectionProperties), unit_stress() method (caches unit stress from unit loads), and _unit_stress_cache field for per-instance caching. Requires new imports from sectionproperties and blueprint unit conversion modules.
Report Rendering Enhancements
blueprints/utils/report/report.py, blueprints/utils/report/_report_to_word.py
Adds split_after optional parameter to add_equation() and add_formula() for multi-line LaTeX rendering using multline environment. Updates equation counting to recognize both multline and equation blocks. Refactors Word document equation rendering to emit multi-line equations line-by-line with tag attachment to final line.
Test Fixtures & Coverage
tests/checks/eurocode/steel/conftest.py, tests/checks/eurocode/steel/test_strength_shear.py, tests/checks/eurocode/steel/test_strength_torsion.py, tests/checks/eurocode/steel/test_strength_torsion_shear.py, tests/codes/eurocode/.../test_formula_6_18_sub_av.py, tests/utils/test_report.py, tests/utils/test_report_to_word.py
Adds pytest fixtures for welded HEB and UNP sections. Introduces comprehensive test suites for all three check classes covering zero-load edge cases, ok/not-ok results with tolerance-based assertions, profile validation, documentation references, and equation splitting behavior. Updates formula tests for split-flange signature.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CheckShear as CheckStrengthShear<br/>(Class12/34)
    participant Profile as SteelCrossSection<br/>& Profile
    participant Formula as Formula<br/>Components
    participant Report as Report<br/>Generator
    
    User->>CheckShear: Instantiate with v, axis,<br/>gamma_m0
    CheckShear->>Profile: Access cross-section<br/>geometry & material
    CheckShear->>Profile: Retrieve unit stresses or<br/>compute shear area
    Profile-->>CheckShear: Return stress/area data
    CheckShear->>Formula: Build shear_area() or<br/>shear_stress() formula
    Formula-->>CheckShear: Formula object
    CheckShear->>Formula: Build plastic/elastic<br/>resistance formula
    Formula-->>CheckShear: Resistance formula
    CheckShear->>Formula: Build unity_check formula<br/>(v_ed / v_pl_rd or tau_ed / tau_max)
    Formula-->>CheckShear: Unity check formula
    CheckShear->>CheckShear: Evaluate unity_check<br/>& compute result()
    CheckShear->>Report: Generate detailed report()<br/>with formulas, values,<br/>& conditional messaging
    Report-->>CheckShear: Report object
    CheckShear-->>User: Return CheckResult<br/>& Report
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • PR #959: Adds the fabrication/production attribute on SteelCrossSection that the new shear-strength checks depend on to select between rolled and welded shear-area formulas.
  • PR #978: Modifies Eurocode steel checks and I-profile related section/profile APIs, including new strength-check dataclasses and profile/unit-stress helpers that overlap with this PR's infrastructure changes.
  • PR #891: Updates report-to-Word tooling and report rendering modules (blueprints/utils/report.py and conversion utilities), which this PR extends with multi-line equation support.

Suggested reviewers

  • SZeltaat
  • egarciamendez

Poem

🐰 Hop along with checks so grand,
Shear and torsion now at hand!
Split those flanges, formulas bright,
Multi-line reports shine with light!
Steel will stand the test of might!

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main feature: adding a torsion steel check (Part 4 of a multi-part implementation), with reference to issue #966.
Description check ✅ Passed The PR description covers all required template sections: comprehensive description of changes, explicit issue reference (#966), appropriate type selection (New feature + documentation update), and all checklist items marked complete.
Linked Issues check ✅ Passed The PR implements the torsion steel check feature from issue #966, adding CheckStrengthStVenantTorsionClass1234 and CheckStrengthTorsionShearClass12/34 classes with complete tests and documentation.
Out of Scope Changes check ✅ Passed All changes directly support torsion checking: stress calculation methods added to Profile, formula 6.18 refactored for flange parameters, Report enhanced for multi-line equations, and comprehensive tests added for new functionality.
Docstring Coverage ✅ Passed Docstring coverage is 98.90% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 966-feature-request-add-torsion-steel-check

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (918095b) to head (20050e3).

Additional details and impacted files
@@            Coverage Diff             @@
##              main      #968    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files          423       426     +3     
  Lines        13121     13477   +356     
==========================================
+ Hits         13121     13477   +356     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Base automatically changed from 840-feature-request-add-first-steel-check-to-blueprints to main February 11, 2026 12:32
…ng NotImplementedError for unsupported profile types

test: add tests for source_docs() method in CheckStrengthShearClass12 and CheckStrengthShearClass34
…les' into 966-feature-request-add-torsion-steel-check
- Updated CheckStrengthStVenantTorsionClass1234 to streamline calculations and improve clarity.
- Introduced static method for source documents and refined torsional resistance calculations.
- Enhanced reporting functionality to provide clearer output for torsion checks.
- Refactored CheckStrengthTorsionShearClass12IProfile and CheckStrengthTorsionShearClass34 to improve structure and readability.
- Updated test cases for torsion strength checks to remove unnecessary section properties and improve clarity.
- Ensured all tests pass with the new structure and logic.
…engthTorsionShearClass12 and update references
@GerjanDorgelo GerjanDorgelo marked this pull request as ready for review February 14, 2026 20:22
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/utils/test_report.py (1)

106-110: ⚠️ Potential issue | 🟡 Minor

Test renamed to test_multline_add_text_calls — likely a typo.

This test validates multiple sequential add_paragraph calls, not the LaTeX multline environment. The rename from test_multiline_add_text_calls to test_multline_add_text_calls appears to be an accidental edit. "multiline" (natural English) fits the test's purpose better than "multline" (the LaTeX environment name), since this test has nothing to do with equation splitting.

🤖 Fix all issues with AI agents
In `@blueprints/checks/eurocode/steel/strength_shear.py`:
- Around line 204-206: The class docstring for CheckStrengthShearClass34
incorrectly says "plastic" while the implementation (and the name field "Elastic
shear strength check") performs the elastic shear check (formula 6.19); update
the docstring to describe the elastic shear force resistance check for steel
cross-section class 3 and 4 (Eurocode 3), optionally referencing formula 6.19 to
match the implemented behavior in CheckStrengthShearClass34 and keep the
description consistent with the `name` attribute.
- Around line 105-107: Replace the runtime-only assertion in the I-profile shear
area calculation with a hard validation that always runs: in the function that
contains the assert checking parameters a, b1, b2, tf1, tf2, tw, hw, r1, r2 (the
I-profile shear area calculator in strength_shear.py) raise a ValueError (or a
custom validation exception) with the same message when any of those params is
None instead of using assert; keep the same message text ("All profile
parameters must be defined for I-profile shear area calculation.") and reference
the same parameter names so callers and tests can detect and handle the
validation error.
- Around line 278-287: elastic_resistance() can produce NaN when shear_stress()
returns 0 (e.g. when self.v == 0) due to 0*inf semantics; update
elastic_resistance to guard against zero shear stress or zero shear force by
checking sig_zxy == 0 or self.v == 0 and return 0.0 in that case, otherwise
compute the existing formula using self.steel_cross_section.yield_strength,
self.gamma_m0 and KN_TO_N; reference the elastic_resistance and shear_stress
methods and the attributes self.v, self.steel_cross_section.yield_strength, and
self.gamma_m0.

In `@blueprints/checks/eurocode/steel/strength_torsion_shear.py`:
- Around line 213-219: The report shows signed torsional stress because
total_stress_val is computed with the signed moment; update the calculation in
strength_torsion_shear.py so total_stress_val = unit_torsional_shear_stress() *
abs(self.m_x) (use abs(self.m_x) instead of self.m_x) and then pass that
absolute value into report.add_paragraph (references:
unit_torsional_shear_stress, m_x, total_stress_val, report.add_paragraph) to
match the check logic that uses abs(self.m_x).
- Line 1: Update the module docstring in
blueprints/checks/eurocode/steel/strength_torsion_shear.py to correctly
reference "Eurocode 3 (EN 1993-1-1)" instead of "Eurocode 2" and state that the
implemented checks use formulas 6.25/6.26 for combined torsion and shear (not
6.23); keep the brief description of the module purpose (torsional shear stress
resistance with shear force present) but correct the standard and formula
numbers to match the implemented logic in this module.
- Around line 56-69: The example uses an undefined variable mx when constructing
CheckStrengthTorsionShearClass12; change the argument to use the declared m_x
variable (i.e., pass m_x into CheckStrengthTorsionShearClass12) so the call to
CheckStrengthTorsionShearClass12(heb_300_s355, mx, v=v, axis=axis, gamma_m0=1.0)
becomes consistent with the earlier m_x declaration and avoids the NameError.

In `@blueprints/checks/eurocode/steel/strength_torsion.py`:
- Line 1: The module docstring in
blueprints/checks/eurocode/steel/strength_torsion.py incorrectly says "Eurocode
2"; update the top-level docstring to reference "Eurocode 3" (EN 1993-1-1) and
adjust the descriptive text accordingly so the module correctly states it
implements torsional shear stress resistance per Eurocode 3 (EN 1993-1-1).
- Around line 45-58: Update the example to use the actual class name and import
path: replace the import of TorsionStrengthCheck from
blueprints.checks.eurocode.steel.torsion_strength with the correct import of
CheckStrengthStVenantTorsionClass1234 from
blueprints.checks.eurocode.steel.strength_torsion, and instantiate that class
(replace TorsionStrengthCheck(...) with
CheckStrengthStVenantTorsionClass1234(...)) so the example matches the real
symbol names.

In `@blueprints/structural_sections/_profile.py`:
- Around line 236-239: The Returns docstring incorrectly states "Callable[...,
StressPost]" but the method returns a StressPost directly (it calls
section.calculate_stress(...)); update the Returns section to "StressPost" and
change the description to reflect that the function returns a StressPost
instance (e.g., "StressPost — stress distribution result from
section.calculate_stress") so the docstring matches the actual return value.
- Around line 269-289: The docstring for the unit_stress method incorrectly
declares the return type as StressPost; update the docstring to match the actual
return type (dict[str, Any]) or otherwise describe that it returns a dictionary
representing the unit stress distribution. Locate the unit_stress method and
change the "Returns" section (and the type on the first line of the docstring,
if present) so it accurately states dict[str, Any] and describes the returned
dict from calculate_stress(...).get_stress()[0]; leave the implementation and
the _unit_stress_cache handling unchanged.

In `@tests/checks/eurocode/steel/test_strength_torsion.py`:
- Around line 26-44: Rename the two misnamed test functions to reflect they test
torsion: change test_result_tension_ok to test_result_torsion_ok and
test_result_tension_not_ok to test_result_torsion_not_ok; update any
references/usages (e.g., in test discovery or fixtures) and keep the body
unchanged — these tests instantiate CheckStrengthStVenantTorsionClass1234 with
m_x and assert torsional unity_check/factor_of_safety, so only the function
names need renaming for clarity.
🧹 Nitpick comments (10)
blueprints/utils/report/report.py (1)

153-174: The sort comment is slightly misleading but the logic is correct.

The comment on line 158 says "Sort by decreasing index so insertion doesn't affect later positions," but the sort is on the tuple (n, char), not on the actual string position. The code still works correctly because each \\ insertion doesn't introduce new occurrences of the target split characters, so subsequent searches on the modified string always find the correct nth occurrence regardless of processing order.

One edge-case to be aware of: if split_after is used with inline equations (line 177), the \\ line breaks inside $...$ inline math won't render as line breaks. Consider guarding against this or documenting that split_after is only meaningful for block equations.

blueprints/checks/eurocode/steel/strength_torsion_shear.py (2)

78-78: section_properties field is declared but never used in Class12.

The field is accepted and stored, but none of the methods (e.g. unit_torsional_shear_stress, shear_area, raw_shear_resistance, etc.) reference self.section_properties. Compare with CheckStrengthStVenantTorsionClass1234 in strength_torsion.py which actually populates and uses it in __post_init__. If this is intended for future use, consider removing it to avoid confusion, or wire it up if it was intended to be used.


81-85: Consider using isinstance instead of exact type() check.

type(...) not in implemented_shapes won't match subclasses of IProfile. While this may not be an issue today, isinstance is the idiomatic check and is more resilient to future inheritance.

📝 Proposed fix
-        implemented_shapes = (IProfile,)
-        if type(self.steel_cross_section.profile) not in implemented_shapes:
+        if not isinstance(self.steel_cross_section.profile, IProfile):
             raise NotImplementedError(f"The provided profile shape {type(self.steel_cross_section.profile).__name__} has not been implemented yet.")
tests/checks/eurocode/steel/test_strength_torsion_shear.py (1)

73-79: Mutating a class-scoped frozen fixture is fragile and order-dependent.

Using object.__setattr__(heb_steel_cross_section, "fabrication_method", ...) on a scope="class" fixture modifies shared state across all tests in the class. This makes tests order-dependent — for example, test_source_docs will see whatever fabrication_method was last set by a preceding test. If tests are reordered or run in isolation, results could differ.

Consider using the dedicated heb_welded_steel_cross_section fixture (already defined in conftest) instead of mutating the shared one, or switch to scope="function" for fixtures that need per-test mutation.

Also applies to: 83-83, 100-102

blueprints/checks/eurocode/steel/strength_torsion.py (1)

64-71: section_properties is computed and cached but never read by any method.

__post_init__ populates self.section_properties, but unit_torsional_shear_stress(), torsional_resistance(), and other methods never reference it — they go through self.steel_cross_section.profile.unit_stress() which independently creates its own Section and computes its own properties. Either this field should be wired into the computation path, or it can be removed to avoid the unnecessary overhead of computing section properties that are never used.

tests/checks/eurocode/steel/test_strength_shear.py (3)

29-30: Non-standard pytest.approx argument order.

Throughout the tests, pytest.approx(result.unity_check, 0.005) is used as the left-hand side of ==. While this works (the second positional arg is rel), the idiomatic pattern is to place pytest.approx on the expected side:

assert result.unity_check == pytest.approx(0.99, rel=0.005)

This makes the test intent clearer ("actual ≈ expected") and avoids confusion about the positional rel argument. This pattern is repeated throughout the file (~20 occurrences).


115-116: Dead comparison — calc_without_section_props is identical to calc.

The variable name calc_without_section_props implies this was testing a scenario where section_properties is omitted, but CheckStrengthShearClass34 no longer has a section_properties parameter. Both instances are constructed identically, so assert calc == calc_without_section_props is trivially true and tests nothing useful.

Either remove the redundant assertion or, if testing equality semantics is intended, add a brief comment explaining the purpose.


25-25: Consider documenting the origin of the "magic" capacity values.

The test values like 355 * 4.74 / 1.732 * 0.99 encode f_y * A_v_cm² / √3 * utilization_ratio, but this is not immediately obvious. A brief inline comment explaining the formula would help future maintainers verify correctness, e.g.:

# V_pl_Rd ≈ f_y * A_v / √3 (units: N/mm² * cm² → kN after implicit conversion)
v = 355 * 4.74 / 1.732 * 0.99

This applies to all similar test value computations in this file.

blueprints/utils/report/_report_to_word.py (2)

444-461: Multi-line equation rendering looks correct.

The approach of stripping environment wrappers, splitting on \\, and rendering each chunk as a separate centered paragraph is clean. Tag placement on the final line only is the correct behavior for numbered multi-line equations.

One minor edge-case: if the equation content happens to produce an empty string after splitting (e.g., trailing \\), self._formula("") would be called and could fail in latex2mathml. Consider filtering out blank lines:

Optional defensive guard
-        for idx, line in enumerate(lines):
+        for idx, line in enumerate(lines):
+            if not line.strip():
+                continue

178-178: Regex captures multline|equation but the group is unused.

The pattern r"\\begin\{(multline|equation)\}" includes a capture group, but the matched group value is never read — the type is always set to "equation" from the dictionary key. This works correctly but the capture group is unnecessary. A non-capturing group (?:multline|equation) would be slightly more precise.

Comment on lines +105 to +107
assert all(param is not None for param in [a, b1, b2, tf1, tf2, tw, hw, r1, r2]), (
"All profile parameters must be defined for I-profile shear area calculation."
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

assert used for production validation — can be silently disabled with python -O.

Using assert for input validation is unreliable because assertions are stripped when Python runs with the -O (optimize) flag. Replace with an explicit ValueError or a custom validation exception to guarantee the check always runs.

Proposed fix
-        assert all(param is not None for param in [a, b1, b2, tf1, tf2, tw, hw, r1, r2]), (
-            "All profile parameters must be defined for I-profile shear area calculation."
-        )
+        if not all(param is not None for param in [a, b1, b2, tf1, tf2, tw, hw, r1, r2]):
+            raise ValueError("All profile parameters must be defined for I-profile shear area calculation.")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert all(param is not None for param in [a, b1, b2, tf1, tf2, tw, hw, r1, r2]), (
"All profile parameters must be defined for I-profile shear area calculation."
)
if not all(param is not None for param in [a, b1, b2, tf1, tf2, tw, hw, r1, r2]):
raise ValueError("All profile parameters must be defined for I-profile shear area calculation.")
🤖 Prompt for AI Agents
In `@blueprints/checks/eurocode/steel/strength_shear.py` around lines 105 - 107,
Replace the runtime-only assertion in the I-profile shear area calculation with
a hard validation that always runs: in the function that contains the assert
checking parameters a, b1, b2, tf1, tf2, tw, hw, r1, r2 (the I-profile shear
area calculator in strength_shear.py) raise a ValueError (or a custom validation
exception) with the same message when any of those params is None instead of
using assert; keep the same message text ("All profile parameters must be
defined for I-profile shear area calculation.") and reference the same parameter
names so callers and tests can detect and handle the validation error.

Comment on lines +204 to +206
@dataclass(frozen=True)
class CheckStrengthShearClass34:
"""Class to perform plastic shear force resistance check for steel cross-section class 3 and 4 (Eurocode 3).
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Docstring says "plastic" but this is the elastic shear check.

The class docstring reads "Class to perform plastic shear force resistance check…" but CheckStrengthShearClass34 implements the elastic shear check (formula 6.19, not 6.18). The name field on line 254 correctly says "Elastic shear strength check", so this is just a copy-paste slip in the docstring.

-    """Class to perform plastic shear force resistance check for steel cross-section class 3 and 4 (Eurocode 3).
+    """Class to perform elastic shear force resistance check for steel cross-section class 3 and 4 (Eurocode 3).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@dataclass(frozen=True)
class CheckStrengthShearClass34:
"""Class to perform plastic shear force resistance check for steel cross-section class 3 and 4 (Eurocode 3).
`@dataclass`(frozen=True)
class CheckStrengthShearClass34:
"""Class to perform elastic shear force resistance check for steel cross-section class 3 and 4 (Eurocode 3).
🤖 Prompt for AI Agents
In `@blueprints/checks/eurocode/steel/strength_shear.py` around lines 204 - 206,
The class docstring for CheckStrengthShearClass34 incorrectly says "plastic"
while the implementation (and the name field "Elastic shear strength check")
performs the elastic shear check (formula 6.19); update the docstring to
describe the elastic shear force resistance check for steel cross-section class
3 and 4 (Eurocode 3), optionally referencing formula 6.19 to match the
implemented behavior in CheckStrengthShearClass34 and keep the description
consistent with the `name` attribute.

Comment on lines +278 to +287
def elastic_resistance(self) -> float:
"""Calculate the shear force elastic resistance of the steel cross-section (EN 1993-1-1:2005 art. 6.2.6).

Returns
-------
float
The calculated shear force resistance in N.
"""
sig_zxy = self.shear_stress()
return float(self.steel_cross_section.yield_strength / np.sqrt(3) / self.gamma_m0 / sig_zxy * abs(self.v) * KN_TO_N)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Division by zero in elastic_resistance() when v == 0.

When v == 0, shear_stress() returns 0, and the expression ... / sig_zxy * abs(self.v) ... evaluates to inf * 0 = nan (numpy semantics). This nan is then passed to CheckResult.from_comparison, which appears to handle provided == 0 gracefully (tests pass), but the intermediate nan is fragile and could break if CheckResult or downstream logic changes.

Consider guarding explicitly:

Proposed fix
 def elastic_resistance(self) -> float:
     ...
     sig_zxy = self.shear_stress()
+    if sig_zxy == 0:
+        return float("inf")
     return float(self.steel_cross_section.yield_strength / np.sqrt(3) / self.gamma_m0 / sig_zxy * abs(self.v) * KN_TO_N)
🤖 Prompt for AI Agents
In `@blueprints/checks/eurocode/steel/strength_shear.py` around lines 278 - 287,
elastic_resistance() can produce NaN when shear_stress() returns 0 (e.g. when
self.v == 0) due to 0*inf semantics; update elastic_resistance to guard against
zero shear stress or zero shear force by checking sig_zxy == 0 or self.v == 0
and return 0.0 in that case, otherwise compute the existing formula using
self.steel_cross_section.yield_strength, self.gamma_m0 and KN_TO_N; reference
the elastic_resistance and shear_stress methods and the attributes self.v,
self.steel_cross_section.yield_strength, and self.gamma_m0.

@@ -0,0 +1,413 @@
"""Module for checking torsional shear stress resistance with shear force present (Eurocode 2, formula 6.23)."""
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Module docstring references "Eurocode 2" instead of "Eurocode 3".

EN 1993-1-1 is Eurocode 3 (steel structures), not Eurocode 2 (concrete). Also, this module implements formulas 6.25/6.26 (combined torsion + shear), not 6.23.

📝 Proposed fix
-"""Module for checking torsional shear stress resistance with shear force present (Eurocode 2, formula 6.23)."""
+"""Module for checking torsional shear stress resistance with shear force present (Eurocode 3, EN 1993-1-1:2005, formulas 6.25/6.26)."""
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"""Module for checking torsional shear stress resistance with shear force present (Eurocode 2, formula 6.23)."""
"""Module for checking torsional shear stress resistance with shear force present (Eurocode 3, EN 1993-1-1:2005, formulas 6.25/6.26)."""
🤖 Prompt for AI Agents
In `@blueprints/checks/eurocode/steel/strength_torsion_shear.py` at line 1, Update
the module docstring in
blueprints/checks/eurocode/steel/strength_torsion_shear.py to correctly
reference "Eurocode 3 (EN 1993-1-1)" instead of "Eurocode 2" and state that the
implemented checks use formulas 6.25/6.26 for combined torsion and shear (not
6.23); keep the brief description of the module purpose (torsional shear stress
resistance with shear force present) but correct the standard and formula
numbers to match the implemented logic in this module.

Comment on lines +56 to +69
-------
from blueprints.checks.eurocode.steel.strength_torsion_shear import CheckStrengthTorsionShearClass12
from blueprints.materials.steel import SteelMaterial, SteelStrengthClass
from blueprints.structural_sections.steel.standard_profiles.heb import HEB

steel_material = SteelMaterial(steel_class=SteelStrengthClass.S355)
heb_300_profile = HEB.HEB300.with_corrosion(1.5)
m_x = 10 # Applied torsional moment in kNm
v = 100 # Applied shear force in kN
axis = "Vz" # Shear force applied in z-direction

heb_300_s355 = SteelCrossSection(profile=heb_300_profile, material=steel_material)
calc = CheckStrengthTorsionShearClass12(heb_300_s355, mx, v=v, axis=axis, gamma_m0=1.0)
calc.report().to_word("torsion_and_shear_strength.docx", language="nl")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Example code has an undefined variable mx.

Line 68 references mx but the local variable on line 63 is m_x. This will cause a NameError if anyone copies the example.

📝 Proposed fix
-    calc = CheckStrengthTorsionShearClass12(heb_300_s355, mx, v=v, axis=axis, gamma_m0=1.0)
+    calc = CheckStrengthTorsionShearClass12(heb_300_s355, m_x=m_x, v=v, axis=axis, gamma_m0=1.0)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
-------
from blueprints.checks.eurocode.steel.strength_torsion_shear import CheckStrengthTorsionShearClass12
from blueprints.materials.steel import SteelMaterial, SteelStrengthClass
from blueprints.structural_sections.steel.standard_profiles.heb import HEB
steel_material = SteelMaterial(steel_class=SteelStrengthClass.S355)
heb_300_profile = HEB.HEB300.with_corrosion(1.5)
m_x = 10 # Applied torsional moment in kNm
v = 100 # Applied shear force in kN
axis = "Vz" # Shear force applied in z-direction
heb_300_s355 = SteelCrossSection(profile=heb_300_profile, material=steel_material)
calc = CheckStrengthTorsionShearClass12(heb_300_s355, mx, v=v, axis=axis, gamma_m0=1.0)
calc.report().to_word("torsion_and_shear_strength.docx", language="nl")
-------
from blueprints.checks.eurocode.steel.strength_torsion_shear import CheckStrengthTorsionShearClass12
from blueprints.materials.steel import SteelMaterial, SteelStrengthClass
from blueprints.structural_sections.steel.standard_profiles.heb import HEB
steel_material = SteelMaterial(steel_class=SteelStrengthClass.S355)
heb_300_profile = HEB.HEB300.with_corrosion(1.5)
m_x = 10 # Applied torsional moment in kNm
v = 100 # Applied shear force in kN
axis = "Vz" # Shear force applied in z-direction
heb_300_s355 = SteelCrossSection(profile=heb_300_profile, material=steel_material)
calc = CheckStrengthTorsionShearClass12(heb_300_s355, m_x=m_x, v=v, axis=axis, gamma_m0=1.0)
calc.report().to_word("torsion_and_shear_strength.docx", language="nl")
🤖 Prompt for AI Agents
In `@blueprints/checks/eurocode/steel/strength_torsion_shear.py` around lines 56 -
69, The example uses an undefined variable mx when constructing
CheckStrengthTorsionShearClass12; change the argument to use the declared m_x
variable (i.e., pass m_x into CheckStrengthTorsionShearClass12) so the call to
CheckStrengthTorsionShearClass12(heb_300_s355, mx, v=v, axis=axis, gamma_m0=1.0)
becomes consistent with the earlier m_x declaration and avoids the NameError.

@@ -0,0 +1,184 @@
"""Module for checking torsional shear stress resistance (Eurocode 2, formula 6.23)."""
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Module docstring references "Eurocode 2" instead of "Eurocode 3".

Same issue as in strength_torsion_shear.py. EN 1993-1-1 is Eurocode 3.

📝 Proposed fix
-"""Module for checking torsional shear stress resistance (Eurocode 2, formula 6.23)."""
+"""Module for checking torsional shear stress resistance (Eurocode 3, EN 1993-1-1:2005, formula 6.23)."""
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"""Module for checking torsional shear stress resistance (Eurocode 2, formula 6.23)."""
"""Module for checking torsional shear stress resistance (Eurocode 3, EN 1993-1-1:2005, formula 6.23)."""
🤖 Prompt for AI Agents
In `@blueprints/checks/eurocode/steel/strength_torsion.py` at line 1, The module
docstring in blueprints/checks/eurocode/steel/strength_torsion.py incorrectly
says "Eurocode 2"; update the top-level docstring to reference "Eurocode 3" (EN
1993-1-1) and adjust the descriptive text accordingly so the module correctly
states it implements torsional shear stress resistance per Eurocode 3 (EN
1993-1-1).

Comment on lines +45 to +58
Example
-------
from blueprints.checks.eurocode.steel.torsion_strength import TorsionStrengthCheck
from blueprints.materials.steel import SteelMaterial, SteelStrengthClass
from blueprints.structural_sections.steel.standard_profiles.heb import HEB

steel_material = SteelMaterial(steel_class=SteelStrengthClass.S355)
heb_300_profile = HEB.HEB300.with_corrosion(1.5)
m_x = 10 # Applied torsional moment in kNm

heb_300_s355 = SteelCrossSection(profile=heb_300_profile, material=steel_material)
calc = TorsionStrengthCheck(heb_300_s355, m_x, gamma_m0=1.0)
calc.report().to_word("torsion_strength.docx", language="nl")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Example code references old class name and import path.

The example imports TorsionStrengthCheck from blueprints.checks.eurocode.steel.torsion_strength, but the actual class is CheckStrengthStVenantTorsionClass1234 in blueprints.checks.eurocode.steel.strength_torsion.

📝 Proposed fix
-    from blueprints.checks.eurocode.steel.torsion_strength import TorsionStrengthCheck
+    from blueprints.checks.eurocode.steel.strength_torsion import CheckStrengthStVenantTorsionClass1234
     from blueprints.materials.steel import SteelMaterial, SteelStrengthClass
     from blueprints.structural_sections.steel.standard_profiles.heb import HEB
 
     steel_material = SteelMaterial(steel_class=SteelStrengthClass.S355)
     heb_300_profile = HEB.HEB300.with_corrosion(1.5)
     m_x = 10  # Applied torsional moment in kNm
 
     heb_300_s355 = SteelCrossSection(profile=heb_300_profile, material=steel_material)
-    calc = TorsionStrengthCheck(heb_300_s355, m_x, gamma_m0=1.0)
-    calc.report().to_word("torsion_strength.docx", language="nl")
+    calc = CheckStrengthStVenantTorsionClass1234(heb_300_s355, m_x=m_x, gamma_m0=1.0)
+    calc.report().to_word("torsion_strength.docx", language="nl")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Example
-------
from blueprints.checks.eurocode.steel.torsion_strength import TorsionStrengthCheck
from blueprints.materials.steel import SteelMaterial, SteelStrengthClass
from blueprints.structural_sections.steel.standard_profiles.heb import HEB
steel_material = SteelMaterial(steel_class=SteelStrengthClass.S355)
heb_300_profile = HEB.HEB300.with_corrosion(1.5)
m_x = 10 # Applied torsional moment in kNm
heb_300_s355 = SteelCrossSection(profile=heb_300_profile, material=steel_material)
calc = TorsionStrengthCheck(heb_300_s355, m_x, gamma_m0=1.0)
calc.report().to_word("torsion_strength.docx", language="nl")
Example
-------
from blueprints.checks.eurocode.steel.strength_torsion import CheckStrengthStVenantTorsionClass1234
from blueprints.materials.steel import SteelMaterial, SteelStrengthClass
from blueprints.structural_sections.steel.standard_profiles.heb import HEB
steel_material = SteelMaterial(steel_class=SteelStrengthClass.S355)
heb_300_profile = HEB.HEB300.with_corrosion(1.5)
m_x = 10 # Applied torsional moment in kNm
heb_300_s355 = SteelCrossSection(profile=heb_300_profile, material=steel_material)
calc = CheckStrengthStVenantTorsionClass1234(heb_300_s355, m_x=m_x, gamma_m0=1.0)
calc.report().to_word("torsion_strength.docx", language="nl")
🤖 Prompt for AI Agents
In `@blueprints/checks/eurocode/steel/strength_torsion.py` around lines 45 - 58,
Update the example to use the actual class name and import path: replace the
import of TorsionStrengthCheck from
blueprints.checks.eurocode.steel.torsion_strength with the correct import of
CheckStrengthStVenantTorsionClass1234 from
blueprints.checks.eurocode.steel.strength_torsion, and instantiate that class
(replace TorsionStrengthCheck(...) with
CheckStrengthStVenantTorsionClass1234(...)) so the example matches the real
symbol names.

Comment on lines +236 to +239
Returns
-------
Callable[..., StressPost]
A function that calculates the stress distribution when called.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Docstring return type is incorrect.

The Returns section says Callable[..., StressPost] with description "A function that calculates the stress distribution when called." However, the method signature returns StressPost directly, and the implementation indeed returns the result of section.calculate_stress(...) which is a StressPost. The docstring appears to be a leftover from a different design.

📝 Proposed fix
         Returns
         -------
-        Callable[..., StressPost]
-            A function that calculates the stress distribution when called.
+        StressPost
+            The stress distribution for the given internal forces.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Returns
-------
Callable[..., StressPost]
A function that calculates the stress distribution when called.
Returns
-------
StressPost
The stress distribution for the given internal forces.
🤖 Prompt for AI Agents
In `@blueprints/structural_sections/_profile.py` around lines 236 - 239, The
Returns docstring incorrectly states "Callable[..., StressPost]" but the method
returns a StressPost directly (it calls section.calculate_stress(...)); update
the Returns section to "StressPost" and change the description to reflect that
the function returns a StressPost instance (e.g., "StressPost — stress
distribution result from section.calculate_stress") so the docstring matches the
actual return value.

Comment on lines +269 to +289
def unit_stress(self) -> dict[str, Any]:
"""Calculate the unit stress distribution for the profile.

This property is cached, so the calculation is performed only once per instance.

Returns
-------
StressPost
The unit stress distribution for the profile.
"""
# Check if we already have cached unit stress
if self._unit_stress_cache is not None:
return self._unit_stress_cache

# Calculate unit stress
result = self.calculate_stress(1, 1, 1, 1, 1, 1).get_stress()[0]

# Cache the result
object.__setattr__(self, "_unit_stress_cache", result)

return result
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Docstring return type says StressPost but the method returns dict[str, Any].

The type hint on line 269 correctly says -> dict[str, Any], and the implementation returns self.calculate_stress(...).get_stress()[0] which is a dict. The docstring on lines 276-277 should match.

📝 Proposed fix
         Returns
         -------
-        StressPost
-            The unit stress distribution for the profile.
+        dict[str, Any]
+            The unit stress distribution dictionary for the profile (first element from get_stress()).
🤖 Prompt for AI Agents
In `@blueprints/structural_sections/_profile.py` around lines 269 - 289, The
docstring for the unit_stress method incorrectly declares the return type as
StressPost; update the docstring to match the actual return type (dict[str,
Any]) or otherwise describe that it returns a dictionary representing the unit
stress distribution. Locate the unit_stress method and change the "Returns"
section (and the type on the first line of the docstring, if present) so it
accurately states dict[str, Any] and describes the returned dict from
calculate_stress(...).get_stress()[0]; leave the implementation and the
_unit_stress_cache handling unchanged.

Comment on lines +26 to +44
def test_result_tension_ok(self, unp_steel_cross_section: SteelCrossSection) -> None:
"""Test result() for ok tension load."""
m_x = -0.3896 * 0.99
calc = CheckStrengthStVenantTorsionClass1234(unp_steel_cross_section, m_x, gamma_m0=1.0)
result = calc.result()
assert result.is_ok is True
assert pytest.approx(result.unity_check, 0.005) == 0.99
assert pytest.approx(result.factor_of_safety, 0.005) == 1 / 0.99
assert calc.report()

def test_result_tension_not_ok(self, unp_steel_cross_section: SteelCrossSection) -> None:
"""Test result() for not ok tension load."""
m_x = 0.3896 * 1.01
calc = CheckStrengthStVenantTorsionClass1234(unp_steel_cross_section, m_x, gamma_m0=1.0)
result = calc.result()
assert result.is_ok is False
assert pytest.approx(result.unity_check, 0.005) == 1.01
assert pytest.approx(result.factor_of_safety, 0.005) == 1 / 1.01
assert calc.report()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Test method names say "tension" but they test torsion.

test_result_tension_ok and test_result_tension_not_ok test torsional moment (m_x) scenarios, not tension. This appears to be a naming oversight from copy-paste. Consider renaming for clarity.

📝 Proposed fix
-    def test_result_tension_ok(self, unp_steel_cross_section: SteelCrossSection) -> None:
-        """Test result() for ok tension load."""
+    def test_result_torsion_ok(self, unp_steel_cross_section: SteelCrossSection) -> None:
+        """Test result() for ok torsion load."""
         m_x = -0.3896 * 0.99
         ...

-    def test_result_tension_not_ok(self, unp_steel_cross_section: SteelCrossSection) -> None:
-        """Test result() for not ok tension load."""
+    def test_result_torsion_not_ok(self, unp_steel_cross_section: SteelCrossSection) -> None:
+        """Test result() for not ok torsion load."""
         m_x = 0.3896 * 1.01
         ...
🤖 Prompt for AI Agents
In `@tests/checks/eurocode/steel/test_strength_torsion.py` around lines 26 - 44,
Rename the two misnamed test functions to reflect they test torsion: change
test_result_tension_ok to test_result_torsion_ok and test_result_tension_not_ok
to test_result_torsion_not_ok; update any references/usages (e.g., in test
discovery or fixtures) and keep the body unchanged — these tests instantiate
CheckStrengthStVenantTorsionClass1234 with m_x and assert torsional
unity_check/factor_of_safety, so only the function names need renaming for
clarity.

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.

[✨ Feature request]: PART 4 - Add torsion steel check

1 participant