Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding example with analysis of reinforced concrete columns under biaxial bending #29

Merged
merged 2 commits into from
Oct 27, 2024

Conversation

janderson4
Copy link
Contributor

@janderson4 janderson4 commented Oct 13, 2024

Summary by Sourcery

Add a new example for analyzing reinforced concrete columns under biaxial bending, including PMM and PM diagrams, DCR calculations, and detailed calculation reports. Enhance the canvas elements to support markers and improve report generation with new options for displaying long calculations. Update documentation and add comprehensive tests for the new features.

New Features:

  • Introduce a new example for analyzing reinforced concrete columns under biaxial bending, including calculation of PMM diagrams, PM diagrams, and demand-to-capacity ratios (DCRs).
  • Add support for generating detailed calculation reports for reinforced concrete columns, including cross-section diagrams and code references.
  • Implement a new feature to create 3D Axial-Moment-Moment interaction diagrams and 2D PM interaction diagrams for specific load cases.
  • Add new classes and methods for handling canvas elements with markers, including lines, polylines, text, dimensions, and leaders.
  • Introduce a new enumeration for controlling the display of long mathematical expressions in reports, with options for scaling or line-breaking.

Enhancements:

  • Refactor the canvas elements to support markers, allowing for more flexible and detailed SVG representations.
  • Enhance the report generation capabilities with new options for displaying long calculations and improved table handling.
  • Improve the documentation structure and content, updating the version and adding new sections for the canvas and calculation helpers.

Documentation:

  • Update the documentation to reflect the new version 1.2.4, including changes in the canvas and calculation helpers sections.
  • Add detailed documentation for new classes and methods related to reinforced concrete column analysis and canvas elements.

Tests:

  • Add comprehensive tests for the new reinforced concrete column analysis features, including tests for PMM and PM diagrams.
  • Introduce tests for the new canvas elements with markers, ensuring correct SVG output and marker handling.
  • Add tests for the new table and input table classes, verifying data handling and override functionality.

Copy link

sourcery-ai bot commented Oct 13, 2024

Reviewer's Guide by Sourcery

This pull request adds an example with analysis of reinforced concrete columns under biaxial bending. It introduces new functionality for drawing and analyzing concrete column cross-sections, calculating PMM (axial force-moment-moment) interaction diagrams, and generating calculation reports. The changes include new classes, functions, and tests related to concrete column analysis, as well as updates to existing canvas and table functionality in the efficalc library.

Class diagram for new and updated canvas elements

classDiagram
    class CanvasElement {
        <<abstract>>
    }
    class ElementWithMarkers {
        +_get_markers() List~Marker~
        +get_markers() List~Marker~
        +_apply_context_marker_styles(marker: Marker) Marker
    }
    CanvasElement <|-- ElementWithMarkers
    class Line {
        +_get_markers() List~Marker~
    }
    ElementWithMarkers <|-- Line
    class Polyline {
        +_get_markers() List~Marker~
    }
    ElementWithMarkers <|-- Polyline
    class Text {
        +text: str
        +x: float
        +y: float
        +font_size: float | str
        +rotate: float
        +horizontal_base: Literal["start", "center", "end"]
        +vertical_base: Literal["auto", "top", "middle", "bottom"]
        +fill: str
        +stroke: str
        +stroke_width: float
        +to_svg() str
    }
    CanvasElement <|-- Text
    class Dimension {
        +x1: float
        +y1: float
        +x2: float
        +y2: float
        +text: Optional~str~
        +gap: float
        +offset: float
        +unit: Optional~str~
        +text_position: Literal["top", "bottom"]
        +text_size: float
        +to_svg() str
    }
    ElementWithMarkers <|-- Dimension
    class Leader {
        +marker_x: float
        +marker_y: float
        +text_x: float
        +text_y: float
        +text: str
        +marker: Marker
        +landing_len: float
        +direction: Literal["right", "left"]
        +text_size: float
        +to_svg() str
    }
    ElementWithMarkers <|-- Leader
    class ArrowMarker {
        +reverse: bool
        +orientation: MarkerOrientation
        +base: Literal["point", "center", "flat"]
        +to_svg() str
    }
    Marker <|-- ArrowMarker
Loading

File-Level Changes

Change Details Files
Added concrete column analysis functionality
  • Introduced Column class to represent concrete column properties
  • Added functions for calculating axial load limits
  • Implemented PMM diagram generation and analysis
  • Created functions for searching and interpolating points on the PMM surface
  • Added visual tests for PMM plotting
  • Implemented DCR (Demand-to-Capacity Ratio) calculations
examples/conc_col_pmm/col/column.py
examples/conc_col_pmm/col/assign_max_min.py
examples/conc_col_pmm/calc_document/plotting/pmm_mesh.py
examples/conc_col_pmm/pmm_search/load_search/point_search_load.py
examples/conc_col_pmm/pmm_search/ecc_search/get_dcr_ecc.py
examples/conc_col_pmm/tests/visual_tests/visual_test_pmm_plotter_plotly.py
Enhanced canvas drawing capabilities
  • Added new canvas elements: Text, Dimension, and Leader
  • Implemented drawing functions for column cross-sections and compression zones
  • Updated existing canvas elements with new features
efficalc/canvas/canvas_elements.py
examples/conc_col_pmm/col/col_canvas/draw_column_with_dimensions.py
examples/conc_col_pmm/col/col_canvas/draw_column_comp_zone.py
Added Table and InputTable classes
  • Implemented Table class for displaying data in reports
  • Created InputTable class for accepting dynamic input data
  • Added tests for Table and InputTable functionality
efficalc/base_definitions/table.py
tests/base_definitions/test_table.py
Updated report generation and styling
  • Modified ReportBuilder to handle long calculation display options
  • Updated HTML generation for improved report styling
  • Added support for tables in report generation
efficalc/report_builder.py
efficalc/generate_html.py
Added new tests and updated existing ones
  • Created tests for concrete column analysis functions
  • Added tests for new canvas elements
  • Updated existing tests to accommodate new features
examples/conc_col_pmm/tests/test_get_dcr_ecc.py
tests/canvas/test_dimension.py
tests/canvas/test_leader.py
tests/canvas/test_text.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @janderson4 - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider adding more inline documentation for complex algorithms, especially in the PMM search functions. While the code is well-structured, additional comments explaining the mathematical concepts would improve maintainability.
  • The new plotting functions using matplotlib and plotly are a great addition. However, consider abstracting the plotting logic further to allow for easier swapping of plotting backends in the future.
Here's what I looked at during the review
  • 🟡 General issues: 9 issues found
  • 🟢 Security: all looks good
  • 🟡 Testing: 5 issues found
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@@ -28,9 +32,11 @@ def __init__(
self,
width: float,
height: float,
min_xy: tuple[float, float] = (0, 0),
Copy link

Choose a reason for hiding this comment

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

suggestion: Consider adding a comment explaining the purpose and usage of min_xy

This new parameter affects the canvas coordinate system, but its purpose and effects are not immediately clear. A brief explanation would help users understand how to use it effectively.

        min_xy: tuple[float, float] = (0, 0),
        """Minimum x and y coordinates for the canvas. Defaults to (0, 0)."""

self._apply_context_marker_styles(marker)
return markers

def _apply_context_marker_styles(self, marker: Marker) -> Marker:
Copy link

Choose a reason for hiding this comment

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

suggestion: Consider the placement and implementation of _apply_context_marker_styles

This method seems similar to the one removed from the Canvas class. Consider if this is the optimal location for this functionality or if it could be further simplified.

    def _apply_context_marker_styles(self, marker: Marker) -> None:
        if marker.fill == "context-fill":
            marker.fill = self.context_fill
        if marker.stroke == "context-stroke":
            marker.stroke = self.context_stroke

def to_svg(self) -> str:
return f'<path d="{self.to_path_commands()}"{self.get_common_svg_style_elements()}{self._get_marker_assignments()} />'


class Text(CanvasElement):
Copy link

Choose a reason for hiding this comment

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

suggestion: Consider creating a common base class or utility functions for new element classes

The new classes (Text, Dimension, Leader) share similar patterns, especially in their to_svg methods. A common base class or shared utility functions could help reduce code duplication.

class SVGElement(CanvasElement):
    def to_svg(self):
        raise NotImplementedError("Subclasses must implement to_svg method")

class Text(SVGElement):

@@ -210,6 +214,32 @@ def _generate_comparison_html(item: Comparison) -> str:
return _wrap_div(comp_html, class_name=CALC_ITEM_WRAPPER_CLASS)


def _generate_result_table_html(item: Table) -> str:
Copy link

Choose a reason for hiding this comment

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

suggestion: Consider using a template engine or helper functions for HTML generation

The new HTML generation code, particularly in _generate_result_table_html, introduces quite a bit of string concatenation. Consider using a template engine or extracting some of this logic into separate helper functions for better maintainability.

def _generate_result_table_html(item: Table) -> str:
    table_attrs = []
    if item.full_width:
        table_attrs.append('width="100%"')
    if item.striped:
        table_attrs.append('class="striped"')
    return f'<table {" ".join(table_attrs)}>'

"""


def plot(pmm_data):
Copy link

Choose a reason for hiding this comment

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

suggestion: Consider refactoring the plot function for improved maintainability

The plot function is quite long and complex. Consider breaking it down into smaller, more focused functions to improve readability and maintainability. This will make the code easier to test and modify in the future.

def plot(pmm_data):
    """Generate plots for PMM data."""
    return _generate_plots(pmm_data)

def _generate_plots(pmm_data):
    X = pmm_data.X
    Y = pmm_data.Y


plt.show()

# plt.savefig("test_plot.png")
Copy link

Choose a reason for hiding this comment

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

suggestion (testing): Visual test for point plotter

This visual test is useful for manually verifying the output of the point plotter. Consider automating this test by comparing the generated plot with a reference image, or by checking specific properties of the plot programmatically.

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image

# ... (rest of the imports and functions remain the same)

if __name__ == "__main__":
    # ... (existing code to generate the plot)

    plt.savefig("generated_plot.png")
    plt.close()

    generated_image = np.array(Image.open("generated_plot.png"))
    reference_image = np.array(Image.open("reference_plot.png"))

    assert np.allclose(generated_image, reference_image, atol=5), "Generated plot differs from reference"


fig = pmm_plotter_plotly.plot(pmm_data)

fig.show()
Copy link

Choose a reason for hiding this comment

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

suggestion (testing): Visual test for PMM plotter using Plotly

Similar to the point plotter test, consider automating this test. You could check specific properties of the Plotly figure object, such as the number of traces, axis labels, or data ranges.

import pytest
from plotly.graph_objs import Figure

def test_pmm_plotter_plotly():
    col = example_col()
    axial_limits = assign_max_min.calculate_axial_load_limits(col)
    load_data = [[300, 100, 200, True], [-100, 50, -60, False], [1500, 300, -300, False]]
    loads = [LoadCombination(*load) for load in load_data]
    pmm_data = get_pmm_data.get_pmm_data(col, 36, 12, loads, axial_limits)
    fig = pmm_plotter_plotly.plot(pmm_data)
    assert isinstance(fig, Figure)
    assert len(fig.data) > 0
    assert fig.layout.scene.xaxis.title.text == "Mx"
    assert fig.layout.scene.yaxis.title.text == "My"
    assert fig.layout.scene.zaxis.title.text == "P"


pmm_data = get_pmm_data.get_pmm_data(example_col, 36, 12, loads, axial_limits)

_ = pmm_plotter_plotly.plot(pmm_data)
Copy link

Choose a reason for hiding this comment

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

suggestion (testing): Basic test for pmm_plotter_plotly function

This test checks for runtime errors but doesn't verify the correctness of the plot. Consider adding assertions to check the structure or content of the generated plot.

from ..calc_document.plotting import get_pmm_data, pmm_plotter_plotly
from ..col import assign_max_min
from ..pmm_search.load_combo import LoadCombination
import plotly.graph_objs as go

def test_pmm_plotter_plotly(example_col, loads):
    axial_limits = assign_max_min.calculate_axial_load_limits(example_col)
    load_data = [[300, 100, 200, True], [-100, 50, -60, False], [1500, 300, -300, False]]
    loads = [LoadCombination(*load) for load in load_data]
    pmm_data = get_pmm_data.get_pmm_data(example_col, 36, 12, loads, axial_limits)
    fig = pmm_plotter_plotly.plot(pmm_data)
    assert isinstance(fig, go.Figure)
    assert len(fig.data) > 0

# in the format (Mx, My, P).
_, _, _, mesh = pmm_mesh.get_mesh(col, 48, 18, axial_limits)

_ = get_capacity.get_capacity(mesh, loads[0])
Copy link

Choose a reason for hiding this comment

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

suggestion (testing): Basic test for get_capacity function

This test only checks if the function runs without errors. Consider adding assertions to verify the correctness of the returned capacity values.

from ..calc_document.plotting import get_capacity, pmm_mesh
from ..col.assign_max_min import calculate_axial_load_limits


def test_get_capacity(example_col, loads):
    col = example_col
    axial_limits = calculate_axial_load_limits(col)
    _, _, _, mesh = pmm_mesh.get_mesh(col, 48, 18, axial_limits)
    capacity = get_capacity.get_capacity(mesh, loads[0])
    assert isinstance(capacity, float)
    assert capacity > 0

_, _, _, mesh = pmm_mesh.get_mesh(example_col3, 36, 12, axial_limits)

capacity = get_capacity.get_capacity(mesh, loads[0])
_ = point_plotter.plot(capacity, loads[0], False)
Copy link

Choose a reason for hiding this comment

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

suggestion (testing): Basic test for point_plotter function

Similar to the pmm_plotter_plotly test, this only checks for runtime errors. Consider adding assertions to verify the content or structure of the generated plot.

def test_point_plotter(example_col3, loads):
    axial_limits = calculate_axial_load_limits(example_col3)
    _, _, _, mesh = pmm_mesh.get_mesh(example_col3, 36, 12, axial_limits)
    capacity = get_capacity.get_capacity(mesh, loads[0])
    plot = point_plotter.plot(capacity, loads[0], False)

    assert isinstance(plot, go.Figure)
    assert len(plot.data) > 0
    assert plot.layout.title.text is not None
    assert plot.layout.xaxis.title.text is not None
    assert plot.layout.yaxis.title.text is not None

Make the load case table numbered
Factor out the load case column capacities
Round very small numbers down to zero
Add detail in DCR calculations
@youandvern youandvern merged commit da94f29 into youandvern:main Oct 27, 2024
2 of 3 checks passed
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.

2 participants