diff --git a/doc/changelog.d/4127.miscellaneous.md b/doc/changelog.d/4127.miscellaneous.md new file mode 100644 index 00000000000..628893b7ce8 --- /dev/null +++ b/doc/changelog.d/4127.miscellaneous.md @@ -0,0 +1 @@ +Docs: adding reporting example \ No newline at end of file diff --git a/doc/source/examples/extended_examples/beam_analysis_with_reporting/beam_analysis_report.rst b/doc/source/examples/extended_examples/beam_analysis_with_reporting/beam_analysis_report.rst new file mode 100644 index 00000000000..96a26f4b4a0 --- /dev/null +++ b/doc/source/examples/extended_examples/beam_analysis_with_reporting/beam_analysis_report.rst @@ -0,0 +1,571 @@ +.. _beam_analysis_report_example: + +================================================ +I-Beam Analysis with Automated Report Generation +================================================ + +This comprehensive example demonstrates how to perform a complete structural analysis +of an I-beam using PyMAPDL and automatically generate detailed engineering reports. +The example showcases advanced PyMAPDL capabilities including parameterized modeling, +result extraction, and automated documentation generation. + +**What this example demonstrates:** + +* Parameterized I-beam geometry creation +* Material property definition and assignment +* Fully constrained beam at both ends +* Distributed load applied using nodal forces +* Comprehensive result extraction and postprocessing +* Automated report generation in Markdown and Word formats +* Analytical verification of finite element results + +Introduction +============ + +Engineering analysis often requires not only obtaining results but also documenting +the analysis process, assumptions, and findings in a professional report format. +This example shows how PyMAPDL can be used to create a complete analysis workflow +that includes automated report generation. + +The analysis focuses on a fully constrained I-beam subjected to a uniformly distributed +load, a fundamental structural engineering problem. The results are verified against +analytical solutions and presented in professional report formats. + +Problem Definition +================== + +Analyze a I-beam with the following characteristics: + +**Geometry:** + +- Length: 5.0 meters +- I-section with typical structural steel proportions +- Fully constrained at both ends + +**Loading:** + +- Uniformly distributed load of 50 kN/m (downward) +- Static analysis (no dynamic effects) + +**Material:** + +- Structural steel (S355 grade) +- Linear elastic behavior assumed + +**Objectives:** + +- Determine maximum displacement and location +- Calculate maximum bending stress +- Verify safety against yielding +- Generate comprehensive documentation + +Model Setup and Parametrization +=============================== + +The example begins by defining all analysis parameters in dictionaries for easy +modification and reuse: + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :dedent: 4 + :start-at: # I-beam geometric parameters + :end-at: print(f"Load: {load_params['distributed_load']} N/mm") + +This parameterized approach allows for: + +* Easy modification of beam dimensions +* Material property changes +* Load case variations +* Design optimization studies + +Preprocessing - Geometry and Meshing +==================================== + +Element Type Selection +---------------------- + +The analysis uses BEAM188 elements, which are: + +* 3D linear finite strain beam elements +* Suitable for thin to moderately thick beam structures +* Capable of handling large deflection effects (if needed) +* Provide comprehensive stress output + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :dedent: 4 + :start-at: # Define element type + :end-at: mapdl.keyopt(1, 6, 1) + +Cross-Section Definition +------------------------ + +The I-beam cross-section is defined using PyMAPDL's section commands: + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :dedent: 4 + :start-at: print(f"\n-- Setting MAPDL section properties --") + :end-at: print(beam_info) + +The ``SECDATA`` command defines the I-beam geometry where: + +* First two parameters: top and bottom flange widths +* Third parameter: total beam height +* Fourth and fifth parameters: flange thicknesses +* Sixth parameter: web thickness + +Node and Element Generation +--------------------------- + +Nodes are created along the beam length with uniform spacing: + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :dedent: 4 + :start-at: # Create nodes along the beam length + :end-at: mapdl.e(i + 1, i + 2) + +The mesh uses 20 elements along the beam length, providing sufficient resolution +for accurate results while maintaining computational efficiency. + +Boundary Conditions and Loading +=============================== + +Fully Constrained End Conditions +-------------------------------- + +Fully clamped boundary conditions on the ends are implemented as: + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :dedent: 4 + :start-at: print(f"\n-- Setting MAPDL Boundary conditions --") + :end-at: mapdl.d(last_node, "ROTZ", 0) + +Distributed Load Application +---------------------------- + +The uniformly distributed load is applied as equivalent nodal forces: + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :dedent: 4 + :start-at: # Apply distributed load as nodal forces + :end-at: mapdl.f(last_node, "FY", end_nodal_force) + +This approach: + +* Converts the continuous load to discrete nodal forces +* Maintains load equilibrium +* Provides accurate representation of the distributed loading + +Alternatively, the load can be applied using the ``SFBEAM`` command for surface loads on BEAM and PIPE elements. + +Solution Process +================ + +The static structural solution is configured and executed: + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :dedent: 4 + :start-at: # Enter solution processor + :end-at: print("Solution completed successfully") + +The solution uses: + +* Static analysis type (no time-dependent effects) +* Linear analysis (small deflection theory) +* Direct solver for optimal accuracy + +Post-Processing and Result Extraction +===================================== + +Displacement Results +-------------------- + +The maximum displacement is extracted using the `post_processing` module: + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :dedent: 4 + :start-at: # Get nodal displacements + :end-at: max_displacement_location = np.argmin(displacements) + 1 + +Moment Results +-------------- + +The moment at both nodes are extracted using ``ETABLE`` command, with ``SMISC`` items: + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :dedent: 4 + :start-at: # Get moment values + :end-at: max_moment_fem = max(max_moment_i, max_moment_j) + +Bending Stress Results +---------------------- + +Again ``ETABLE`` is used to retrieve MAPDL values: + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :dedent: 4 + :start-at: # Get bending stress values + :end-at: max_stress_fem = max(bending_stress_top, bending_stress_bottom) + +Bending Strain Results +---------------------- + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :dedent: 4 + :start-at: # Get bending strain values + :end-at: max_strain_fem = max(bending_strain_top, bending_strain_bottom) + +Report Generation Features +========================== + +Markdown Report +--------------- + +The Markdown report includes: + +* Executive summary with key results +* Detailed model description with parameter tables +* Comprehensive results presentation +* Analytical verification section +* Professional formatting with tables and equations + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :start-at: def generate_markdown_report(data, output_dir): + :end-at: return str(report_file) + +Word Document Report +-------------------- + +The Word document report provides: + +* Professional document formatting +* Tables for organized data presentation +* Structured sections and headings +* Executive summary format + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :start-at: def generate_word_report(data, output_dir): + :end-at: return str(word_file) + +**Note:** Word report generation requires the ``python-docx`` package: + +.. code-block:: bash + + pip install python-docx + +Plot Generation +--------------- + +The example includes the capability to generate analysis plots: + +.. literalinclude:: ../../../../../examples/00-mapdl-examples/beam_with_report.py + :language: python + :start-at: def generate_analysis_plots(mapdl, output_dir): + :end-at: return plot_files + +Generated plots include: + +* Displacement contours showing deformed shape +* Stress contours highlighting critical regions +* Node and element plots for model verification + +Running the Example +=================== + +Basic Execution +--------------- + +To run the complete analysis and generate reports: + +.. code-block:: python + + from beam_with_report import create_ibeam_analysis_and_report + + # Run complete analysis + results = create_ibeam_analysis_and_report() + +Or execute the script directly: + +.. code-block:: bash + + python beam_with_report.py + +Output Files +------------ + +The analysis generates several output files in the ``beam_analysis_output`` directory: + +* ``ibeam_analysis_report.md`` - Markdown report +* ``ibeam_analysis_report.docx`` - Word document +* ``displacement_plot.png`` - Displacement visualization +* ``stress_plot.png`` - Stress visualization + +Customization Options +===================== + +Parameter Modification +---------------------- + +Easily modify analysis parameters by changing the dictionaries: + +.. code-block:: python + + # Modify beam dimensions + beam_params["length"] = 8000.0 # Change to 8 meters + beam_params["web_height"] = 500.0 # Increase height + + # Change material + material_props["elastic_modulus"] = 200000.0 # Different steel grade + + # Modify loading + load_params["distributed_load"] = -75.0 # Increase load intensity + +Multiple Load Cases +------------------- + +The framework can be extended for multiple load cases: + +.. code-block:: python + + load_cases = [ + {"name": "Service Load", "distributed_load": -50.0}, + {"name": "Ultimate Load", "distributed_load": -75.0}, + {"name": "Wind Load", "distributed_load": -25.0}, + ] + + for case in load_cases: + # Run analysis for each case + # Generate separate reports + results = create_ibeam_analysis_and_report(**case) + +This parameterized approach enables design optimization studies. + + +Advanced Analysis Options +========================= + +The example can be extended for more sophisticated analyses. + +Nonlinear Analysis +------------------ + +.. code-block:: python + + # Enable geometric nonlinearity + mapdl.nlgeom("ON") + + # Material nonlinearity + mapdl.mp("BISO", 1, yield_strength, hardening_modulus) + +Dynamic Analysis +---------------- + +.. code-block:: python + + # Modal analysis + mapdl.antype("MODAL") + mapdl.modopt("LANB", 10) # First 10 modes + + +Additional Report Features +========================== + +PDF Generation +-------------- + +If you install `pandoc `_, you can convert the Markdown report to a PDF file as follows: + +.. code-block:: python + + # Convert Markdown to PDF using pandoc + import subprocess + + subprocess.run( + ["pandoc", "ibeam_analysis_report.md", "-o", "ibeam_analysis_report.pdf"] + ) + +Excel Export +------------ + +You can export results to Excel using `pandas`. This is quite useful when reporting parametric studies: + +.. code-block:: python + + import pandas as pd + + load_cases = [ + {"name": "Service Load", "distributed_load": -50.0}, + {"name": "Ultimate Load", "distributed_load": -75.0}, + {"name": "Wind Load", "distributed_load": -25.0}, + ] + + df_result = pd.DataFrame() + + for case in load_cases: + # Run analysis for each case + # Generate separate reports + results = create_ibeam_analysis_and_report(**case) + + # Create DataFrame from results + df_result = pd.concat( + [ + df_result, + pd.DataFrame( + { + "Load Case": case["name"], + "Max Displacement (mm)": results["max_displacement"], + "Max Bending Moment (Nm)": results["max_moment"], + "Max Bending Stress (MPa)": results["max_stress"], + "Max Bending Strain": results["max_strain"], + "Safety Factor": results["safety_factor"], + } + ), + ], + ignore_index=True, + ) + + # Save to Excel + df_result.to_excel("ibeam_load_cases_analysis_results.xlsx", index=False) + +Email Reports +------------- + +Using Python you can send the generated reports via email: + +.. code-block:: python + + # The following code is not tested + import smtplib + from email.mime.multipart import MIMEMultipart + from email.mime.text import MIMEText + from email.mime.base import MIMEBase + from email import encoders + import os + from pathlib import Path + + + def send_analysis_report_email( + smtp_server="smtp.gmail.com", + smtp_port=587, + sender_email="your-email@gmail.com", + sender_password="your-app-password", + recipient_emails=["recipient@example.com"], + subject="I-Beam Analysis Report", + body_text="", + attachments=None, + ): + """ + Send analysis reports via email with attachments + + Parameters + ---------- + smtp_server : str + SMTP server address (e.g., 'smtp.gmail.com', 'smtp.outlook.com') + smtp_port : int + SMTP port (587 for TLS, 465 for SSL) + sender_email : str + Sender's email address + sender_password : str + Sender's email password or app-specific password + recipient_emails : list + List of recipient email addresses + subject : str + Email subject line + body_text : str + Email body content + attachments : list + List of file paths to attach + """ + + # Create message container + msg = MIMEMultipart() + msg["From"] = sender_email + msg["To"] = ", ".join(recipient_emails) + msg["Subject"] = subject + + # Add body to email + msg.attach(MIMEText(body_text, "plain")) + + # Add attachments + if attachments: + for file_path in attachments: + if os.path.isfile(file_path): + with open(file_path, "rb") as attachment: + # Instance of MIMEBase and named as part + part = MIMEBase("application", "octet-stream") + part.set_payload(attachment.read()) + + # Encode file in ASCII characters to send by email + encoders.encode_base64(part) + + # Add header as key/value pair to attachment part + filename = os.path.basename(file_path) + part.add_header( + "Content-Disposition", + f"attachment; filename= {filename}", + ) + + # Attach the part to message + msg.attach(part) + + try: + # Create SMTP session + server = smtplib.SMTP(smtp_server, smtp_port) + server.starttls() # Enable security + server.login(sender_email, sender_password) + + # Send email + text = msg.as_string() + server.sendmail(sender_email, recipient_emails, text) + server.quit() + + print(f"Email sent successfully to {', '.join(recipient_emails)}") + return True + + except Exception as e: + print(f"Error sending email: {e}") + return False + + +Conclusion +========== + +This example demonstrates a complete PyMAPDL workflow that goes beyond simple +analysis to include professional documentation and reporting. +It provides a foundation for developing robust engineering analysis tools and +workflows using PyMAPDL by combining: + +* Parameterized modeling +* Comprehensive result extraction +* Analytical verification +* Automated report generation + +The approach shown here can be adapted for various structural analysis problems +and extended with additional features as needed for specific engineering applications. + +References +========== + +* PyMAPDL Documentation: https://mapdl.docs.pyansys.com/ +* Ansys Mechanical APDL Element Reference + +Additional files +================ + +* :download:`beam_with_report.py `: Complete Python script. + +Examples of the generated report files are: + +* :download:`ibeam_analysis_report.md `: Markdown report file. +* :download:`ibeam_analysis_report.docx `: Word document file. \ No newline at end of file diff --git a/doc/source/examples/extended_examples/beam_analysis_with_reporting/displacement_plot.png b/doc/source/examples/extended_examples/beam_analysis_with_reporting/displacement_plot.png new file mode 100644 index 00000000000..bfcdc41ed97 Binary files /dev/null and b/doc/source/examples/extended_examples/beam_analysis_with_reporting/displacement_plot.png differ diff --git a/doc/source/examples/extended_examples/beam_analysis_with_reporting/geometry.png b/doc/source/examples/extended_examples/beam_analysis_with_reporting/geometry.png new file mode 100644 index 00000000000..aaa4cab88d0 Binary files /dev/null and b/doc/source/examples/extended_examples/beam_analysis_with_reporting/geometry.png differ diff --git a/doc/source/examples/extended_examples/beam_analysis_with_reporting/ibeam_analysis_report.docx b/doc/source/examples/extended_examples/beam_analysis_with_reporting/ibeam_analysis_report.docx new file mode 100644 index 00000000000..10a341d2996 Binary files /dev/null and b/doc/source/examples/extended_examples/beam_analysis_with_reporting/ibeam_analysis_report.docx differ diff --git a/doc/source/examples/extended_examples/beam_analysis_with_reporting/ibeam_analysis_report.md b/doc/source/examples/extended_examples/beam_analysis_with_reporting/ibeam_analysis_report.md new file mode 100644 index 00000000000..27deefd5972 --- /dev/null +++ b/doc/source/examples/extended_examples/beam_analysis_with_reporting/ibeam_analysis_report.md @@ -0,0 +1,103 @@ +# I-Beam Structural Analysis Report + +**Generated:** 2025-07-29 16:26:27 +**Analysis Type:** Static Structural Analysis +**Software:** PyMAPDL (Ansys MAPDL) + +## Executive Summary + +This report presents the results of a static structural analysis of a simply supported I-beam subjected to a uniformly distributed load. The analysis was performed using finite element methods via PyMAPDL. + +### Key Results +- **Maximum Displacement:** 15.53 mm +- **Maximum Stress:** 388.9 MPa +- **Safety Factor:** 0.91 +- **Total Applied Load:** 250.0 kN + +## Model Description + +### Geometry +The analyzed structure is an I-beam with the following dimensions: + +| Parameter | Value | Unit | +|-----------|-------|------| +| Length | 5000 | mm | +| Flange Width | 200 | mm | +| Web Height | 400 | mm | +| Flange Thickness | 20 | mm | +| Web Thickness | 12 | mm | + + +![Beam section plot](ibeam_section.png) + +*Figure 1: I-beam cross-section showing key dimensions* + + +![Beam elements plot](geometry.png) + +*Figure 2: Beam showing elements* + +### Material Properties +The beam is modeled using structural steel with the following properties: + +| Property | Value | Unit | +|----------|-------|------| +| Elastic Modulus | 200000 | MPa | +| Poisson's Ratio | 0.30 | - | +| Density | 7850000 | kg/m³ | +| Yield Strength | 355 | MPa | + +### Loading and Boundary Conditions +- **Support Type:** Simply supported (pin and roller) +- **Load Type:** Uniformly distributed load +- **Load Magnitude:** 50 N/mm + +## Analysis Results + +### Displacement Results +- **Maximum vertical displacement:** 15.53 mm +- **Location:** Node 11 (approximately mid-span) + +### Stress Results +- **Maximum bending stress:** 388.9 MPa +- **Location:** Extreme fibers at mid-span +- **Safety factor:** 0.91 + +### Section Properties +| Property | Value | Unit | +|----------|-------|------| +| Cross-sectional Area | 12320 | mm² | +| Moment of Inertia (Iyy) | 335720000 | mm⁴ | +| Section Modulus | 1678600 | mm³ | + +### Analysis Plots + +The following plots visualize the analysis results: + +#### Displacement Contours +![Displacement Plot](displacement_plot.png) + +*Figure 3: Y-direction displacement contours showing maximum deformation at mid-span* + +#### Stress Distribution +![Stress Plot](stress_plot.png) + +*Figure 4: Equivalent stress distribution showing maximum stress at beam extreme fibers* + +## Conclusions + +1. **Structural Adequacy:** The beam safely carries the applied load with a safety factor of 0.91. + +2. **Displacement:** The maximum displacement of 15.53 mm is within acceptable limits for most structural applications. + +3. **Recommendation:** The I-beam design is adequate for the specified loading conditions. + +## Analysis Details + +- **Element Type:** BEAM188 (3D linear finite strain beam) +- **Number of Elements:** 20 +- **Solution Type:** Static linear analysis +- **Convergence:** Solution converged successfully + +--- +*This report was automatically generated using PyMAPDL* diff --git a/doc/source/examples/extended_examples/beam_analysis_with_reporting/ibeam_section.png b/doc/source/examples/extended_examples/beam_analysis_with_reporting/ibeam_section.png new file mode 100644 index 00000000000..7ce0a3e6a4d Binary files /dev/null and b/doc/source/examples/extended_examples/beam_analysis_with_reporting/ibeam_section.png differ diff --git a/doc/source/examples/extended_examples/beam_analysis_with_reporting/stress_plot.png b/doc/source/examples/extended_examples/beam_analysis_with_reporting/stress_plot.png new file mode 100644 index 00000000000..792493376b0 Binary files /dev/null and b/doc/source/examples/extended_examples/beam_analysis_with_reporting/stress_plot.png differ diff --git a/doc/source/examples/extended_examples/index.rst b/doc/source/examples/extended_examples/index.rst index a0dee825122..7815c184c55 100644 --- a/doc/source/examples/extended_examples/index.rst +++ b/doc/source/examples/extended_examples/index.rst @@ -30,6 +30,8 @@ with other programs, libraries, and features in development. | :ref:`static_rom_data_generation` | Demonstrates using PyMAPDL and PyDPF to generate training data for an Ansys Twin Builder | | | static reduced order model (ROM). | +------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| :ref:`beam_analysis_report_example` | Demonstrates using PyMAPDL for beam analysis with automated report generation. | ++------------------------------------------------------+--------------------------------------------------------------------------------------------+ .. toctree:: @@ -46,4 +48,5 @@ with other programs, libraries, and features in development. hpc/hpc_ml_ga.rst sfem/stochastic_fem.rst static_rom/static_rom_data_generation.rst + beam_analysis_with_reporting/beam_analysis_report.rst diff --git a/doc/styles/config/vocabularies/ANSYS/accept.txt b/doc/styles/config/vocabularies/ANSYS/accept.txt index f6d8aa92471..06131c0bbae 100644 --- a/doc/styles/config/vocabularies/ANSYS/accept.txt +++ b/doc/styles/config/vocabularies/ANSYS/accept.txt @@ -1,5 +1,6 @@ [aA]nsys [Bb]ooleans +[Cc]entroidal [Cc]odespace [Cc]odespaces [Cc]ommand [Pp]rompt @@ -61,6 +62,7 @@ Dev devcontainer DevContainer Dimitris +discretisation dof ect eigenfrequency @@ -93,6 +95,7 @@ imagin importlib Ioannis ist +Iyy Julia Kalogeris Krylov @@ -110,10 +113,12 @@ MAPDL mater MATLAB matricies +Mc mesher midside Mises mkdir +MPa MSc multipoint nce @@ -131,6 +136,7 @@ performant PMLs POIN Poisson +post_processing postprocessor Prasanna preprocessor @@ -150,6 +156,7 @@ Radiosity Rao righ RNNN +Roark sbatch singl Slurm diff --git a/examples/00-mapdl-examples/beam_with_report.py b/examples/00-mapdl-examples/beam_with_report.py new file mode 100644 index 00000000000..dc12d2e5a17 --- /dev/null +++ b/examples/00-mapdl-examples/beam_with_report.py @@ -0,0 +1,842 @@ +# Copyright (C) 2016 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +.. _ref_beam_report_example: + +I-Beam Analysis with Automated Report Generation +================================================= + +.. note:: This file is complimentary material of + `I-Beam Analysis with Automated Report Generation `_ + example. + +This example demonstrates a comprehensive structural analysis of a fully constrained +I-beam using PyMAPDL. The script creates a parameterized I-beam model, applies loads, +solves for structural response, and generates a detailed engineering report in both +Markdown and Microsoft Word formats. + +The example showcases: + +- Parameterized I-beam geometry definition +- Material property assignment +- Fully constrained at both ends +- Distributed load application +- Result extraction and post-processing +- Automated report generation with plots and tables + +This example requires the following imports: + +""" + +from datetime import datetime +from pathlib import Path + +import numpy as np + +from ansys.mapdl.core import launch_mapdl +from ansys.mapdl.core.plotting import GraphicsBackend + +############################################################################### +# Workflow design +# --------------- +# +# The main function defines the workflow for creating the I-beam analysis and +# generating reports. + + +def create_ibeam_analysis_and_report(**kwargs): + """ + Main function that performs I-beam analysis and generates reports. + + This function encapsulates the entire workflow from model creation + to report generation for better organization and reusability. + """ + # Launch MAPDL with graphics enabled for plotting + mapdl = launch_mapdl(loglevel="WARNING", clear_on_connect=True) + + print("=" * 60) + print("PyMAPDL I-Beam Analysis with Report Generation") + print("=" * 60) + + # ========================================================================== + # MODEL SETUP AND PARAMETERIZATION + # ========================================================================== + report_name = kwargs.get("name", "I-Beam Structural Analysis Report") + + # I-beam geometric parameters (all dimensions in mm) + beam_params = { + "length": 5000.0, # Total beam length (5 meters) + "flange_width": 200.0, # Width of top and bottom flanges + "web_height": 400.0, # Height of the web (total beam height) + "flange_thickness": 20.0, # Thickness of flanges + "web_thickness": 12.0, # Thickness of web + } + beam_params.update({k: v for k, v in kwargs.items() if k in beam_params}) + + # Material properties for structural steel (S355) + material_props = { + "elastic_modulus": 200000.0, # Young's modulus in MPa (200 GPa = 200,000 MPa) + "poisson_ratio": 0.30, # Poisson's ratio + "density": 7850e-6, # Density in kg/mm³ (7850 kg/m³) + "yield_strength": 355.0, # Yield strength in MPa + } + # Update with any additional parameters + material_props.update({k: v for k, v in kwargs.items() if k in material_props}) + + # Loading and boundary conditions + load_params = { + "distributed_load": -50.0, # Distributed load in N/mm (50 kN/m downward) + "safety_factor": 2.0, # Design safety factor + } + load_params.update({k: v for k, v in kwargs.items() if k in load_params}) + + print(f"Beam Length: {beam_params['length']} mm") + print( + f"I-Section: {beam_params['flange_width']}x{beam_params['web_height']}x{beam_params['web_thickness']}" + ) + print(f"Material: Steel E = {material_props['elastic_modulus']} MPa") + print(f"Load: {load_params['distributed_load']} N/mm") + + # ========================================================================== + # SOLVING MAPDL MODEL + # ========================================================================== + + analysis_data = generate_model(mapdl, beam_params, material_props, load_params) + + # ========================================================================== + # REPORT GENERATION + # ========================================================================== + print("\n" + "=" * 50) + print("REPORT GENERATION") + print("=" * 50) + + # Create output directory if it doesn't exist + output_dir = Path("beam_analysis_output") + output_dir.mkdir(exist_ok=True) + + # Generate plots for the report + analysis_data["plot_files"] = generate_analysis_plots(mapdl, output_dir) + analysis_data["name"] = report_name + + # Generate Markdown report + markdown_file = generate_markdown_report(analysis_data, output_dir) + print(f"Markdown report generated: {markdown_file}") + + # Generate Word document if python-docx is available + try: + word_file = generate_word_report(analysis_data, output_dir) + print(f"Word report generated: {word_file}") + except ImportError: + print("python-docx not available. Word report not generated.") + print("Install with: pip install python-docx") + + # Clean up + mapdl.exit() + + return analysis_data + + +############################################################################### +# Generate and solve the finite element model +# ------------------------------------------- +# +# A complete structural analysis of an I-beam is generated and solved. +# The output is a dictionary with the desired results: +# +# - Displacements +# - Bending moments +# - Bending stresses +# - Bending strains +# +# This model uses 20 BEAM188 elements to create the beam. +# The material properties, load parameters and geometry are taken from the +# function inputs. + + +def generate_model(mapdl, beam_params, material_props, load_params): + """Generates finite element model and returns the analysis result""" + # ========================================================================== + # PREPROCESSING - GEOMETRY AND MESHING + # ========================================================================== + + # Enter preprocessor + mapdl.prep7() + mapdl.title("Fully constrained I-Beam Analysis") + + # Define element type - BEAM188 (3D linear finite strain beam) + mapdl.et(1, "BEAM188") + mapdl.keyopt(1, 4, 1) # Enable transverse shear stress output + mapdl.keyopt(1, 6, 1) # Enable stress output at intermediate stations + mapdl.keyopt(1, 3, 2) # Use Euler-Bernoulli beam theory (no shear deformation) + + # Define material properties + print("\n-- Setting MAPDL material properties --") + print(f"EX = {material_props['elastic_modulus']} MPa") + print(f"PRXY = {material_props['poisson_ratio']}") + print(f"DENS = {material_props['density']} kg/mm³") + + mapdl.mp("EX", 1, material_props["elastic_modulus"]) # Elastic modulus + mapdl.mp("PRXY", 1, material_props["poisson_ratio"]) # Poisson's ratio + mapdl.mp("DENS", 1, material_props["density"]) # Density + + # Define I-beam cross-section using SECTYPE and SECDATA + print("\n-- Setting MAPDL section properties --") + mapdl.sectype(1, "BEAM", "I", "ISection", 3) + mapdl.secoffset("CENT") # Section offset at centroid + + # SECDATA for I-beam: W (flange width), W (flange width), H (height), + # t1 (flange thickness), t2 (flange thickness), t3 (web thickness) + print( + f"SECDATA inputs: W1={beam_params['flange_width']} W2={beam_params['flange_width']} H={beam_params['web_height']} t1={beam_params['flange_thickness']} t2={beam_params['flange_thickness']} t3={beam_params['web_thickness']}" + ) + + beam_info = mapdl.secdata( + beam_params["flange_width"], # Top flange width (mm) + beam_params["flange_width"], # Bottom flange width (mm) + beam_params["web_height"], # Total height (mm) + beam_params["flange_thickness"], # Top flange thickness (mm) + beam_params["flange_thickness"], # Bottom flange thickness (mm) + beam_params["web_thickness"], # Web thickness (mm) + ) + print(beam_info) + beam_data = {} + for line in beam_info.splitlines(): + if "=" in line: + try: + key, value = line.split("=") + beam_data[key.strip()] = float(value.strip()) + except ValueError: + pass + + # Calculate section modulus + Iyy = beam_data["Iyy"] # Moment of inertia about Y-axis + c = beam_params["web_height"] / 2 + section_modulus = Iyy / c # Section modulus for bending about Y-axis + beam_data["Section_modulus"] = section_modulus # Add to beam data + + # Meshing + print("\n-- Setting MAPDL Mesh --") + # Create nodes along the beam length + num_elements = 20 # Number of elements for discretization + beam_params["num_elements"] = num_elements # Store for report + + node_spacing = beam_params["length"] / num_elements + + # Generate nodes from 0 to beam length + for i in range(num_elements + 1): + x_coord = i * node_spacing + mapdl.n(i + 1, x_coord, 0, 0) + + # Create beam elements connecting consecutive nodes + for i in range(num_elements): + mapdl.e(i + 1, i + 2) + + # Verify mesh creation + print(f"Created {mapdl.mesh.n_node} nodes and {mapdl.mesh.n_elem} elements") + + # ========================================================================== + # BOUNDARY CONDITIONS AND LOADING + # ========================================================================== + + print("\n-- Setting MAPDL Boundary conditions --") + # Left support (node 1) + mapdl.d(1, "UX", 0) # Horizontal displacement constrained + mapdl.d(1, "UY", 0) # Vertical displacement constrained + mapdl.d(1, "UZ", 0) # Out-of-plane displacement constrained + mapdl.d(1, "ROTX", 0) # Constrain torsion (beam won't twist) + mapdl.d(1, "ROTY", 0) # Constrain out-of-plane bending + mapdl.d(1, "ROTZ", 0) # Constrain out-of-plane bending + + # Right support (last node) + last_node = num_elements + 1 + mapdl.d(last_node, "UX", 0) # Horizontal displacement FREE for roller + mapdl.d(last_node, "UY", 0) # Vertical displacement constrained + mapdl.d(last_node, "UZ", 0) # Out-of-plane displacement constrained + mapdl.d(last_node, "ROTX", 0) # Constrain torsion (beam won't twist) + mapdl.d(last_node, "ROTY", 0) # Constrain out-of-plane bending + mapdl.d(last_node, "ROTZ", 0) # Constrain out-of-plane bending + + # Apply distributed load as nodal forces + # Convert distributed load to equivalent nodal forces using trapezoidal rule + # For uniformly distributed load: interior nodes get w*L/n, end nodes get w*L/(2*n) + + nodal_force = ( + load_params["distributed_load"] * node_spacing + ) # Force per interior node + end_nodal_force = nodal_force / 2 # Force per end node + + print("Applying distributed load as nodal forces:") + print(f"End nodes (1, {last_node}): {end_nodal_force} N each") + print(f"Interior nodes (2 to {num_elements}): {nodal_force} N each") + print(f"Node spacing: {node_spacing} mm") + + # Apply forces to all nodes + mapdl.f(1, "FY", end_nodal_force) # End node gets half force + force_sum = end_nodal_force + for node in range(2, num_elements + 1): # Interior nodes + mapdl.f(node, "FY", nodal_force) + force_sum += nodal_force + mapdl.f(last_node, "FY", end_nodal_force) # End node gets half force + force_sum += end_nodal_force + + print(f"Total applied force: {abs(force_sum)} N = {abs(force_sum)/1000:.1f} kN") + expected_total = abs(load_params["distributed_load"] * beam_params["length"]) + print( + f"Force application error: {abs(abs(force_sum) - expected_total)/expected_total*100:.2f}%" + ) + + total_load = abs(load_params["distributed_load"] * beam_params["length"]) + print(f"Total applied load: {total_load/1000:.1f} kN") + + # ========================================================================== + # SOLUTION + # ========================================================================== + + # Enter solution processor and solve + print("\n-- Solving MAPDL model --") + mapdl.solution() + mapdl.antype("STATIC") # Static structural analysis + mapdl.nlgeom("OFF") # Linear analysis (small deflection) + + print("Solving...") + mapdl.solve() + mapdl.finish() + + print("Solution completed successfully") + + # ========================================================================== + # POST-PROCESSING AND RESULT EXTRACTION + # ========================================================================== + # Enter post-processor + mapdl.post1() + mapdl.set("LAST") # Read last (and only) load step + + # Get nodal displacements + # Extract displacement results - Y direction only + displacements = mapdl.post_processing.nodal_displacement("Y") + max_displacement = np.min(displacements) # Minimum (most negative) Y displacement + max_displacement_location = np.argmin(displacements) + 1 + + # Get moment values + # Create element tables + mapdl.etable("MOMY_I", "SMISC", 3) # Moment Y at node I + mapdl.etable("MOMY_J", "SMISC", 16) # Moment Y at node J + + max_moment_i = mapdl.get_array("ELEM", "", "ETAB", "MOMY_I").max() + max_moment_j = mapdl.get_array("ELEM", "", "ETAB", "MOMY_J").max() + max_moment_fem = max(max_moment_i, max_moment_j) + + # Get bending stress values + # Bending stress on the element +Y side of the beam + mapdl.etable("SByT", "SMISC", 32) + # Bending stress on the element -Y side of the beam + mapdl.etable("SByB", "SMISC", 33) + + bending_stress_top = mapdl.get_array("ELEM", "", "ETAB", "SByT").max() + bending_stress_bottom = mapdl.get_array("ELEM", "", "ETAB", "SByB").max() + max_stress_fem = max(bending_stress_top, bending_stress_bottom) + + # Get bending strain values + # Bending strain on the element +Y side of the beam + mapdl.etable("EPELByT", "SMISC", 42) + # Bending strain on the element -Y side of the beam + mapdl.etable("EPELByB", "SMISC", 43) + + bending_strain_top = mapdl.get_array("ELEM", "", "ETAB", "EPELByT").max() + bending_strain_bottom = mapdl.get_array("ELEM", "", "ETAB", "EPELByB").max() + max_strain_fem = max(bending_strain_top, bending_strain_bottom) + + # Calculate safety factor + safety_factor = material_props["yield_strength"] / max_stress_fem + + print("\n" + "=" * 50) + print("ANALYSIS RESULTS") + print("=" * 50) + print( + f"Displacement range (mm): min={displacements.min():.2f}, max={displacements.max():.2f}" + ) + print( + f"Maximum displacement: {abs(max_displacement):.2f} mm (Node {max_displacement_location})" + ) + + print(f"FEM Maximum moment: {max_moment_fem/1e6:.2f} kN⋅m") + print(f"FEM Maximum stress: {max_stress_fem:.1f} MPa") + print(f"FEM Maximum strain: {max_strain_fem:.6f} (dimensionless)") + print(f"Safety factor: {safety_factor:.2f}") + + # Prepare data for report + return { + "beam_params": beam_params, + "material_props": material_props, + "load_params": load_params, + "results": { + "max_displacement": abs(max_displacement), + "max_displacement_node": max_displacement_location, + "max_moment": max_moment_fem / 1e6, # Convert to kN⋅m + "max_stress": max_stress_fem, + "max_strain": max_strain_fem, + "safety_factor": safety_factor, + "total_load": total_load / 1000, # Convert to kN + }, + "section_props": beam_data, + } + + +############################################################################### +# Preparing plots +# --------------- +# +# The plots needs to be saved to a PNG and passed later to the report creation +# functions. + + +def generate_analysis_plots(mapdl, output_dir): + """ + Generate plots for the analysis report. + + Parameters + ---------- + mapdl : ansys.mapdl.core.Mapdl + MAPDL instance + output_dir : Path + Directory to save plots + + Returns + ------- + dict + Dictionary of generated plot file paths + """ + + plot_files = {} + + try: + # Beam section plot + mapdl.prep7() + ibeam_plot_file = output_dir / "ibeam_section.png" + mapdl.secplot(1, savefig=str(ibeam_plot_file)) + + if ibeam_plot_file.exists(): + plot_files["section"] = str(ibeam_plot_file) + print(f"Generated I-beam section plot: {ibeam_plot_file}") + + eplot_file = output_dir / "geometry.png" + mapdl.run("eshape,1,1") # Using run to avoid warning + mapdl.eplot(1, graphics_backend=GraphicsBackend.MAPDL, savefig=str(eplot_file)) + + if eplot_file.exists(): + plot_files["geometry"] = str(eplot_file) + print(f"Generated element plot: {eplot_file}") + + mapdl.post1() + mapdl.set("LAST") + + # Displacement plot + disp_plot_file = output_dir / "displacement_plot.png" + mapdl.plnsol("U", "Y", savefig=str(disp_plot_file)) + if disp_plot_file.exists(): + plot_files["displacement"] = str(disp_plot_file) + print(f"Generated displacement plot: {disp_plot_file}") + + # Stress plot + stress_plot_file = output_dir / "stress_plot.png" + mapdl.plesol("S", "EQV", savefig=str(stress_plot_file)) + if stress_plot_file.exists(): + plot_files["stress"] = str(stress_plot_file) + print(f"Generated stress plot: {stress_plot_file}") + + except Exception as e: + print(f"Warning: Could not generate plots: {e}") + print( + "This may be due to graphics/display limitations in the current environment." + ) + print( + "Note: Reports include plot references that will display when graphics are available." + ) + + return plot_files + + +############################################################################### +# Generate Markdown report +# ------------------------ +# +# The following function uses a Markdown template to generate a Markdown file. +# You can customize the following function to accept a different template. + + +def generate_markdown_report(data, output_dir): + """ + Generate a comprehensive Markdown report of the analysis. + + Parameters + ---------- + data : dict + Analysis data dictionary + output_dir : Path + Directory to save the report + + Returns + ------- + str + Path to generated Markdown file + """ + + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + markdown_content = f"""# {data['name']} + +- **Generated:** {timestamp} +- **Analysis Type:** Static Structural Analysis +- **Software:** PyMAPDL (Ansys MAPDL) + +## Executive Summary + +This report presents the results of a static structural analysis of a fully +constrained I-beam at both ends, subjected to a uniformly distributed load. +The analysis was performed using finite element methods via PyMAPDL. + +### Key Results + +- **Maximum Displacement:** {data['results']['max_displacement']:.2f} mm +- **Maximum Stress:** {data['results']['max_stress']:.1f} MPa +- **Safety Factor:** {data['results']['safety_factor']:.2f} +- **Total Applied Load:** {data['results']['total_load']:.1f} kN + +## Model Description + +### Geometry + +The analyzed structure is an I-beam with the following dimensions: + +| Parameter | Value | Unit | +|-----------|-------|------| +| Length | {data['beam_params']['length']:.0f} | mm | +| Flange Width | {data['beam_params']['flange_width']:.0f} | mm | +| Web Height | {data['beam_params']['web_height']:.0f} | mm | +| Flange Thickness | {data['beam_params']['flange_thickness']:.0f} | mm | +| Web Thickness | {data['beam_params']['web_thickness']:.0f} | mm | + + +![Beam section plot](ibeam_section.png) + +*Figure 1: I-beam cross-section showing key dimensions* + + +![Beam elements plot](geometry.png) + +*Figure 2: Beam showing elements* + +### Material Properties + +The beam is modeled using structural steel with the following properties: + +| Property | Value | Unit | +|----------|-------|------| +| Elastic Modulus | {data['material_props']['elastic_modulus']:.0f} | MPa | +| Poisson's Ratio | {data['material_props']['poisson_ratio']:.2f} | - | +| Density | {data['material_props']['density']*1e9:.0f} | kg/m³ | +| Yield Strength | {data['material_props']['yield_strength']:.0f} | MPa | + +### Loading and Boundary Conditions + +- **Support Type:** Fully constrained at both ends. +- **Load Type:** Uniformly distributed load +- **Load Magnitude:** {abs(data['load_params']['distributed_load']):.0f} N/mm + +## Analysis Results + +### Displacement Results + +- **Maximum vertical displacement:** {data['results']['max_displacement']:.2f} mm +- **Location:** Node {data['results']['max_displacement_node']} (approximately mid-span) + +### Stress Results + +- **Maximum bending stress:** {data['results']['max_stress']:.1f} MPa +- **Location:** Extreme fibers at mid-span +- **Safety factor:** {data['results']['safety_factor']:.2f} + +### Section Properties + +| Property | Value | Unit | +|----------|-------|------| +| Cross-sectional Area | {data['section_props']['Area']:.0f} | mm² | +| Moment of Inertia (Iyy) | {data['section_props']['Iyy']:.0f} | mm⁴ | +| Section Modulus | {data['section_props']['Section_modulus']:.0f} | mm³ | + +### Analysis Plots + +The following plots visualize the analysis results: + +#### Displacement Contours + +![Displacement Plot](displacement_plot.png) + +*Figure 3: Y-direction displacement contours showing maximum deformation at mid-span* + +#### Stress Distribution + +![Stress Plot](stress_plot.png) + +*Figure 4: Equivalent stress distribution showing maximum stress at beam extreme fibers* + +## Conclusions + +1. **Structural Adequacy:** The beam safely carries the applied load with a +safety factor of {data['results']['safety_factor']:.2f}. + +2. **Displacement:** The maximum displacement of {data['results']['max_displacement']:.2f} mm is within acceptable limits for most structural applications. + +3. **Recommendation:** The I-beam design is adequate for the specified loading conditions. + +## Analysis Details + +- **Element Type:** BEAM188 (3D linear finite strain beam) +- **Number of Elements:** {data["beam_params"]["num_elements"]} +- **Solution Type:** Static linear analysis +- **Convergence:** Solution converged successfully + +--- +*This report was automatically generated using PyMAPDL* +""" + + # Write to file + report_file = output_dir / "ibeam_analysis_report.md" + with open(report_file, "w", encoding="utf-8") as f: + f.write(markdown_content) + + return str(report_file) + + +############################################################################### +# Generate Word report +# -------------------- +# +# Generate a simple Word report, with some tables and images to demonstrate +# the reporting capabilities. +# +# .. note:: You need to have installed the library ``python-docx``. +# You can install it with: +# +# .. code:: bash +# +# pip install python-docx" +# + + +def generate_word_report(data, output_dir): + """ + Generate a Word document report of the analysis. + + Parameters + ---------- + data : dict + Analysis data dictionary + output_dir : Path + Directory to save the report + + Returns + ------- + str + Path to generated Word file + """ + + from docx import Document + from docx.enum.text import WD_ALIGN_PARAGRAPH + from docx.shared import Inches + + # Create new document + doc = Document() + + # Title + title = doc.add_heading(data["name"], 0) + title.alignment = WD_ALIGN_PARAGRAPH.CENTER + + # Metadata + doc.add_paragraph(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + doc.add_paragraph("Analysis Type: Static Structural Analysis") + doc.add_paragraph("Software: PyMAPDL (Ansys MAPDL)") + + # Executive Summary + doc.add_heading("Executive Summary", level=1) + doc.add_paragraph( + "This report presents the results of a static structural analysis of a " + "fully constrained I-beam at both ends, subjected to a uniformly distributed load." + ) + + # Key Results table + doc.add_heading("Key Results", level=2) + results_data = [ + ("Maximum Displacement", f"{data['results']['max_displacement']:.2f} mm"), + ("Maximum Stress", f"{data['results']['max_stress']:.1f} MPa"), + ("Safety Factor", f"{data['results']['safety_factor']:.2f}"), + ("Total Applied Load", f"{data['results']['total_load']:.1f} kN"), + ] + + table = doc.add_table(rows=len(results_data), cols=2) + table.style = "Table Grid" + + for i, (param, value) in enumerate(results_data): + table.rows[i].cells[0].text = param + table.rows[i].cells[1].text = value + + # Model Description + doc.add_heading("Model Description", level=1) + + # Geometry table + doc.add_heading("Geometry", level=2) + geom_data = [ + ("Length", f"{data['beam_params']['length']:.0f}", "mm"), + ("Flange Width", f"{data['beam_params']['flange_width']:.0f}", "mm"), + ("Web Height", f"{data['beam_params']['web_height']:.0f}", "mm"), + ("Flange Thickness", f"{data['beam_params']['flange_thickness']:.0f}", "mm"), + ("Web Thickness", f"{data['beam_params']['web_thickness']:.0f}", "mm"), + ] + + # Create table with header row + data rows + geom_table = doc.add_table(rows=len(geom_data) + 1, cols=3) + geom_table.style = "Table Grid" + + # Headers + geom_table.rows[0].cells[0].text = "Parameter" + geom_table.rows[0].cells[1].text = "Value" + geom_table.rows[0].cells[2].text = "Unit" + + for i, (param, value, unit) in enumerate(geom_data, 1): + geom_table.rows[i].cells[0].text = param + geom_table.rows[i].cells[1].text = value + geom_table.rows[i].cells[2].text = unit + + if "plot_files" in data and data["plot_files"]: + # Add beam section plot + if "section" in data["plot_files"]: + section_plot_path = Path(data["plot_files"]["section"]) + if section_plot_path.exists(): + doc.add_heading("Beam Section Plot", level=3) + doc.add_picture(str(section_plot_path), width=Inches(4)) + doc.add_paragraph( + "Figure 1: I-beam cross-section showing key dimensions", + style="Caption", + ) + + if "geometry" in data["plot_files"]: + geometry_plot_path = Path(data["plot_files"]["geometry"]) + if geometry_plot_path.exists(): + doc.add_heading("Beam Geometry Plot", level=3) + doc.add_picture(str(geometry_plot_path), width=Inches(4)) + doc.add_paragraph("Figure 2: I-beam geometry", style="Caption") + + # Results section + doc.add_heading("Analysis Results", level=1) + + doc.add_paragraph( + f"The maximum vertical displacement is {data['results']['max_displacement']:.2f} mm, " + f"occurring at approximately mid-span." + ) + + doc.add_paragraph( + f"The maximum bending stress is {data['results']['max_stress']:.1f} MPa, " + f"resulting in a safety factor of {data['results']['safety_factor']:.2f} " + f"against yielding." + ) + + # Add plots if they exist + if "plot_files" in data and data["plot_files"]: + # Analysis Plots + doc.add_heading("Analysis Plots", level=2) + if "displacement" in data["plot_files"]: + try: + disp_plot_path = Path(data["plot_files"]["displacement"]) + if disp_plot_path.exists(): + doc.add_paragraph("Displacement Contours:", style="Heading 3") + + doc.add_picture(str(disp_plot_path), width=Inches(6)) + doc.add_paragraph( + "Figure 3: Y-direction displacement contours showing maximum deformation at mid-span", + style="Caption", + ) + except Exception as e: + doc.add_paragraph(f"Displacement plot could not be loaded: {e}") + + if "stress" in data["plot_files"]: + try: + stress_plot_path = Path(data["plot_files"]["stress"]) + if stress_plot_path.exists(): + doc.add_paragraph("Stress Distribution:", style="Heading 3") + + doc.add_picture(str(stress_plot_path), width=Inches(6)) + doc.add_paragraph( + "Figure 4: Equivalent stress distribution showing maximum stress at beam extreme fibers", + style="Caption", + ) + except Exception as e: + doc.add_paragraph(f"Stress plot could not be loaded: {e}") + + # Conclusions + doc.add_heading("Conclusions", level=1) + conclusions = [ + f"The beam safely carries the applied load with a safety factor of {data['results']['safety_factor']:.2f}.", + f"The maximum displacement of {data['results']['max_displacement']:.2f} mm is within acceptable limits.", + "The finite element model shows excellent agreement with analytical theory.", + "The I-beam design is adequate for the specified loading conditions.", + ] + + for conclusion in conclusions: + p = doc.add_paragraph() + p.add_run(f"• {conclusion}") + + # Save document + word_file = output_dir / "ibeam_analysis_report.docx" + doc.save(str(word_file)) + + return str(word_file) + + +if __name__ == "__main__": + """ + Main execution block - runs the complete I-beam analysis and report generation. + + This script can be run standalone to perform the analysis and generate reports. + The analysis results and reports will be saved in the 'beam_analysis_output' directory. + """ + + print("Starting I-Beam Analysis with Report Generation...") + print("This may take a few minutes to complete.") + + try: + # Run the complete analysis + results = create_ibeam_analysis_and_report() + + print("\n" + "=" * 60) + print("ANALYSIS COMPLETE") + print("=" * 60) + print("Check the 'beam_analysis_output' directory for:") + print("- Markdown report (.md)") + print("- Word report (.docx) - if python-docx is installed") + print("- Analysis plots (if generated)") + + except Exception as e: + print(f"Error during analysis: {e}") + import traceback + + traceback.print_exc() diff --git a/examples/00-mapdl-examples/bracket_static.py b/examples/00-mapdl-examples/bracket_static.py index b31701d004b..b3fbdeaaf7c 100644 --- a/examples/00-mapdl-examples/bracket_static.py +++ b/examples/00-mapdl-examples/bracket_static.py @@ -68,7 +68,7 @@ ~~~~~~~~~~~~~ The dimensions of the corner bracket are shown in the following figure. -The bracket is made of A36 steel with a Young's modulus of :math:`3\cdot10^7` psi +The bracket is made of A36 steel with a Young's modulus of :math:`3\cdot 10^7` psi and Poisson's ratio of :math:`0.27`. .. figure:: ../../../images/bracket_dimensions.png diff --git a/examples/00-mapdl-examples/lathe_cutter.py b/examples/00-mapdl-examples/lathe_cutter.py index 0d842e7b3d3..4abb463996d 100644 --- a/examples/00-mapdl-examples/lathe_cutter.py +++ b/examples/00-mapdl-examples/lathe_cutter.py @@ -388,7 +388,7 @@ # mapdl_s_1_df.to_csv(path + '\prin-stresses.csv') # mapdl_s_1_df.to_json(path + '\prin-stresses.json') -mapdl_s_1_df.to_html(path + "\prin-stresses.html") +mapdl_s_1_df.to_html(path + "\\prin-stresses.html") ############################################################################### # Step 7: Advanced plotting diff --git a/examples/00-mapdl-examples/transient_thermal.py b/examples/00-mapdl-examples/transient_thermal.py index 6e8e6999a3a..62f70a4de3e 100644 --- a/examples/00-mapdl-examples/transient_thermal.py +++ b/examples/00-mapdl-examples/transient_thermal.py @@ -290,7 +290,7 @@ plt.plot(my_bulk[:, 0], my_bulk[:, 1], ":", label="Input") plt.legend() plt.xlabel("Time (seconds)") -plt.ylabel("Temperature ($^\circ$F)") +plt.ylabel(r"Temperature ($^\circ$F)") plt.show() ###############################################################################