{ "cells": [ { "cell_type": "markdown", "id": "nf-intro", "metadata": {}, "source": [ "# NF mesh reconstruction\n", "\n", "Reconstruct a high-resolution 3D mesh from near-field (NF) HEDM `.mic` layers via voxel segmentation and CUBIT/SCULPT hex meshing, using `NearFieldMeshBuilder`. Use this to produce an Exodus `mesh.e` and per-element MRP orientations from NF data to drive CPFE, typically paired with an FF residual-strain initial condition.\n", "\n", "**Needs:** CUBIT/SCULPT (via `sculpt_config`). The driver must run under an `if __name__ == \"__main__\"` guard: NF reconstruction uses `multiprocess.Pool`.\n", "\n", "
\n", "

Tier B: needs CUBIT/SCULPT

\n", "

This notebook needs CUBIT's psculpt and epu, 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 — 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": "nf-inputs", "metadata": {}, "source": [ "## Inputs\n", "\n", "A folder of per-layer `.mic` files (tab-delimited, `%`-prefixed headers). If the source is `.ang` files (8-column, no header), convert them to `.mic` first: **graintrace has no `.ang` reader**, so that conversion happens outside the package. The `exp_file_token` argument is the filename prefix used to find the layers. See [concepts](../concepts.rst) for the `.mic` format. For the synthetic path, `SyntheticHEDMGenerator` writes the NF folder (see `examples/demonstrate_cpfe_nfff.py`)." ] }, { "cell_type": "markdown", "id": "nf-recipe-md", "metadata": {}, "source": [ "## Recipe" ] }, { "cell_type": "code", "execution_count": null, "id": "nf-recipe-code", "metadata": {}, "outputs": [], "source": [ "from graintrace.construct_nf_mesh import NearFieldMeshBuilder\n", "import numpy as np\n", "\n", "def main():\n", " builder_nf = NearFieldMeshBuilder(\n", " input_folder=\"experiment_data/NF\", save_dir=\"out/NF\",\n", " exp_file_token=\"layer\", angle_convention=\"bunge\", angle_type=\"radians\",\n", " symmetry=\"432\", prefix=\"reconstructed\", write_intermediate=True, write_vtk=True,\n", " )\n", " merged_grid = builder_nf.reconstruct(\n", " dz=5.0, nx=200, ny=300,\n", " segmentation={ # flat dict (radians) for NearFieldMeshBuilder\n", " \"misorientation_tol\": 5.0 / 180 * np.pi, \"connectivity\": 6,\n", " \"batch_norm\": 200_000, \"grain_threshold\": 1000, \"stop_count\": 500,\n", " \"grain_threshold_final\": 10000,\n", " },\n", " )\n", " mesh_path = builder_nf.mesh(sculpt_config=sculpt_config, sculpt_options=sculpt_options,\n", " merged_grid=merged_grid)\n", " # per-element MRP orientations -> builder_nf.mapped_orientations_path + \".csv\"\n", "\n", "if __name__ == \"__main__\":\n", " main()" ] }, { "cell_type": "markdown", "id": "nf-key-params", "metadata": {}, "source": [ "## Key parameters\n", "\n", "- `dz`: layer thickness in micrometers, must match the data; `nx`, `ny` set the in-plane grid resolution.\n", "- `segmentation`: a flat dict (no `method`/`params` nesting) with `misorientation_tol` in radians and `connectivity` of 6 or 26.\n", "- `sculpt_config`: required keys `psculpt`, `epu`, `nprocs`, plus `launcher` and `environment` for MPI. `sculpt_options` is a tuple of CLI flags, e.g. `(\"--adapt\", \"-S\", \"2\", \"-CS\", \"4\", \"--void_mat\", \"0\")`. See [configuration](../configuration.rst) and [meshing](meshing.ipynb)." ] }, { "cell_type": "markdown", "id": "nf-outputs", "metadata": {}, "source": [ "## Outputs (in `save_dir`)\n", "\n", "- `merged_segmented_fixed_grid.npy`: the segmented voxel grid, also a restart checkpoint.\n", "- `mesh.e`: the Exodus mesh for CPFE.\n", "- `orientations.csv`: per-element neml2 MRP orientations; use as the CPFE `ori_file`." ] }, { "cell_type": "markdown", "id": "nf-gotchas", "metadata": {}, "source": [ "## Gotchas\n", "\n", "- Restart: if segmentation already ran, load `merged_segmented_fixed_grid.npy` directly and pass it as `merged_grid` to `mesh(...)` instead of re-running `reconstruct(...)`.\n", "- Derive the NF bounding box from the saved grid coordinates for the CPFE boundary conditions; see [pitfalls](../pitfalls.rst)." ] }, { "cell_type": "markdown", "id": "nf-full-example", "metadata": {}, "source": [ "## Full example\n", "\n", "**Full script:** [examples/demonstrate_cpfe_nfff.py](https://github.com/applied-material-modeling/graintrace/blob/main/examples/demonstrate_cpfe_nfff.py)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python" }, "nbsphinx": { "execute": "never" } }, "nbformat": 4, "nbformat_minor": 5 }