How to Implement Fatigue Simulations Using PhaseFieldX: A Complete Guide

Enable fatigue simulations in PhaseFieldX by setting fatigue=True in the Input class, selecting a degradation function like "asymptotic", and running the staggered solver which automatically updates the cumulative fatigue history at each load step.

PhaseFieldX is an open-source finite element framework for phase-field fracture modeling. This guide explains how to implement fatigue simulations using PhaseFieldX by leveraging its built-in variational phase-field fatigue model for brittle materials.

Core Components of Fatigue Simulations in PhaseFieldX

PhaseFieldX implements fatigue through three interconnected components that handle parameter storage, degradation calculation, and field updates during the staggered solution process.

The Input Class Configuration

The Input class in src/phasefieldx/Element/Phase_Field_Fracture/Input.py stores all simulation parameters, including the fatigue toggle and degradation function selection. Key fatigue-related attributes include:

  • fatigue – Boolean flag to activate the fatigue loop
  • fatigue_degradation_function – String name of the degradation model (e.g., "asymptotic")
  • fatigue_val – Material-specific critical fatigue value (\alpha_{\text{crit}})

Fatigue Degradation Functions

The file src/phasefieldx/Element/Phase_Field_Fracture/fatigue_degradation_functions.py implements the mathematical models that evaluate the evolution of the fatigue history variable (\bar{\alpha}) and return the degradation factor (f(\bar\alpha)).

Currently, the framework provides the asymptotic model, which uses the material parameter fatigue_val as a critical threshold. When (\bar\alpha) exceeds this value, the degradation factor follows (\bigl(2\alpha_{\mathrm{crit}}/(\alpha_{\bar}+\alpha_{\mathrm{crit}})\bigr)^2); otherwise it remains 1 (no degradation).

The Staggered Fracture Solver

The core integration happens in src/phasefieldx/Element/Phase_Field_Fracture/solver/solver.py. After the displacement field is updated at each load step, the solver executes the fatigue routine when Data.fatigue is True (lines 62-76):

  • Updates the degradation-type field (g(\cdot)) for the current displacement
  • Computes the incremental fatigue history (\Delta\alpha) using the difference between current and previous phase-field values
  • Accumulates the fatigue history variable (\bar\alpha)
  • Applies the selected fatigue degradation function to compute the final degradation field

Step-by-Step Implementation Guide

Follow these steps to configure and execute a fatigue simulation using PhaseFieldX.

Step 1: Configure Fatigue Parameters in Input

Import the Input class and enable fatigue with the appropriate degradation function and material constants.

from phasefieldx.Element.Phase_Field_Fracture.Input import Input

Data = Input(
    E=210.0,                     # Young's modulus [kN/mm²]

    nu=0.3,                      # Poisson's ratio

    Gc=0.0027,                   # Critical energy release rate [kN/mm]

    l=0.004,                     # Length-scale parameter [mm]

    degradation="isotropic",
    split_energy="no",
    degradation_function="quadratic",
    irreversibility="miehe",
    fatigue=True,                        # Enable fatigue

    fatigue_degradation_function="asymptotic",
    fatigue_val=0.05625,                 # Material-specific fatigue constant

    save_solution_vtu=True,
    results_folder_name="fatigue_demo"
)

Step 2: Set Up Cyclic Boundary Conditions

Fatigue simulations require cyclic loading. Define a time-dependent boundary condition function that applies a periodic load. The example in examples/Fatigue/plot_1800.py demonstrates this pattern:

import numpy as np
import petsc4py

amplitude = 0.002
f = 1/8
w = 2 * np.pi * f

def update_boundary_conditions(bcs, t):
    # Sinusoidal cyclic displacement

    val = 2/np.pi * amplitude * np.arcsin(np.sin(w * t))
    bcs[0].g.value[...] = petsc4py.PETSc.ScalarType(val)
    return 0, val, 0

Step 3: Execute the Solver

Call the solve function from phasefieldx.Element.Phase_Field_Fracture.solver.solver with the configured Data object and cyclic boundary conditions.

from phasefieldx.Element.Phase_Field_Fracture.solver.solver import solve

dt = 1.0
final_time = 8 * 200 + 1   # 200 cycles, 8 steps per cycle

solve(
    Data,
    msh,
    final_time,
    V_u,
    V_phi,
    bcs_list_u,
    bcs_list_phi,
    update_boundary_conditions,
    f=None,
    T_list_u=None,
    update_loading=None,
    ds_list=ds_list,
    dt=dt,
    path=None,
    bcs_list_u_names=["top", "bottom"],
    min_stagger_iter=2,
    max_stagger_iter=500,
    stagger_error_tol=1e-8
)

The solver automatically writes the fatigue field Fatigue to VTU files at each saved timestep.

Step 4: Post-Process Fatigue Results

Use the AllResults class to extract the cumulative fatigue history or visualize the degradation field.

from phasefieldx.PostProcessing.ReferenceResult import AllResults

results = AllResults("fatigue_demo")
cycles = results.dof_files["top.dof"]["#step"] * (1/8)
fatigue_field = results.field_files["Fatigue"]

Complete Working Example

Below is a condensed, runnable script based on examples/Fatigue/plot_1800.py that demonstrates the full workflow:

#!/usr/bin/env python3
import os
import numpy as np
import dolfinx
import mpi4py
import petsc4py
from phasefieldx.Element.Phase_Field_Fracture.Input import Input
from phasefieldx.Element.Phase_Field_Fracture.solver.solver import solve
from phasefieldx.Boundary.boundary_conditions import bc_xy, bc_y, get_ds_bound_from_marker

# 1. Load mesh

msh_file = os.path.join("mesh", "mesh.msh")
gdim = 2
mesh_comm = mpi4py.MPI.COMM_WORLD
mesh_data = dolfinx.io.gmsh.read_from_msh(msh_file, mesh_comm, 0, gdim)
msh = mesh_data.mesh
facet_markers = mesh_data.facet_tags
fdim = msh.topology.dim - 1

# 2. Boundary setup

bottom = facet_markers.find(9)
top = facet_markers.find(10)
ds_bottom = get_ds_bound_from_marker(bottom, msh, fdim)
ds_top = get_ds_bound_from_marker(top, msh, fdim)
ds_list = np.array([[ds_bottom, "bottom"], [ds_top, "top"]])

# 3. Function spaces

V_u = dolfinx.fem.functionspace(msh, ("Lagrange", 1, (msh.geometry.dim,)))
V_phi = dolfinx.fem.functionspace(msh, ("Lagrange", 1))

# 4. Boundary conditions

bc_bottom = bc_xy(bottom, V_u, fdim)
bc_top = bc_y(top, V_u, fdim)
bcs_list_u = [bc_top, bc_bottom]
bcs_list_phi = []

# 5. Cyclic load definition

amplitude = 0.002
f = 1/8
w = 2 * np.pi * f
def update_boundary_conditions(bcs, t):
    val = 2/np.pi * amplitude * np.arcsin(np.sin(w * t))
    bcs[0].g.value[...] = petsc4py.PETSc.ScalarType(val)
    return 0, val, 0

# 6. Fatigue configuration

Data = Input(
    E=210.0, nu=0.3, Gc=0.0027, l=0.004,
    degradation="isotropic", split_energy="no",
    degradation_function="quadratic", irreversibility="miehe",
    fatigue=True,
    fatigue_degradation_function="asymptotic",
    fatigue_val=0.05625,
    save_solution_vtu=True,
    results_folder_name="fatigue_demo"
)

# 7. Solve

dt = 1.0
final_time = 8 * 200 + 1
solve(
    Data, msh, final_time, V_u, V_phi,
    bcs_list_u, bcs_list_phi, update_boundary_conditions,
    f=None, T_list_u=None, update_loading=None,
    ds_list=ds_list, dt=dt, path=None,
    bcs_list_u_names=["top", "bottom"],
    min_stagger_iter=2, max_stagger_iter=500, stagger_error_tol=1e-8
)

Extending Fatigue Models

To implement a custom fatigue degradation law, extend src/phasefieldx/Element/Phase_Field_Fracture/fatigue_degradation_functions.py:

import numpy as np

def logarithmic(alpha_bar, Data):
    """
    Custom logarithmic fatigue degradation model.
    """
    alpha_crit = Data.fatigue_val
    return np.log1p(alpha_bar / alpha_crit)

# Register in the dispatcher (around line 62-66 in the source)

def fatigue_degradation(alpha_bar, Data):
    if Data.fatigue_degradation_function == "asymptotic":
        return asymptotic(alpha_bar, Data)
    elif Data.fatigue_degradation_function == "logarithmic":
        return logarithmic(alpha_bar, Data)
    else:
        raise ValueError("Unknown fatigue model")

The framework automatically dispatches to your new function based on the fatigue_degradation_function string provided in the Input object.

Summary

  • Enable fatigue by setting fatigue=True in the Input class along with fatigue_degradation_function and fatigue_val parameters.
  • Source files for fatigue logic are located in src/phasefieldx/Element/Phase_Field_Fracture/Input.py, fatigue_degradation_functions.py, and solver/solver.py.
  • Cyclic loading is required for meaningful fatigue analysis; define time-dependent boundary conditions using the update_boundary_conditions callback.
  • The solver automatically updates the cumulative fatigue history (\bar\alpha) and computes the degradation field at each load step when fatigue is enabled.
  • Post-processing uses the AllResults class to extract the Fatigue field and cumulative history from the output directory.

Frequently Asked Questions

What is the asymptotic fatigue degradation function in PhaseFieldX?

The asymptotic function is the default fatigue degradation model implemented in fatigue_degradation_functions.py. It uses the material parameter fatigue_val as a critical threshold (\alpha_{\text{crit}}). When the cumulative fatigue history (\bar\alpha) exceeds this value, the degradation factor follows (\bigl(2\alpha_{\mathrm{crit}}/(\alpha_{\bar}+\alpha_{\mathrm{crit}})\bigr)^2); otherwise it remains 1, indicating no degradation.

How does PhaseFieldX update the fatigue history variable during simulation?

Inside the staggered solver (solver.py), after each displacement field update, the code checks if Data.fatigue is True. If enabled, it computes the incremental fatigue history (\Delta\alpha) using the difference between the current and previous phase-field values, accumulates this into the cumulative history variable alpha_cum_bar, and applies the selected fatigue degradation function to update the Fatigue field for the next iteration.

Can I implement custom fatigue degradation laws in PhaseFieldX?

Yes, you can extend the fatigue functionality by adding new functions to src/phasefieldx/Element/Phase_Field_Fracture/fatigue_degradation_functions.py. Define your mathematical model as a Python function accepting alpha_bar and Data parameters, then register it in the fatigue_degradation dispatcher by adding a new conditional branch that checks for your custom function name. The framework will automatically use your implementation when you specify the corresponding string in fatigue_degradation_function.

Where are the fatigue simulation results stored in PhaseFieldX?

The fatigue simulation results are stored in the directory specified by results_folder_name in your Input object. When save_solution_vtu=True, the solver writes VTU files containing the Fatigue field (the degradation factor) and the phase-field variable at each saved timestep. You can access these results programmatically using the AllResults class from phasefieldx.PostProcessing.ReferenceResult to load the cumulative history data and field values for visualization or analysis.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →