|
| 1 | +""" |
| 2 | +.. _ref_cyclic_analysis_example: |
| 3 | +
|
| 4 | +Cyclic Analysis |
| 5 | +--------------- |
| 6 | +
|
| 7 | +This example creates a bladed disc using parametric geometry of a cyclic sector |
| 8 | +and then runs a modal analysis on that cyclic sector. We then post-process the |
| 9 | +results using the legacy `MAPDL reader <https://readerdocs.pyansys.com/>`_ and |
| 10 | +finally generate another cyclic model using our parametric modeler. |
| 11 | +
|
| 12 | +Our first task is to create a simple cyclic model with 7 sectors. |
| 13 | +
|
| 14 | +.. image:: ../../../images/cyclic_disc.png |
| 15 | +
|
| 16 | +First, start MAPDL as a service. |
| 17 | +
|
| 18 | +""" |
| 19 | +# sphinx_gallery_thumbnail_number = 3 |
| 20 | + |
| 21 | +import numpy as np |
| 22 | +import pyvista as pv |
| 23 | + |
| 24 | +from ansys.mapdl.core import launch_mapdl |
| 25 | + |
| 26 | +mapdl = launch_mapdl() |
| 27 | + |
| 28 | + |
| 29 | +############################################################################### |
| 30 | +# Create the Cyclic Sector |
| 31 | +# ~~~~~~~~~~~~~~~~~~~~~~~~ |
| 32 | +# Create a single "sector" of our cyclic model. |
| 33 | +# |
| 34 | + |
| 35 | + |
| 36 | +def gen_sector(mapdl, sectors): |
| 37 | + """Generate a single sector within MAPDL.""" |
| 38 | + |
| 39 | + # thickness |
| 40 | + thickness = 0.003 # meters |
| 41 | + arc_end = 2 * np.pi / sectors |
| 42 | + arc_cent = arc_end / 2 |
| 43 | + |
| 44 | + # radius |
| 45 | + rad = 0.01 # M |
| 46 | + arc = pv.CircularArc( |
| 47 | + [rad, 0, 0], [np.cos(arc_end) * rad, np.sin(arc_end) * rad, 0], [0, 0, 0] |
| 48 | + ) |
| 49 | + |
| 50 | + # interior circle |
| 51 | + kp_begin = [rad, 0, 0] |
| 52 | + kp_end = [np.cos(arc_end) * rad, np.sin(arc_end) * rad, 0] |
| 53 | + kp_center = [0, 0, 0] |
| 54 | + |
| 55 | + # exterior circle in (M) |
| 56 | + out_rad = 5.2e-2 |
| 57 | + |
| 58 | + # solve for angle to get same arc.length at the end |
| 59 | + cent_ang = arc.length / out_rad / 2 |
| 60 | + |
| 61 | + # interior circle |
| 62 | + kp_beg_outer = [ |
| 63 | + np.cos(arc_cent - cent_ang) * out_rad, |
| 64 | + np.sin(arc_cent - cent_ang) * out_rad, |
| 65 | + 0, |
| 66 | + ] |
| 67 | + kp_end_outer = [ |
| 68 | + np.cos(arc_cent + cent_ang) * out_rad, |
| 69 | + np.sin(arc_cent + cent_ang) * out_rad, |
| 70 | + 0, |
| 71 | + ] |
| 72 | + |
| 73 | + mapdl.prep7() |
| 74 | + mapdl.k(0, *kp_center) |
| 75 | + mapdl.k(0, *kp_begin) |
| 76 | + mapdl.k(0, *kp_end) |
| 77 | + mapdl.k(0, *kp_beg_outer) |
| 78 | + mapdl.k(0, *kp_end_outer) |
| 79 | + |
| 80 | + # inner arc |
| 81 | + mapdl.l(1, 2) # left line |
| 82 | + mapdl.l(1, 3) # right line |
| 83 | + lnum_inter = mapdl.l(2, 3) # internal line |
| 84 | + mapdl.al("all") |
| 85 | + |
| 86 | + # outer "blade" |
| 87 | + lnum = [lnum_inter, mapdl.l(4, 5), mapdl.l(2, 4), mapdl.l(3, 5)] |
| 88 | + mapdl.al(*lnum) |
| 89 | + |
| 90 | + # extrude the model in the Z direction by ``thickness`` |
| 91 | + mapdl.vext("all", dz=thickness) |
| 92 | + |
| 93 | + |
| 94 | +# generate a single sector of a 7 sector model |
| 95 | +sectors = 7 |
| 96 | +gen_sector(mapdl, sectors) |
| 97 | + |
| 98 | +# Volume plot |
| 99 | +mapdl.vplot() |
| 100 | + |
| 101 | + |
| 102 | +############################################################################### |
| 103 | +# Make the Model Cyclic |
| 104 | +# ~~~~~~~~~~~~~~~~~~~~~ |
| 105 | +# Make the model cyclic by running :func:`Mapdl.cyclic` |
| 106 | +# |
| 107 | +# Note how the number of sectors matches |
| 108 | + |
| 109 | +output = mapdl.cyclic() |
| 110 | +print(f"Expected Sectors: {sectors}") |
| 111 | +print(output) |
| 112 | + |
| 113 | + |
| 114 | +############################################################################### |
| 115 | +# Generate the mesh |
| 116 | +# ~~~~~~~~~~~~~~~~~ |
| 117 | +# Generate the finite element mesh using quadritic hexahedrals, SOLID186. |
| 118 | + |
| 119 | +# element size of 0.01 |
| 120 | +esize = 0.001 |
| 121 | + |
| 122 | +mapdl.et(1, 186) |
| 123 | +mapdl.esize(esize) |
| 124 | +mapdl.vsweep("all") |
| 125 | + |
| 126 | +# plot the finite element mesh |
| 127 | +mapdl.eplot() |
| 128 | + |
| 129 | + |
| 130 | +############################################################################### |
| 131 | +# Apply Material Properties |
| 132 | +# ~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 133 | + |
| 134 | +# Define a material (nominal steel in SI) |
| 135 | +mapdl.mp("EX", 1, 210e9) # Elastic moduli in Pa (kg/(m*s**2)) |
| 136 | +mapdl.mp("DENS", 1, 7800) # Density in kg/m3 |
| 137 | +mapdl.mp("NUXY", 1, 0.3) # Poisson's Ratio |
| 138 | + |
| 139 | +# apply it to all elements |
| 140 | +mapdl.emodif("ALL", "MAT", 1) |
| 141 | + |
| 142 | + |
| 143 | +############################################################################### |
| 144 | +# Run the Modal Analysis |
| 145 | +# ~~~~~~~~~~~~~~~~~~~~~~ |
| 146 | +# Let's get the first 10 modes. Note that this will actually compute |
| 147 | +# ``(sectors/2)*nmode`` based on the cyclic boundary conditions. |
| 148 | + |
| 149 | +output = mapdl.modal_analysis(nmode=10, freqb=1) |
| 150 | +print(output) |
| 151 | + |
| 152 | + |
| 153 | +############################################################################### |
| 154 | +# Get the Results of the Cyclic Modal Analysis |
| 155 | +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 156 | +# Visualize a traveling wave from the modal analysis. |
| 157 | +# |
| 158 | +# For more details, see `Validation of a Modal Work Approach for Forced |
| 159 | +# Response Analysis of Bladed Disks |
| 160 | +# <https://www.mdpi.com/2076-3417/11/12/5437/pdf>`_, or the `Cyclic Symmetry |
| 161 | +# Analysis Guide |
| 162 | +# <https://ansyshelp.ansys.com/Views/Secured/corp/v222/en/pdf/Ansys_Mechanical_APDL_Cyclic_Symmetry_Analysis_Guide.pdf>`_ |
| 163 | +# |
| 164 | +# .. note:: |
| 165 | +# This uses the legacy result reader, which will be deprecated at some point |
| 166 | +# in favor of DPF, but we can use this for now for some great animations. |
| 167 | +# |
| 168 | +# For more details regarding cyclic result post processing, see: |
| 169 | +# - `Understanding Nodal Diameters from a Cyclic Model Analysis <https://readerdocs.pyansys.com/examples/01-cyclic_results/academic_sector_nd.html>`_ |
| 170 | +# - `Modal Cyclic Symmetry Example <https://dpfdocs.pyansys.com/examples/02-modal-harmonic/01-modal_cyclic.html>`_ |
| 171 | + |
| 172 | +# grab the result object from MAPDL |
| 173 | +result = mapdl.result |
| 174 | +print(result) |
| 175 | + |
| 176 | + |
| 177 | +############################################################################### |
| 178 | +# List the Table of Harmonic Indices |
| 179 | +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 180 | +# This is the table of harmonic indices. This table provides the corresponding |
| 181 | +# harmonic index for each cumulative mode. |
| 182 | +print("C. Index Harmonic Index") |
| 183 | +for i, hindex in zip(range(result.n_results), result.harmonic_indices): |
| 184 | + print(f"{i:3d} {hindex:3d}") |
| 185 | + |
| 186 | + |
| 187 | +############################################################################### |
| 188 | +# Generate an Animation of a Traveling Wave |
| 189 | +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 190 | +# Here's the nodal diameter 1 of first bend on our cyclic analysis. |
| 191 | +# |
| 192 | +# We can get the first mode (generally first bend for a bladed rotor) for nodal |
| 193 | +# diameter 1 with: |
| 194 | +# |
| 195 | +# ``mode_num = np.nonzero(result.harmonic_indices == 1)[0][0]`` |
| 196 | +# |
| 197 | + |
| 198 | +pv.global_theme.background = "w" |
| 199 | + |
| 200 | +_ = result.animate_nodal_displacement( |
| 201 | + 11, |
| 202 | + displacement_factor=5e-4, |
| 203 | + movie_filename="traveling_wave11.gif", |
| 204 | + n_frames=30, |
| 205 | + off_screen=True, |
| 206 | + loop=False, |
| 207 | + add_text=False, |
| 208 | + show_scalar_bar=False, |
| 209 | + cmap="jet", |
| 210 | +) |
| 211 | + |
| 212 | +############################################################################### |
| 213 | +# And here's 1st torsional for nodal diameter 3. |
| 214 | + |
| 215 | +_ = result.animate_nodal_displacement( |
| 216 | + 36, |
| 217 | + displacement_factor=2e-4, |
| 218 | + movie_filename="traveling_wave36.gif", |
| 219 | + n_frames=30, |
| 220 | + off_screen=True, |
| 221 | + loop=False, |
| 222 | + add_text=False, |
| 223 | + show_scalar_bar=False, |
| 224 | + cmap="jet", |
| 225 | +) |
| 226 | + |
| 227 | + |
| 228 | +############################################################################### |
| 229 | +# Parametric Geometry |
| 230 | +# ~~~~~~~~~~~~~~~~~~~ |
| 231 | +# Since our geometry creation is scripted, we can create a structure with any |
| 232 | +# number of "sectors". Let's make a more interesting one with 20 sectors. |
| 233 | +# |
| 234 | +# First, be sure to clear MAPDL so we start from scratch. |
| 235 | + |
| 236 | +mapdl.clear() |
| 237 | +mapdl.prep7() |
| 238 | + |
| 239 | +# Generate a single sector of a 20 sector model |
| 240 | +gen_sector(mapdl, 20) |
| 241 | + |
| 242 | +# make it cyclic |
| 243 | +mapdl.cyclic() |
| 244 | + |
| 245 | +# Mesh it |
| 246 | +esize = 0.001 |
| 247 | +mapdl.et(1, 186) |
| 248 | +mapdl.esize(esize) |
| 249 | +mapdl.vsweep("all") |
| 250 | + |
| 251 | +# apply materials |
| 252 | +mapdl.mp("EX", 1, 210e9) # Elastic moduli in Pa (kg/(m*s**2)) |
| 253 | +mapdl.mp("DENS", 1, 7800) # Density in kg/m3 |
| 254 | +mapdl.mp("NUXY", 1, 0.3) # Poisson's Ratio |
| 255 | +mapdl.emodif("ALL", "MAT", 1) |
| 256 | + |
| 257 | +# Run the modal analysis |
| 258 | +output = mapdl.modal_analysis(nmode=6, freqb=1) |
| 259 | + |
| 260 | +# grab the result object from MAPDL |
| 261 | +result = mapdl.result |
| 262 | +print(result) |
| 263 | + |
| 264 | + |
| 265 | +############################################################################### |
| 266 | +# List the Table of Harmonic Indices |
| 267 | +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 268 | +# Note how the harmonic indices of these modes goes up to 10, or N/2 where N is |
| 269 | +# the number of sectors. |
| 270 | +# |
| 271 | + |
| 272 | +print("C. Index Harmonic Index") |
| 273 | +for i, hindex in zip(range(result.n_results), result.harmonic_indices): |
| 274 | + print(f"{i:3d} {hindex:3d}") |
| 275 | + |
| 276 | + |
| 277 | +############################################################################### |
| 278 | +# Plot Nodal First Bend for Nodal Diameter 2 |
| 279 | +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 280 | +# Note how you can clearly see two nodal lines for this mode shape since it's |
| 281 | +# nodal diameter 2. |
| 282 | + |
| 283 | +result.plot_nodal_displacement( |
| 284 | + 12, cpos="xy", cmap="jet", show_scalar_bar=False, add_text=False |
| 285 | +) |
| 286 | + |
| 287 | + |
| 288 | +############################################################################### |
| 289 | +# Animate Nodal First Bend for Nodal Diameter 2 |
| 290 | +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 291 | +# Let's end this example by animating mode 12, which corresponds to first bend |
| 292 | +# 2nd nodal diameter for this example. |
| 293 | + |
| 294 | +_ = result.animate_nodal_displacement( |
| 295 | + 12, |
| 296 | + displacement_factor=2e-4, |
| 297 | + movie_filename="traveling_wave12.gif", |
| 298 | + n_frames=30, |
| 299 | + off_screen=True, |
| 300 | + loop=False, |
| 301 | + add_text=False, |
| 302 | + show_scalar_bar=False, |
| 303 | + cmap="jet", |
| 304 | +) |
0 commit comments