{"spec_id":"line-stress-strain","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nline-stress-strain: Engineering Stress-Strain Curve\nLibrary: plotnine 0.15.7 | Python 3.13.14\nQuality: 83/100 | Updated: 2026-06-21\n\"\"\"\n\nimport os\nimport sys\n\nimport numpy as np\nimport pandas as pd\n\n\n# Work around naming conflict between plotnine.py script and plotnine package\nscript_dir = os.path.dirname(os.path.abspath(__file__))\nif script_dir in sys.path:\n    sys.path.remove(script_dir)\nif \"\" in sys.path:\n    sys.path.remove(\"\")\nif \".\" in sys.path:\n    sys.path.remove(\".\")\n\nfrom plotnine import (\n    aes,\n    annotate,\n    coord_cartesian,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_line,\n    geom_point,\n    geom_segment,\n    geom_text,\n    ggplot,\n    labs,\n    scale_color_identity,\n    scale_size_identity,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nREGION_ALPHA = 0.18 if THEME == \"light\" else 0.13\n\n# Imprint palette — position 1 is always the first categorical series\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\nnp.random.seed(42)\n\nyoungs_modulus = 210000  # MPa\nyield_stress = 250  # MPa\nuts = 400  # MPa\nfracture_strain = 0.35\nnecking_strain = 0.22\n\n# Elastic region\nelastic_strain = np.linspace(0, yield_stress / youngs_modulus, 40)\nelastic_stress = youngs_modulus * elastic_strain\n\n# Yield plateau (part of the plastic region — not a separate band per spec)\nplateau_strain = np.linspace(elastic_strain[-1], 0.025, 15)\nplateau_stress = np.full_like(plateau_strain, yield_stress)\n\n# Strain hardening (power law)\nhardening_strain = np.linspace(0.025, necking_strain, 80)\nhardening_stress = yield_stress + (uts - yield_stress) * ((hardening_strain - 0.025) / (necking_strain - 0.025)) ** 0.45\n\n# Necking to fracture\nnecking_strain_vals = np.linspace(necking_strain, fracture_strain, 40)\nnecking_stress = (\n    uts - (uts - 320) * ((necking_strain_vals - necking_strain) / (fracture_strain - necking_strain)) ** 1.3\n)\n\nstrain = np.concatenate([elastic_strain, plateau_strain[1:], hardening_strain[1:], necking_strain_vals[1:]])\nstress = np.concatenate([elastic_stress, plateau_stress[1:], hardening_stress[1:], necking_stress[1:]])\ndf = pd.DataFrame({\"strain\": strain, \"stress\": stress})\n\n# 0.2% offset line\noffset = 0.002\nelastic_end = yield_stress / youngs_modulus  # ~0.00119\noffset_strain_end = (yield_stress + 50) / youngs_modulus + offset\n\n# Critical points\nyield_point_strain = elastic_end + offset\nyield_point_stress = yield_stress\nfracture_stress_pt = float(necking_stress[-1])\n\ndf_points = pd.DataFrame(\n    {\n        \"strain\": [yield_point_strain, necking_strain, fracture_strain],\n        \"stress\": [yield_point_stress, uts, fracture_stress_pt],\n        \"color\": [IMPRINT[4]] * 3,\n        \"size\": [3.0, 3.0, 3.0],\n    }\n)\n\n# Region labels: 3 regions per spec (elastic, strain hardening, necking)\n# Yield plateau is a critical point, not a separate shaded band\ndf_regions = pd.DataFrame(\n    {\n        \"strain\": [elastic_end / 2, 0.13, 0.29],\n        \"stress\": [350, 335, 385],\n        \"label\": [\"Elastic\", \"Strain\\nHardening\", \"Necking\"],\n    }\n)\n\nplot = (\n    ggplot()\n    # Three region shadings: elastic, plastic (strain hardening), necking\n    + annotate(\"rect\", xmin=0, xmax=elastic_end, ymin=0, ymax=460, alpha=REGION_ALPHA, fill=IMPRINT[2])\n    + annotate(\"rect\", xmin=elastic_end, xmax=necking_strain, ymin=0, ymax=460, alpha=REGION_ALPHA, fill=IMPRINT[3])\n    + annotate(\"rect\", xmin=necking_strain, xmax=fracture_strain, ymin=0, ymax=460, alpha=REGION_ALPHA, fill=IMPRINT[4])\n    # Main stress-strain curve (Imprint position 1 — first categorical series)\n    + geom_line(df, aes(x=\"strain\", y=\"stress\"), color=IMPRINT[0], size=1.0)\n    # 0.2% offset construction line\n    + geom_segment(\n        aes(x=offset, xend=offset_strain_end, y=0, yend=yield_stress + 50),\n        color=IMPRINT[4],\n        size=0.6,\n        linetype=\"dashed\",\n    )\n    + annotate(\"text\", x=0.011, y=52, label=\"0.2% offset\", size=3.5, color=INK_SOFT, fontstyle=\"italic\")\n    # Critical point markers\n    + geom_point(df_points, aes(x=\"strain\", y=\"stress\", color=\"color\", size=\"size\"))\n    + scale_color_identity()\n    + scale_size_identity()\n    # Critical point labels — individual positions for clarity near y-axis\n    + annotate(\n        \"text\",\n        x=yield_point_strain + 0.018,\n        y=yield_point_stress + 22,\n        label=\"Yield Point\\n(0.2% offset)\",\n        size=3.0,\n        color=INK,\n        fontweight=\"bold\",\n    )\n    + annotate(\"text\", x=necking_strain, y=uts + 28, label=\"UTS\", size=3.0, color=INK, fontweight=\"bold\")\n    + annotate(\n        \"text\",\n        x=fracture_strain - 0.012,\n        y=fracture_stress_pt + 28,\n        label=\"Fracture\",\n        size=3.0,\n        color=INK,\n        fontweight=\"bold\",\n    )\n    # Region labels\n    + geom_text(df_regions, aes(x=\"strain\", y=\"stress\", label=\"label\"), size=3.0, color=INK_SOFT, fontstyle=\"italic\")\n    # Elastic modulus annotation\n    + annotate(\n        \"text\", x=0.028, y=145, label=f\"E = {youngs_modulus // 1000} GPa\", size=3.5, color=IMPRINT[2], fontweight=\"bold\"\n    )\n    + labs(\n        x=\"Engineering Strain\",\n        y=\"Engineering Stress (MPa)\",\n        title=\"line-stress-strain · python · plotnine · anyplot.ai\",\n    )\n    + scale_x_continuous(breaks=np.arange(0, 0.40, 0.05))\n    + scale_y_continuous(breaks=np.arange(0, 500, 50))\n    + coord_cartesian(xlim=(0, 0.38), ylim=(0, 460))\n    + theme_minimal()\n    + theme(\n        figure_size=(8, 4.5),\n        text=element_text(size=7),\n        plot_title=element_text(size=12, weight=\"bold\", color=INK),\n        axis_title=element_text(size=10, color=INK, weight=\"bold\"),\n        axis_text=element_text(size=8, color=INK_SOFT),\n        panel_grid_major=element_line(color=INK, size=0.3, alpha=0.15),\n        panel_grid_major_x=element_blank(),\n        panel_grid_minor=element_blank(),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        panel_border=element_rect(color=INK_SOFT, fill=None),\n        axis_line=element_line(color=INK_SOFT),\n    )\n)\n\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=8, height=4.5, units=\"in\", verbose=False)\n"}