{ "cells": [ { "cell_type": "markdown", "id": "mc-intro", "metadata": {}, "source": [ "# Material calibration\n", "\n", "Calibrate six crystal-plasticity parameters (elastic E, nu, G, slip strength, Voce hardening slope and saturation) to a macroscopic stress-strain curve plus full-field elastic strains. Fitting uses a neml2 v3 + pyzag analytic-adjoint Taylor model driven by LBFGS, through `MaterialCalibration` and `TaylorModel`. Run this to fit material parameters before CPFE.\n", "\n", "**Needs:** NEML2 v3 only (no MOOSE); CUDA optional. See [installation](../install.rst).\n", "\n", "
\n", "

Tier B: needs NEML2 v3 + pyzag

\n", "

This notebook needs NEML2 v3 with pyzag, which is not available in CI or Google Colab, so it is not executed when these docs are built. It ships as an annotated code listing with no stored output \u2014 nothing on this page is a hand-written or fabricated result. Run it in a configured environment (see the installation guide) to produce the figures, and run it from the repository root so the mwe_data/ paths resolve.

\n", "
" ] }, { "cell_type": "markdown", "id": "mc-inputs", "metadata": {}, "source": "## Inputs\n\nA folder of per-stress-level CSVs with the orientation matrix `O11..O33`, `eKen11..eKen33`, and `GrainRadius`, plus a `strain-stress.csv` (two columns: strain, stress). The repo ships `mwe_data/ff_calibration/` (9 load steps of 500 grains). For a physically registered fit, first run [experiment rotation](experiment-rotation.ipynb)." }, { "cell_type": "markdown", "id": "mc-recipe-md", "metadata": {}, "source": "## Recipe" }, { "cell_type": "code", "id": "mc-recipe-code", "metadata": {}, "execution_count": null, "outputs": [], "source": "from pathlib import Path\nimport graintrace as _gt\nfrom graintrace.material_calibration import MaterialCalibration\nfrom graintrace.taylor import TaylorModel\n\n_cpfe_base = str(Path(_gt.__file__).parent / \"cpfe_base\")\n\ncalib = MaterialCalibration(\n model_class=TaylorModel,\n model_args=dict(\n neml2_path=_cpfe_base + \"/neml2_cpfe_calibration.i\",\n npoints=30, nchunk=2, device=\"cuda\", compile=False,\n ),\n data_args=dict(\n data_dir=\"mwe_data/ff_calibration\",\n strain_stress_file=\"mwe_data/ff_calibration/strain-stress.csv\",\n npoints=30, full_field_strain_units=\"microstrain\", straintype=\"eKen\",\n max_strain=0.006, n_grains=100, seed=42,\n ),\n save_dir=\"out/material_calibration\",\n apply_elastic_correction=False,\n strain_window=(0.0, 0.0015),\n)\ncalib.plot_texture(direction=[1, 1, 1])\ncalib.plot_stress_strain()\ncalib.calibrate(maxiter=15, lr=0.3, max_iter_per_step=6,\n line_search_fn=\"strong_wolfe\",\n plateau_rtol=1e-3, plateau_window=2) # guard stops early\ncalib.load(\"out/material_calibration/calibrated_material.json\")\ncalib.plot_stress_strain(include_model=True)\ncalib.plot_strain_histogram(include_initial_strain=True)" }, { "cell_type": "markdown", "id": "mc-key-params", "metadata": {}, "source": "## Key parameters\n\n- model: `device` (`\"cuda\"` or `\"cpu\"`), `npoints` (pyzag time steps), `nchunk` (chunk size for the bidiagonal-in-time solve).\n- data: `n_grains` (subsample per load step; `None` for all), `max_strain` (macro-curve cap), `straintype` (`\"eKen\"` or `\"eFab\"`).\n- `calibrate`: `maxiter` is an upper bound; the plateau guard (`plateau_rtol`, `plateau_window`) stops early when the relative loss improvement stalls." }, { "cell_type": "markdown", "id": "mc-outputs", "metadata": {}, "source": "## Outputs (in `save_dir`)\n\n- `calibrated_material.json` and `autosave_material.json`.\n- Pole figures, stress-strain overlays, and elastic-strain histograms.\n\nMap the `TaylorModel.opt_vars` to CPFE material names to feed the `material` dict in [CPFE simulation](cpfe-simulation.ipynb): `elastic_tensor_E` to `elastic_E`, `elastic_tensor_G` to `elastic_G`, `elastic_tensor_nu` to `elastic_nu`, `slip_strength_constant_strength` to `slip_constant_strength`, `voce_hardening_initial_slope`, and `voce_hardening_saturated_hardening` to `voce_hardening_saturation`." }, { "cell_type": "markdown", "id": "mc-gotchas", "metadata": {}, "source": "## Gotchas\n\n- CUDA works because `taylor.py` moves the whole nonlinear system with `nsys.to(device)`. A cuda/cpu mismatch surfaces as a silent `loss=inf` and means the model stayed on CPU; do not reintroduce a `torch.set_default_device` hack.\n- v3 mixed-control uses an unweighted grain mean. Some parameters can hit reparametrization bounds on small demo configs (few grains, narrow window); use more grains and a wider window for production fits." }, { "cell_type": "markdown", "id": "mc-full-example", "metadata": {}, "source": "## Full example\n\n**Full script:** [examples/demonstrate_material_calibration.py](https://github.com/applied-material-modeling/graintrace/blob/main/examples/demonstrate_material_calibration.py)" } ], "metadata": { "nbsphinx": { "execute": "never" }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }