CPFE (NF geometry + FF initial strain)#

Run a combined near-field + far-field CPFE simulation: the NF reconstruction supplies the high-resolution hex mesh and per-element orientations, while the FF reconstruction supplies the initial elastic-strain (residual-strain) field. Use this when you want to drive CPFE from an NF mesh with an FF residual-strain initial condition, optionally end to end from synthetic HEDM.

Needs: the native stack: NEPER (FF/synthetic), CUBIT/SCULPT (NF mesh), MOOSE puma-opt + neml2-compile.

Tier B: needs the full native stack

This notebook needs neper, CUBIT’s psculpt/epu, and puma-opt, 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.

Inputs#

  • An NF mesh and orientations, from NF reconstruction (NearFieldMeshBuilder): mesh.e plus a per-element MRP orientations.csv.

  • An FF per-grain elastic-strain file, from FF reconstruction (VoronoiMeshBuilder with generate_mesh=False): reconstruction_cpfe_ee.csv.

If the NF and FF data are not co-registered, shift the FF x, y, z into the NF frame first (see below). A synthetic HEDM generator can produce both stages when you do not have experimental data.

Pipeline#

  1. (optional) Generate synthetic HEDM, writing an FF CSV and NF layers.

  2. Build the NF mesh: NearFieldMeshBuilder.reconstruct then NearFieldMeshBuilder.mesh produce mesh.e and orientations.csv.

  3. Build the FF residual strain: build_voronoi(generate_mesh=False, ...) produces reconstruction_cpfe_ee.csv.

  4. Run CPFE with the NF mesh, NF orientations, and the FF strain field.

Recipe#

Shift the FF elastic-strain file into the NF frame when the two are not co-registered:

[ ]:
import pandas as pd

ff_translation = (dx, dy, dz)   # from experiment geometry
ee = pd.read_csv("out/ff_reconstruction/reconstruction_cpfe_ee.csv",
                 header=None, index_col=False)
ee.iloc[:, 0] += ff_translation[0]
ee.iloc[:, 1] += ff_translation[1]
ee.iloc[:, 2] += ff_translation[2]
ee.to_csv("out/ff_reconstruction/reconstruction_cpfe_ee_shifted.csv",
          index=False, header=False)

Run the simulation:

[ ]:
from graintrace.run_cpfe_simulation import CPFESimulation

sim = CPFESimulation(
    mesh_file="out/nf_reconstruction/mesh.e",
    save_simulation_folder="out/simulation",
    eeres_file="out/ff_reconstruction/reconstruction_cpfe_ee_shifted.csv",
    ori_file="out/nf_reconstruction/orientations.csv",
    dim=3, element_order="FIRST",           # NF meshes are typically FIRST order
    moose_run_file="external/puma/puma-opt",   # your built puma-opt
    use_ff_initial_field=False,             # ee comes from a DIFFERENT mesh
)
sim.set_parameters("material", ...)         # see the material-calibration tutorial
sim.set_parameters("simulation_parameters",
                   device="cuda:0", device_batch=1000,
                   dt=0.5, total_time=2.0, initialize_time=1.0, sync_times="2.0")
sim.set_parameters("boundary", bounding_box=nf_bbox, bc={...})
sim.set_parameters("grid_properties",
                   number_of_elements=[10, 10, 10], bounding_box=grid_bb)
sim.run(ncore=8)

Key parameters#

  • use_ff_initial_field: False when eeres_file comes from a different mesh than the geometry (FF strain on an NF mesh); True only when mesh and strain file are co-registered FF.

  • FF-to-NF shift: add (dx, dy, dz) to the first three columns of reconstruction_cpfe_ee.csv to align frames before passing it as eeres_file.

  • nf_bbox: derive from the NF merged_segmented_fixed_grid.npy coordinates (see configuration).

  • sculpt_config: configures the NF hex mesh (see configuration).

  • CPFE material, simulation, boundary, and grid parameters follow CPFE simulation.

Gotchas#

  • The same NEML2 v3 AOTI notes as CPFE simulation apply: material parameters are baked into the model and compiled on run(); there are no runtime schedulers; a device list maps to MPI ranks.

  • NF reconstruction uses multiprocessing, so the driver must sit under an if __name__ == "__main__": guard.

  • moose_run_file must point at your built v3 puma-opt.

Full example#

Full script: examples/demonstrate_cpfe_nfff.py