Source code for simdesign.rcmrf.geometry.custom

"""
Custom geometry module for defining frame structures from Excel input files.

This module provides the :class:`CustomGeometry` class, which reads structural
geometry data (points, lines, and rectangles) from a user-supplied Excel
workbook and builds a complete frame model via the base geometry framework.
"""
# Imports from installed packages
import pandas as pd
from pathlib import Path
from typing import Union

# Imports from geometry library
from .base import GeometryBase, Point, SystemGridData


# TODO: Use pydantic to set schema for CustomGeometry
[docs] class CustomGeometry(GeometryBase): """ Class representing a custom frame structure. This class inherits from :class:`~simdesign.rcmrf.geometry.base.GeometryBase` and extends it to represent a custom frame structure. It initializes the frame with data from an Excel file specified by the provided path. Attributes ---------- xlsx_path : str | Path The path to the Excel file containing the frame data. """ xlsx_path: Union[str, Path] __str: str """The private attribute for string representation of the CustomGeometry. """ def __init__(self, xlsx_path: Union[str, Path]) -> None: """ Initialize the CustomGeometry with data from the specified Excel file. Parameters ---------- xlsx_path : Union[str, Path] The path to the Excel file containing the frame data. """ tag = Path(xlsx_path).name.removeprefix(".xlsx") self.__str = f"CustomGeometry-{tag}" self.xlsx_path = xlsx_path self._build_base() self._check_for_any_not_allowed_lines() self._check_for_any_not_allowed_rectangles() def __str__(self) -> str: """ Return a string representation of the CustomGeometry. Returns ------- str String representation of the CustomGeometry. """ return self.__str def _initialise_points(self) -> None: """ Read point data from the Excel workbook and populate ``self.points``. Reads the ``POINTS_SHEET`` worksheet, derives grid indices from the unique coordinate values, and creates a :class:`~simdesign.utils.mesh.Point` for each row. Sets ``self.system_grid_data`` once all points are built. """ # Get the data from excel sheet containing points tags and coordinates df = pd.read_excel(self.xlsx_path, sheet_name=self.POINTS_SHEET) # Set the grid coordinates unique_xs: list = (df['x-coord'].unique()).tolist() unique_ys: list = (df['y-coord'].unique()).tolist() unique_zs: list = (df['z-coord'].unique()).tolist() # Start creating points for _, row in df.iterrows(): coords = [float(row['x-coord']), float(row['y-coord']), float(row['z-coord'])] i = float(unique_xs.index(coords[0])) j = float(unique_ys.index(coords[1])) k = float(unique_zs.index(coords[2])) tag = int(row['tag']) grid = [i, j, k] point = Point(grid, coords, tag) self.points.append(point) # Set the system grid data self.system_grid_data = SystemGridData(self.points) def _initialise_lines(self) -> None: """ Read line connectivity from the Excel workbook and populate frame lines. Reads the ``LINES_SHEET`` worksheet and calls :meth:`add_new_line` for each row, assigning the component type and section rotation angle. """ # Get the data from excel sheet containing lines connectivity df = pd.read_excel(self.xlsx_path, sheet_name=self.LINES_SHEET) df = df.where(pd.notna(df), None) # Start creating lines for _, row in df.iterrows(): tag = int(row['tag']) point1 = self.find_point_by_tag(row['point-1']) point2 = self.find_point_by_tag(row['point-2']) rot_angle = float(row['sec-rotation']) component = row['component'] if component: component = str(component).lower().capitalize() self.add_new_line([point1, point2], tag, component, rot_angle) def _initialise_rectangles(self) -> None: """ Read rectangle data from the Excel workbook and populate floor slabs. Reads the ``RECTANGLES_SHEET`` worksheet and calls :meth:`add_new_rectangle` for each row, assigning the component type and infill strength. """ # Dataframe containing rectangles of floor slabs df = pd.read_excel(self.xlsx_path, sheet_name=self.RECTANGLES_SHEET) df = df.where(pd.notna(df), None) # Go through rectangle shapes for _, row in df.iterrows(): # Find points on the rectangle tag = int(row['tag']) point1 = self.find_point_by_tag(row['point-1']) point2 = self.find_point_by_tag(row['point-2']) point3 = self.find_point_by_tag(row['point-3']) point4 = self.find_point_by_tag(row['point-4']) points = [point1, point2, point3, point4] # Component type component = row['component'] if component: component = str(component).lower().capitalize() # infill strength - type strength = row['infill-type'] if strength: strength = str(strength).lower().capitalize() # Add new rectangle self.add_new_rectangle(points, tag, component, strength)