# How to Implement Fatigue Simulations Using PhaseFieldX: A Complete Guide

> Master fatigue simulations in PhaseFieldX with our comprehensive guide. Learn to set fatigue to True, choose degradation functions, and optimize the solver for accurate results. Start your fatigue analysis today.

- Repository: [Miguel Castillón/phasefieldx](https://github.com/castillonmiguel/phasefieldx)
- Tags: how-to-guide
- Published: 2026-02-27

---

**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`](https://github.com/castillonmiguel/phasefieldx/blob/main/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`](https://github.com/castillonmiguel/phasefieldx/blob/main/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`](https://github.com/castillonmiguel/phasefieldx/blob/main/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.

```python
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`](https://github.com/castillonmiguel/phasefieldx/blob/main/examples/Fatigue/plot_1800.py) demonstrates this pattern:

```python
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.

```python
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.

```python
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`](https://github.com/castillonmiguel/phasefieldx/blob/main/examples/Fatigue/plot_1800.py) that demonstrates the full workflow:

```python
#!/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`](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Phase_Field_Fracture/fatigue_degradation_functions.py):

```python
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`](https://github.com/castillonmiguel/phasefieldx/blob/main/src/phasefieldx/Element/Phase_Field_Fracture/Input.py), [`fatigue_degradation_functions.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/fatigue_degradation_functions.py), and [`solver/solver.py`](https://github.com/castillonmiguel/phasefieldx/blob/main/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`](https://github.com/castillonmiguel/phasefieldx/blob/main/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`](https://github.com/castillonmiguel/phasefieldx/blob/main/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`](https://github.com/castillonmiguel/phasefieldx/blob/main/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.