From 7ac47c2c2941a85030fba8c3dd427336a1cd80f9 Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 4 Jan 2024 11:15:12 +0100 Subject: [PATCH 1/2] Update Doxygen to 1.10 (#2253) Update Doxygen to 1.10 and fix some documentation issues that popped up with the new version. --- include/amici/misc.h | 8 ++++++++ include/amici/solver.h | 7 ++++--- scripts/downloadAndBuildDoxygen.sh | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/include/amici/misc.h b/include/amici/misc.h index 81543b399d..6dc3294240 100644 --- a/include/amici/misc.h +++ b/include/amici/misc.h @@ -290,6 +290,10 @@ class CpuTimer { return d_milliseconds(clock::now() - start_).count(); } + /** + * @brief Whether the timer uses a thread clock (i.e. provides proper, + * thread-specific CPU time). + */ static bool const uses_thread_clock = true; private: @@ -329,6 +333,10 @@ class CpuTimer { / CLOCKS_PER_SEC; } + /** + * @brief Whether the timer uses a thread clock (i.e. provides proper, + * thread-specific CPU time). + */ static bool const uses_thread_clock = false; private: diff --git a/include/amici/solver.h b/include/amici/solver.h index 120a963ba4..4a1c95b96a 100644 --- a/include/amici/solver.h +++ b/include/amici/solver.h @@ -48,7 +48,8 @@ class Solver { public: /** Type of what is passed to Sundials solvers as user_data */ using user_data_type = std::pair; - + /** Type of the function to free a raw sundials solver pointer */ + using free_solver_ptr = std::function; /** * @brief Default constructor */ @@ -1608,10 +1609,10 @@ class Solver { void applySensitivityTolerances() const; /** pointer to solver memory block */ - mutable std::unique_ptr> solver_memory_; + mutable std::unique_ptr solver_memory_; /** pointer to solver memory block */ - mutable std::vector>> + mutable std::vector> solver_memory_B_; /** Sundials user_data */ diff --git a/scripts/downloadAndBuildDoxygen.sh b/scripts/downloadAndBuildDoxygen.sh index 19d86be5a1..4efa9ab483 100755 --- a/scripts/downloadAndBuildDoxygen.sh +++ b/scripts/downloadAndBuildDoxygen.sh @@ -9,7 +9,7 @@ DOXYGEN_DIR="${AMICI_PATH}"/ThirdParty/doxygen cd "${AMICI_PATH}"/ThirdParty if [[ ! -d ${DOXYGEN_DIR} ]]; then git clone --single-branch \ - --branch Release_1_9_7 \ + --branch Release_1_10_0 \ --depth 1 \ -c advice.detachedHead=false \ https://github.com/doxygen/doxygen.git "${DOXYGEN_DIR}" From 4df1e8f7a897414c595a5c6d90515bbc98bb9d2c Mon Sep 17 00:00:00 2001 From: Daniel Weindl Date: Thu, 4 Jan 2024 12:16:40 +0100 Subject: [PATCH 2/2] Use proper labels for plotting if IDs are available in ReturnData (#2249) `amici.plotting.*` uses some default labels (e.g. x1, x2, x3) if `model=None`. However, in some cases (ReturnDataReportingMode == full), we can still access IDs via the provided `rdata` argument. Try that. Closes #2175 --- python/sdist/amici/plotting.py | 114 +++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 42 deletions(-) diff --git a/python/sdist/amici/plotting.py b/python/sdist/amici/plotting.py index bd1f3a8ba1..edf4a33156 100644 --- a/python/sdist/amici/plotting.py +++ b/python/sdist/amici/plotting.py @@ -6,6 +6,7 @@ from typing import Iterable, Optional, Sequence, Union import matplotlib.pyplot as plt +import numpy as np import pandas as pd import seaborn as sns from matplotlib.axes import Axes @@ -16,42 +17,55 @@ def plot_state_trajectories( rdata: ReturnDataView, - state_indices: Optional[Iterable[int]] = None, + state_indices: Optional[Sequence[int]] = None, ax: Optional[Axes] = None, model: Model = None, prefer_names: bool = True, + marker=None, ) -> None: """ - Plot state trajectories + Plot state trajectories. :param rdata: AMICI simulation results as returned by - :func:`amici.amici.runAmiciSimulation` - + :func:`amici.amici.runAmiciSimulation`. :param state_indices: - Indices of states for which trajectories are to be plotted - + Indices of state variables for which trajectories are to be plotted. :param ax: - matplotlib Axes instance to plot into - + :class:`matplotlib.pyplot.Axes` instance to plot into. :param model: - amici model instance - + The model *rdata* was generated from. :param prefer_names: Whether state names should be preferred over IDs, if available. + :param marker: + Point marker for plotting (see + `matplotlib documentation `_). """ if not ax: fig, ax = plt.subplots() if not state_indices: state_indices = range(rdata["x"].shape[1]) - for ix in state_indices: - if model is None: - label = f"$x_{{{ix}}}$" - elif prefer_names and model.getStateNames()[ix]: - label = model.getStateNames()[ix] - else: - label = model.getStateIds()[ix] - ax.plot(rdata["t"], rdata["x"][:, ix], label=label) + + if marker is None: + # Show marker if only one time point is available, + # otherwise nothing will be shown + marker = "o" if len(rdata.t) == 1 else None + + if model is None and rdata.ptr.state_ids is None: + labels = [f"$x_{{{ix}}}$" for ix in state_indices] + elif model is not None and prefer_names: + labels = np.asarray(model.getStateNames())[list(state_indices)] + labels = [ + l if l else model.getStateIds()[ix] + for ix, l in enumerate(labels) + ] + elif model is not None: + labels = np.asarray(model.getStateIds())[list(state_indices)] + else: + labels = np.asarray(rdata.ptr.state_ids)[list(state_indices)] + + for ix, label in zip(state_indices, labels): + ax.plot(rdata["t"], rdata["x"][:, ix], marker=marker, label=label) ax.set_xlabel("$t$") ax.set_ylabel("$x(t)$") ax.legend() @@ -64,38 +78,54 @@ def plot_observable_trajectories( ax: Optional[Axes] = None, model: Model = None, prefer_names: bool = True, + marker=None, ) -> None: """ - Plot observable trajectories + Plot observable trajectories. :param rdata: AMICI simulation results as returned by - :func:`amici.amici.runAmiciSimulation` - + :func:`amici.amici.runAmiciSimulation`. :param observable_indices: - Indices of observables for which trajectories are to be plotted - + Indices of observables for which trajectories are to be plotted. :param ax: - matplotlib Axes instance to plot into - + :class:`matplotlib.pyplot.Axes` instance to plot into. :param model: - amici model instance - + The model *rdata* was generated from. :param prefer_names: - Whether observables names should be preferred over IDs, if available. + Whether observable names should be preferred over IDs, if available. + :param marker: + Point marker for plotting (see + `matplotlib documentation `_). + """ if not ax: fig, ax = plt.subplots() if not observable_indices: observable_indices = range(rdata["y"].shape[1]) - for iy in observable_indices: - if model is None: - label = f"$y_{{{iy}}}$" - elif prefer_names and model.getObservableNames()[iy]: - label = model.getObservableNames()[iy] - else: - label = model.getObservableIds()[iy] - ax.plot(rdata["t"], rdata["y"][:, iy], label=label) + + if marker is None: + # Show marker if only one time point is available, + # otherwise nothing will be shown + marker = "o" if len(rdata.t) == 1 else None + + if model is None and rdata.ptr.observable_ids is None: + labels = [f"$y_{{{iy}}}$" for iy in observable_indices] + elif model is not None and prefer_names: + labels = np.asarray(model.getObservableNames())[ + list(observable_indices) + ] + labels = [ + l if l else model.getObservableIds()[ix] + for ix, l in enumerate(labels) + ] + elif model is not None: + labels = np.asarray(model.getObservableIds())[list(observable_indices)] + else: + labels = np.asarray(rdata.ptr.observable_ids)[list(observable_indices)] + + for iy, label in zip(observable_indices, labels): + ax.plot(rdata["t"], rdata["y"][:, iy], marker=marker, label=label) ax.set_xlabel("$t$") ax.set_ylabel("$y(t)$") ax.legend() @@ -106,8 +136,8 @@ def plot_jacobian(rdata: ReturnDataView): """Plot Jacobian as heatmap.""" df = pd.DataFrame( data=rdata.J, - index=rdata._swigptr.state_ids_solver, - columns=rdata._swigptr.state_ids_solver, + index=rdata.ptr.state_ids_solver, + columns=rdata.ptr.state_ids_solver, ) sns.heatmap(df, center=0.0) plt.title("Jacobian") @@ -124,10 +154,10 @@ def plot_expressions( """Plot the given expressions evaluated on the given simulation outputs. :param exprs: - A symbolic expression, e.g. a sympy expression or a string that can be sympified. - Can include state variable, expression, and observable IDs, depending on whether - the respective data is available in the simulation results. - Parameters are not yet supported. + A symbolic expression, e.g., a sympy expression or a string that can be + sympified. It Can include state variable, expression, and + observable IDs, depending on whether the respective data is available + in the simulation results. Parameters are not yet supported. :param rdata: The simulation results. """