Tools#

class fluxfem.tools.NullTimer#

Bases: BaseTimer

section(name: str) Iterator[None]#
class fluxfem.tools.SectionTimer(clock: Callable[[], float] | None = None, hierarchical: bool = False, sep: str = '>')#

Bases: BaseTimer

Lightweight helper to measure how long named sections take.

Examples

>>> timer = SectionTimer()
>>> with timer.section("assemble"):
...     ...
>>> with timer.section("solve"):
...     ...
>>> timer.report()

To track nested calls, enable hierarchical so section names are recorded with their call stack (e.g., outer>inner):

timer = SectionTimer(hierarchical=True)
with timer.section("outer"):
    with timer.section("inner"):
        ...
add(name: str, duration: float) None#
last(name: str, default: float | None = None) float#

Return the most recent duration recorded for a section.

plot(ax: Any | None = None, sort_by: str = 'total', value: str = 'total', descending: bool = True, color: str = 'C0', format_nested: bool | None = None, stacked_nested: bool = False, kind: str = 'pie', moving_average: bool = False, use_self_time: bool = False, **kwargs) tuple[Any, Any]#

Plot timing results choosing between pie (default) or bar chart.

Parameters:
  • kind ({"pie", "bar"}) – Chart type. "pie" uses plot_pie() and is the default, "bar" uses plot_bar().

  • parameters (Other) – Passed through to the selected plotting function.

plot_bar(ax: Any | None = None, sort_by: str = 'total', value: str = 'total', descending: bool = True, color: str = 'C0', format_nested: bool | None = None, stacked_nested: bool = False, moving_average: bool = False, use_self_time: bool = False) tuple[Any, Any]#

Plot timing results as a horizontal bar chart without relying on pyplot state.

Parameters:
  • ax (matplotlib.axes.Axes, optional) – Target axes. If omitted, a new Figure/Axes is created.

  • sort_by ({"total", "avg", "max", "count", "name"}) – Sorting key used before plotting.

  • value ({"total", "avg", "max", "count"}) – Metric plotted on the x-axis.

  • descending (bool) – Sort order for sort_by.

  • color (str) – Bar color passed to Matplotlib.

  • format_nested (bool, optional) – If True and the timer is hierarchical, indent nested section labels using sep for readability. Defaults to hierarchical flag used at construction time (ignored when stacked_nested is True).

  • stacked_nested (bool) – If True and hierarchical data are present, render a stacked bar for every section that has children: self-time (parent minus sum(children)) plus one segment per direct child. Sections without children are drawn as regular bars alongside the stacked groups.

  • moving_average (bool) – If True, plot exponential-free running averages using the incremental mean update (no full history stored).

Returns:

Figure/Axes containing the plot. If ax was provided, its parent figure is returned.

Return type:

(matplotlib.figure.Figure, matplotlib.axes.Axes)

plot_pie(ax: Any | None = None, sort_by: str = 'total', value: str = 'total', descending: bool = True, colors: List[str] | None = None, autopct: str = '%.1f%%', label_threshold: float = 0.05, min_pct_to_label: float = 1.0, show_legend: bool = True, legend_kwargs: dict | None = None, show_total: bool = True, moving_average: bool = False, use_self_time: bool = False) tuple[Any, Any]#

Plot timing results as a pie chart to show relative time share.

Parameters:
  • ax (matplotlib.axes.Axes, optional) – Target axes. If omitted, a new Figure/Axes is created.

  • sort_by ({"total", "avg", "max", "count", "name"}) – Sorting key used before plotting.

  • value ({"total", "avg", "max", "count"}) – Metric used to size the wedges.

  • descending (bool) – Sort order for sort_by.

  • colors (list of str, optional) – Colors passed to Matplotlib pie.

  • autopct (str) – autopct string passed to Matplotlib pie.

  • label_threshold (float) – Minimum fraction (0-1) required to draw a text label on the wedge. Smaller slices omit the label to reduce clutter.

  • min_pct_to_label (float) – Minimum percent value to render autopct text. Use None to always show.

  • show_legend (bool) – If True, draw a legend with all section names.

  • legend_kwargs (dict, optional) – Extra kwargs forwarded to Axes.legend when show_legend is True.

  • show_total (bool) – If True, append total runtime text to the title.

  • moving_average (bool) – If True, plot exponential-free running averages using the incremental mean update (no full history stored).

Returns:

Figure/Axes containing the plot. If ax was provided, its parent figure is returned.

Return type:

(matplotlib.figure.Figure, matplotlib.axes.Axes)

report(sort_by: str = 'total', descending: bool = True, logger_instance: Logger | None = None) str#
reset(name: str | None = None) None#
save_plot(filepath: str, sort_by: str = 'total', value: str = 'total', descending: bool = True, color: str = 'C0', dpi: int = 150, format_nested: bool | None = None, stacked_nested: bool = False, kind: str = 'pie', use_self_time: bool = False, moving_average: bool = False, **kwargs) None#

Render and save the timing plot to filepath.

This helper builds its own Figure/Axes (no pyplot state), so it can be used safely inside loops.

section(name: str) Iterator[None]#
stats() List[SectionStats]#
summary(sort_by: str = 'total', descending: bool = True) List[SectionStats]#
summary_self_time(sort_by: str = 'total', descending: bool = True) List[SectionStats]#
wrap(name: str) Callable[[Callable[[...], Any]], Callable[[...], Any]]#

Decorator form of section().

This is convenient for quickly instrumenting functions without rewriting call sites:

@timer.wrap("my_step")
def my_step(...):
    ...

JIT Helpers#

fluxfem.tools.jit.make_jitted_jacobian(space: FESpaceClosure, res_form: Callable[[jax.tree_util.register_pytree_node_class, jax.numpy.ndarray, P], jax.numpy.ndarray], params: P) Callable[[Array], Any]#

Create a jitted Jacobian assembler: u -> J(u). params and space are closed over.

fluxfem.tools.jit.make_jitted_residual(space: FESpaceClosure, res_form: Callable[[jax.tree_util.register_pytree_node_class, jax.numpy.ndarray, P], jax.numpy.ndarray], params: P, *, sparse: bool = False) Callable[[Array], jax.numpy.ndarray | tuple[jax.numpy.ndarray, jax.numpy.ndarray, int]]#

Create a jitted residual assembler: u -> R(u). params and space are closed over.

Timer#

class fluxfem.tools.timer.BaseTimer#

Bases: ABC

abstractmethod section(name: str) AbstractContextManager[None]#
class fluxfem.tools.timer.NullTimer#

Bases: BaseTimer

section(name: str) Iterator[None]#
class fluxfem.tools.timer.SectionStats(name: str, count: int, total: float, avg: float, max: float)#

Bases: object

avg: float#
count: int#
max: float#
name: str#
total: float#
class fluxfem.tools.timer.SectionTimer(clock: Callable[[], float] | None = None, hierarchical: bool = False, sep: str = '>')#

Bases: BaseTimer

Lightweight helper to measure how long named sections take.

Examples

>>> timer = SectionTimer()
>>> with timer.section("assemble"):
...     ...
>>> with timer.section("solve"):
...     ...
>>> timer.report()

To track nested calls, enable hierarchical so section names are recorded with their call stack (e.g., outer>inner):

timer = SectionTimer(hierarchical=True)
with timer.section("outer"):
    with timer.section("inner"):
        ...
add(name: str, duration: float) None#
last(name: str, default: float | None = None) float#

Return the most recent duration recorded for a section.

plot(ax: Any | None = None, sort_by: str = 'total', value: str = 'total', descending: bool = True, color: str = 'C0', format_nested: bool | None = None, stacked_nested: bool = False, kind: str = 'pie', moving_average: bool = False, use_self_time: bool = False, **kwargs) tuple[Any, Any]#

Plot timing results choosing between pie (default) or bar chart.

Parameters:
  • kind ({"pie", "bar"}) – Chart type. "pie" uses plot_pie() and is the default, "bar" uses plot_bar().

  • parameters (Other) – Passed through to the selected plotting function.

plot_bar(ax: Any | None = None, sort_by: str = 'total', value: str = 'total', descending: bool = True, color: str = 'C0', format_nested: bool | None = None, stacked_nested: bool = False, moving_average: bool = False, use_self_time: bool = False) tuple[Any, Any]#

Plot timing results as a horizontal bar chart without relying on pyplot state.

Parameters:
  • ax (matplotlib.axes.Axes, optional) – Target axes. If omitted, a new Figure/Axes is created.

  • sort_by ({"total", "avg", "max", "count", "name"}) – Sorting key used before plotting.

  • value ({"total", "avg", "max", "count"}) – Metric plotted on the x-axis.

  • descending (bool) – Sort order for sort_by.

  • color (str) – Bar color passed to Matplotlib.

  • format_nested (bool, optional) – If True and the timer is hierarchical, indent nested section labels using sep for readability. Defaults to hierarchical flag used at construction time (ignored when stacked_nested is True).

  • stacked_nested (bool) – If True and hierarchical data are present, render a stacked bar for every section that has children: self-time (parent minus sum(children)) plus one segment per direct child. Sections without children are drawn as regular bars alongside the stacked groups.

  • moving_average (bool) – If True, plot exponential-free running averages using the incremental mean update (no full history stored).

Returns:

Figure/Axes containing the plot. If ax was provided, its parent figure is returned.

Return type:

(matplotlib.figure.Figure, matplotlib.axes.Axes)

plot_pie(ax: Any | None = None, sort_by: str = 'total', value: str = 'total', descending: bool = True, colors: List[str] | None = None, autopct: str = '%.1f%%', label_threshold: float = 0.05, min_pct_to_label: float = 1.0, show_legend: bool = True, legend_kwargs: dict | None = None, show_total: bool = True, moving_average: bool = False, use_self_time: bool = False) tuple[Any, Any]#

Plot timing results as a pie chart to show relative time share.

Parameters:
  • ax (matplotlib.axes.Axes, optional) – Target axes. If omitted, a new Figure/Axes is created.

  • sort_by ({"total", "avg", "max", "count", "name"}) – Sorting key used before plotting.

  • value ({"total", "avg", "max", "count"}) – Metric used to size the wedges.

  • descending (bool) – Sort order for sort_by.

  • colors (list of str, optional) – Colors passed to Matplotlib pie.

  • autopct (str) – autopct string passed to Matplotlib pie.

  • label_threshold (float) – Minimum fraction (0-1) required to draw a text label on the wedge. Smaller slices omit the label to reduce clutter.

  • min_pct_to_label (float) – Minimum percent value to render autopct text. Use None to always show.

  • show_legend (bool) – If True, draw a legend with all section names.

  • legend_kwargs (dict, optional) – Extra kwargs forwarded to Axes.legend when show_legend is True.

  • show_total (bool) – If True, append total runtime text to the title.

  • moving_average (bool) – If True, plot exponential-free running averages using the incremental mean update (no full history stored).

Returns:

Figure/Axes containing the plot. If ax was provided, its parent figure is returned.

Return type:

(matplotlib.figure.Figure, matplotlib.axes.Axes)

report(sort_by: str = 'total', descending: bool = True, logger_instance: Logger | None = None) str#
reset(name: str | None = None) None#
save_plot(filepath: str, sort_by: str = 'total', value: str = 'total', descending: bool = True, color: str = 'C0', dpi: int = 150, format_nested: bool | None = None, stacked_nested: bool = False, kind: str = 'pie', use_self_time: bool = False, moving_average: bool = False, **kwargs) None#

Render and save the timing plot to filepath.

This helper builds its own Figure/Axes (no pyplot state), so it can be used safely inside loops.

section(name: str) Iterator[None]#
stats() List[SectionStats]#
summary(sort_by: str = 'total', descending: bool = True) List[SectionStats]#
summary_self_time(sort_by: str = 'total', descending: bool = True) List[SectionStats]#
wrap(name: str) Callable[[Callable[[...], Any]], Callable[[...], Any]]#

Decorator form of section().

This is convenient for quickly instrumenting functions without rewriting call sites:

@timer.wrap("my_step")
def my_step(...):
    ...

Visualizer#

fluxfem.tools.visualizer.write_displacement_vtu(mesh: BaseMeshClosure, u: ndarray, filepath: str, *, name: str = 'displacement') None#

Convenience wrapper: reshape displacement vector to point data and write VTU. Assumes 3 dof/node ordering [u0,v0,w0, u1,v1,w1, …].

fluxfem.tools.visualizer.write_vtu(mesh: BaseMeshClosure, filepath: str, *, point_data: Mapping[str, ndarray] | None = None, cell_data: Mapping[str, ndarray] | None = None) None#

Write an UnstructuredGrid VTU for HexMesh or TetMesh. point_data/cell_data: dict name -> ndarray. Point data length must match n_points; cell data length must match n_cells.