+
+
Bast, Lisa, Michèle C. Buck, Judith S. Hecker, Robert A. J. Oostendorp, Katharina S. Götze, and Carsten Marr. 2021. “Computational Modeling of Stem and Progenitor Cell Kinetics Identifies Plausible Hematopoietic Lineage Hierarchies.” iScience 24 (2): 102120. https://doi.org/https://doi.org/10.1016/j.isci.2021.102120.
+
+
+
Erdem, Cemal, Arnab Mutsuddy, Ethan M. Bensman, William B. Dodd, Michael M. Saint-Antoine, Mehdi Bouhaddou, Robert C. Blake, et al. 2021. “A Scalable, Open-Source Implementation of a Large-Scale Mechanistic Model for Single Cell Proliferation and Death Signaling.” bioRxiv. https://doi.org/10.1101/2020.11.09.373407.
+
Fröhlich, Fabian, and Peter K. Sorger. 2021. “Fides: Reliable Trust-Region Optimization for Parameter Estimation of Ordinary Differential Equation Models.” bioRxiv. https://doi.org/10.1101/2021.05.20.445065.
Gaspari, Erika. 2021. “Model-Driven Design of Mycoplasma as a Vaccine Chassis.” PhD thesis, Wageningen: Wageningen University. https://doi.org/10.18174/539593.
+
+
Maier, Corinna. 2021. “Bayesian Data Assimilation and Reinforcement Learning for Model-Informed Precision Dosing in Oncology.” Doctoralthesis, Universität Potsdam. https://doi.org/10.25932/publishup-51587.
+
Raimúndez, Elba, Erika Dudkin, Jakob Vanhoefer, Emad Alamoudi, Simon Merkt, Lara Fuhrmann, Fan Bai, and Jan Hasenauer. 2021. “COVID-19 Outbreak in Wuhan Demonstrates the Limitations of Publicly Available Case Numbers for Epidemiological Modeling.” Epidemics 34: 100439. https://doi.org/https://doi.org/10.1016/j.epidem.2021.100439.
@@ -24,8 +33,11 @@ If you applied AMICI in your work and your publication is missing, please let us
Sten, Sebastian, Henrik Podéus, Nicolas Sundqvist, Fredrik Elinder, Maria Engström, and Gunnar Cedersund. 2021. “A Multi-Data Based Quantitative Model for the Neurovascular Coupling in the Brain.” bioRxiv. https://doi.org/10.1101/2021.03.25.437053.
+
+
Tomasoni, Danilo, Alessio Paris, Stefano Giampiccolo, Federico Reali, Giulia Simoni, Luca Marchetti, Chanchala Kaddi, et al. 2021. “QSPcc Reduces Bottlenecks in Computational Model Simulations.” Communications Biology 4 (1): 1022. https://doi.org/10.1038/s42003-021-02553-9.
+
-
Vanhoefer, Jakob, Marta R. a. Matos, Dilan Pathirana, Yannik Schälte, and Jan Hasenauer. 2021. “Yaml2sbml: Human-Readable and -Writable Specification of Ode Models and Their Conversion to Sbml.” Journal of Open Source Software 6 (61): 3215. https://doi.org/10.21105/joss.03215.
+
Vanhoefer, Jakob, Marta R. A. Matos, Dilan Pathirana, Yannik Schälte, and Jan Hasenauer. 2021. “Yaml2sbml: Human-Readable and -Writable Specification of Ode Models and Their Conversion to Sbml.” Journal of Open Source Software 6 (61): 3215. https://doi.org/10.21105/joss.03215.
van Rosmalen, R. P., R. W. Smith, V. A. P. Martins dos Santos, C. Fleck, and M. Suarez-Diez. 2021. “Model Reduction of Genome-Scale Metabolic Models as a Basis for Targeted Kinetic Models.” Metabolic Engineering 64: 74–84. https://doi.org/https://doi.org/10.1016/j.ymben.2021.01.008.
@@ -48,6 +60,9 @@ If you applied AMICI in your work and your publication is missing, please let us
Kuritz, Karsten, Alain R Bonny, João Pedro Fonseca, and Frank Allgöwer. 2020. “PDE-Constrained Optimization for Estimating Population Dynamics over Cell Cycle from Static Single Cell Measurements.” bioRxiv. https://doi.org/10.1101/2020.03.30.015909.
+
+
Maier, Corinna, Niklas Hartung, Charlotte Kloft, Wilhelm Huisinga, and Jana de Wiljes. 2020. “Reinforcement Learning and Bayesian Data Assimilation for Model-Informed Precision Dosing in Oncology.” http://arxiv.org/abs/2006.01061.
+
Schälte, Yannik, and Jan Hasenauer. 2020. “Efficient Exact Inference for Dynamical Systems with Noisy Measurements Using Sequential Approximate Bayesian Computation.” bioRxiv. https://doi.org/10.1101/2020.01.30.927004.
diff --git a/include/amici/defines.h b/include/amici/defines.h
index 7e69576179..852b0c4370 100644
--- a/include/amici/defines.h
+++ b/include/amici/defines.h
@@ -109,6 +109,13 @@ enum class ParameterScaling {
log10
};
+/** modes for observable scaling */
+enum class ObservableScaling {
+ lin,
+ log,
+ log10
+};
+
/** modes for second order sensitivity analysis */
enum class SecondOrderMode {
none,
diff --git a/include/amici/model.h b/include/amici/model.h
index 1e47808443..1de282d206 100644
--- a/include/amici/model.h
+++ b/include/amici/model.h
@@ -802,6 +802,13 @@ class Model : public AbstractModel, public ModelDimensions {
*/
void getObservable(gsl::span
y, const realtype t,
const AmiVector &x);
+
+ /**
+ * @brief Get scaling type for observable
+ * @param iy observable index
+ * @return scaling type
+ */
+ virtual ObservableScaling getObservableScaling(int iy) const;
/**
* @brief Get sensitivity of time-resolved observables.
diff --git a/include/amici/steadystateproblem.h b/include/amici/steadystateproblem.h
index bd7efe47c4..550433e797 100644
--- a/include/amici/steadystateproblem.h
+++ b/include/amici/steadystateproblem.h
@@ -144,8 +144,8 @@ class SteadystateProblem {
* @brief Computes the weighted root mean square of xdot
* the weights are computed according to x:
* w_i = 1 / ( rtol * x_i + atol )
- * @param x current state
- * @param xdot current rhs
+ * @param x current state (sx[ip] for sensitivities)
+ * @param xdot current rhs (sxdot[ip] for sensitivities)
* @param atol absolute tolerance
* @param rtol relative tolerance
* @param ewt error weight vector
diff --git a/include/amici/sundials_matrix_wrapper.h b/include/amici/sundials_matrix_wrapper.h
index 2ccc7622ab..43c1af9842 100644
--- a/include/amici/sundials_matrix_wrapper.h
+++ b/include/amici/sundials_matrix_wrapper.h
@@ -9,6 +9,8 @@
#include
+#include
+
#include "amici/vector.h"
namespace amici {
@@ -118,13 +120,25 @@ class SUNMatrixWrapper {
* @brief Get the number of rows
* @return number of rows
*/
- sunindextype rows() const;
+ sunindextype rows() const {
+ assert(!matrix_ ||
+ (matrix_id() == SUNMATRIX_SPARSE ?
+ num_rows_ == SM_ROWS_S(matrix_) :
+ num_rows_ == SM_ROWS_D(matrix_)));
+ return num_rows_;
+ }
/**
* @brief Get the number of columns
* @return number of columns
*/
- sunindextype columns() const;
+ sunindextype columns() const {
+ assert(!matrix_ ||
+ (matrix_id() == SUNMATRIX_SPARSE ?
+ num_columns_ == SM_COLUMNS_S(matrix_) :
+ num_columns_ == SM_COLUMNS_D(matrix_)));
+ return num_columns_;
+ }
/**
* @brief Get the number of specified non-zero elements (sparse matrices only)
@@ -162,7 +176,13 @@ class SUNMatrixWrapper {
* @param idx data index
* @return idx-th data entry
*/
- realtype get_data(sunindextype idx) const;
+ realtype get_data(sunindextype idx) const{
+ assert(matrix_);
+ assert(matrix_id() == SUNMATRIX_SPARSE);
+ assert(idx < capacity());
+ assert(SM_DATA_S(matrix_) == data_);
+ return data_[idx];
+ }
/**
* @brief Get data entry for a dense matrix
@@ -170,14 +190,26 @@ class SUNMatrixWrapper {
* @param icol col
* @return A(irow,icol)
*/
- realtype get_data(sunindextype irow, sunindextype icol) const;
+ realtype get_data(sunindextype irow, sunindextype icol) const{
+ assert(matrix_);
+ assert(matrix_id() == SUNMATRIX_DENSE);
+ assert(irow < rows());
+ assert(icol < columns());
+ return SM_ELEMENT_D(matrix_, irow, icol);
+ }
/**
* @brief Set data entry for a sparse matrix
* @param idx data index
* @param data data for idx-th entry
*/
- void set_data(sunindextype idx, realtype data);
+ void set_data(sunindextype idx, realtype data) {
+ assert(matrix_);
+ assert(matrix_id() == SUNMATRIX_SPARSE);
+ assert(idx < capacity());
+ assert(SM_DATA_S(matrix_) == data_);
+ data_[idx] = data;
+ }
/**
* @brief Set data entry for a dense matrix
@@ -185,47 +217,93 @@ class SUNMatrixWrapper {
* @param icol col
* @param data data for idx-th entry
*/
- void set_data(sunindextype irow, sunindextype icol, realtype data);
+ void set_data(sunindextype irow, sunindextype icol, realtype data) {
+ assert(matrix_);
+ assert(matrix_id() == SUNMATRIX_DENSE);
+ assert(irow < rows());
+ assert(icol < columns());
+ SM_ELEMENT_D(matrix_, irow, icol) = data;
+ }
/**
* @brief Get the index value of a sparse matrix
* @param idx data index
* @return row (CSC) or column (CSR) for idx-th data entry
*/
- sunindextype get_indexval(sunindextype idx) const;
+ sunindextype get_indexval(sunindextype idx) const {
+ assert(matrix_);
+ assert(matrix_id() == SUNMATRIX_SPARSE);
+ assert(idx < capacity());
+ assert(indexvals_ == SM_INDEXVALS_S(matrix_));
+ return indexvals_[idx];
+ }
/**
* @brief Set the index value of a sparse matrix
* @param idx data index
* @param val row (CSC) or column (CSR) for idx-th data entry
*/
- void set_indexval(sunindextype idx, sunindextype val);
+ void set_indexval(sunindextype idx, sunindextype val) {
+ assert(matrix_);
+ assert(matrix_id() == SUNMATRIX_SPARSE);
+ assert(idx < capacity());
+ assert(indexvals_ == SM_INDEXVALS_S(matrix_));
+ indexvals_[idx] = val;
+ }
/**
* @brief Set the index values of a sparse matrix
* @param vals rows (CSC) or columns (CSR) for data entries
*/
- void set_indexvals(const gsl::span vals);
+ void set_indexvals(const gsl::span vals) {
+ assert(matrix_);
+ assert(matrix_id() == SUNMATRIX_SPARSE);
+ assert(static_cast(vals.size()) == capacity());
+ assert(indexvals_ == SM_INDEXVALS_S(matrix_));
+ std::copy_n(vals.begin(), capacity(), indexvals_);
+ }
/**
* @brief Get the index pointer of a sparse matrix
* @param ptr_idx pointer index
* @return index where the ptr_idx-th column (CSC) or row (CSR) starts
*/
- sunindextype get_indexptr(sunindextype ptr_idx) const;
+ sunindextype get_indexptr(sunindextype ptr_idx) const {
+ assert(matrix_);
+ assert(matrix_id() == SUNMATRIX_SPARSE);
+ assert(ptr_idx <= num_indexptrs());
+ assert(indexptrs_ == SM_INDEXPTRS_S(matrix_));
+ return indexptrs_[ptr_idx];
+ }
/**
* @brief Set the index pointer of a sparse matrix
* @param ptr_idx pointer index
* @param ptr data-index where the ptr_idx-th column (CSC) or row (CSR) starts
*/
- void set_indexptr(sunindextype ptr_idx, sunindextype ptr);
+ void set_indexptr(sunindextype ptr_idx, sunindextype ptr) {
+ assert(matrix_);
+ assert(matrix_id() == SUNMATRIX_SPARSE);
+ assert(ptr_idx <= num_indexptrs());
+ assert(ptr <= capacity());
+ assert(indexptrs_ == SM_INDEXPTRS_S(matrix_));
+ indexptrs_[ptr_idx] = ptr;
+ if (ptr_idx == num_indexptrs())
+ num_nonzeros_ = ptr;
+ }
/**
* @brief Set the index pointers of a sparse matrix
* @param ptrs starting data-indices where the columns (CSC) or rows (CSR) start
*/
- void set_indexptrs(const gsl::span ptrs);
+ void set_indexptrs(const gsl::span ptrs) {
+ assert(matrix_);
+ assert(matrix_id() == SUNMATRIX_SPARSE);
+ assert(static_cast(ptrs.size()) == num_indexptrs() + 1);
+ assert(indexptrs_ == SM_INDEXPTRS_S(matrix_));
+ std::copy_n(ptrs.begin(), num_indexptrs() + 1, indexptrs_);
+ num_nonzeros_ = indexptrs_[num_indexptrs()];
+ }
/**
* @brief Get the type of sparse matrix
diff --git a/matlab/mtoc/config/Doxyfile.template b/matlab/mtoc/config/Doxyfile.template
index a50605b07c..ed2f623f17 100644
--- a/matlab/mtoc/config/Doxyfile.template
+++ b/matlab/mtoc/config/Doxyfile.template
@@ -875,7 +875,6 @@ WARN_LOGFILE =
INPUT = "_SourceDir_/README.md" \
"_SourceDir_/LICENSE.md" \
- "_SourceDir_/INSTALL.md" \
"_SourceDir_/documentation/MATLAB_.md" \
"_SourceDir_/documentation/CPP_.md" \
"_SourceDir_/include" \
diff --git a/matlab/tests/testModels.m b/matlab/tests/testModels.m
index baf1c57bda..047f86daf2 100644
--- a/matlab/tests/testModels.m
+++ b/matlab/tests/testModels.m
@@ -18,18 +18,18 @@ function testModels()
model_dir = [fileparts(mfilename('fullpath')) '/../../models/'];
cd(fileparts(mfilename('fullpath')))
- addpath(genpath('../../tests/cpputest'));
+ addpath(genpath('../../tests/cpp'));
addpath(genpath('../examples'));
% wrapTestModels()
cd(fileparts(mfilename('fullpath')))
hdf5file = fullfile(fileparts(mfilename('fullpath')), ...
- '../../tests/cpputest', 'expectedResults.h5');
-
+ '../../tests/cpp', 'expectedResults.h5');
+
info = h5info(hdf5file);
for imodel = 1:length(info.Groups)
modelname = info.Groups(imodel).Name(2:end);
-
+
if(~isempty(regexp(modelname,'^model_neuron')))
model_atol = 1e-9;
model_rtol = 1e-4;
@@ -42,18 +42,18 @@ function testModels()
if(ismember(testname, ignoredTests))
continue
end
-
+
display(testname);
[results,options,data,t,theta,kappa] = readDataFromHDF5(info.Groups(imodel).Groups(itest),hdf5file);
-
+
% rebuild model
old_path = addpath([model_dir modelname]);
old_pwd = cd([model_dir modelname]);
rebuild = str2func(['rebuild_' modelname]);
rebuild();
cd(old_pwd);
-
+
sol = getResults(modelname,options,data,t,theta,kappa);
compareResults(sol,results);
path(old_path);
diff --git a/python/amici/custom_commands.py b/python/amici/custom_commands.py
index e7f36ebd2d..d4c25a348a 100644
--- a/python/amici/custom_commands.py
+++ b/python/amici/custom_commands.py
@@ -15,7 +15,6 @@
from setuptools.command.install import install
from setuptools.command.install_lib import install_lib
from setuptools.command.sdist import sdist
-from distutils import log
# typehints
Library = Tuple[str, Dict[str, List[str]]]
@@ -24,7 +23,7 @@
class AmiciInstall(install):
"""Custom install to handle extra arguments"""
- log.debug("running AmiciInstall")
+ print("running AmiciInstall")
# Passing --no-clibs allows to install the Python-only part of AMICI
user_options = install.user_options + [
@@ -81,7 +80,7 @@ class AmiciBuildCLib(build_clib):
"""Custom build_clib"""
def run(self):
- log.debug("running AmiciBuildCLib")
+ print("running AmiciBuildCLib")
# Always force recompilation. The way setuptools/distutils check for
# whether sources require recompilation is not reliable and may lead
@@ -91,7 +90,7 @@ def run(self):
build_clib.run(self)
def build_libraries(self, libraries: List[Library]):
- log.debug("running AmiciBuildCLib.build_libraries")
+ print("running AmiciBuildCLib.build_libraries")
no_clibs = 'develop' in self.distribution.command_obj \
and self.get_finalized_command('develop').no_clibs
@@ -139,7 +138,7 @@ def finalize_options(self):
develop.finalize_options(self)
def run(self):
- log.debug("running AmiciDevelop")
+ print("running AmiciDevelop")
if not self.no_clibs:
self.get_finalized_command('build_clib').run()
@@ -156,7 +155,7 @@ def run(self):
Returns:
"""
- log.debug("running AmiciInstallLib")
+ print("running AmiciInstallLib")
if 'ENABLE_AMICI_DEBUGGING' in os.environ \
and os.environ['ENABLE_AMICI_DEBUGGING'] == 'TRUE' \
@@ -187,7 +186,7 @@ def run(self):
the wheel
"""
- log.debug("running AmiciBuildExt")
+ print("running AmiciBuildExt")
no_clibs = 'develop' in self.distribution.command_obj \
and self.get_finalized_command('develop').no_clibs
@@ -220,13 +219,13 @@ def run(self):
f"Found unexpected number of files: {libfilenames}"
src = libfilenames[0]
dest = os.path.join(target_dir, os.path.basename(src))
- log.info(f"copying {src} -> {dest}")
+ print(f"copying {src} -> {dest}")
copyfile(src, dest)
swig_outdir = os.path.join(os.path.abspath(build_dir), "amici")
generate_swig_interface_files(swig_outdir=swig_outdir)
swig_py_module_path = os.path.join(swig_outdir, 'amici.py')
- log.debug("updating typehints")
+ print("updating typehints")
fix_typehints(swig_py_module_path, swig_py_module_path)
# Always force recompilation. The way setuptools/distutils check for
@@ -244,7 +243,7 @@ class AmiciSDist(sdist):
def run(self):
"""Setuptools entry-point"""
- log.debug("running AmiciSDist")
+ print("running AmiciSDist")
save_git_version()
@@ -266,7 +265,7 @@ def save_git_version():
'--always', '--tags']
subprocess.run(cmd, stdout=f)
except Exception as e:
- log.warn(e)
+ print(e)
def set_compiler_specific_library_options(
@@ -298,7 +297,7 @@ def set_compiler_specific_library_options(
for field in ['cflags', 'sources', 'macros']:
try:
lib[1][field] += lib[1][f'{field}_{compiler_type}']
- log.info(f"Changed {field} for {lib[0]} with {compiler_type} "
+ print(f"Changed {field} for {lib[0]} with {compiler_type} "
f"to {lib[1][field]}")
except KeyError:
# No compiler-specific options set
@@ -322,7 +321,7 @@ def set_compiler_specific_extension_options(
new_value = getattr(ext, attr) + \
getattr(ext, f'{attr}_{compiler_type}')
setattr(ext, attr, new_value)
- log.info(f"Changed {attr} for {compiler_type} to {new_value}")
+ print(f"Changed {attr} for {compiler_type} to {new_value}")
except AttributeError:
# No compiler-specific options set
pass
diff --git a/python/amici/import_utils.py b/python/amici/import_utils.py
index 833e622c4a..52c9c793c0 100644
--- a/python/amici/import_utils.py
+++ b/python/amici/import_utils.py
@@ -4,13 +4,44 @@
from typing import Dict, Union, Optional, Callable
import sympy as sp
+import enum
from toposort import toposort
SymbolDef = Dict[sp.Symbol, Union[Dict[str, sp.Expr], sp.Expr]]
+class ObservableTransformation(str, enum.Enum):
+ """
+ Different modes of observable transformation.
+ """
+ LOG10 = 'log10'
+ LOG = 'log'
+ LIN = 'lin'
+
+
+def noise_distribution_to_observable_transformation(
+ noise_distribution: Union[str, Callable]
+) -> ObservableTransformation:
+ """
+ Parse noise distribution string and extract observable transformation
+
+ :param noise_distribution:
+ see :func:`noise_distribution_to_cost_function`
+
+ :return:
+ observable transformation
+ """
+ if isinstance(noise_distribution, str):
+ if noise_distribution.startswith('log-'):
+ return ObservableTransformation.LOG
+ if noise_distribution.startswith('log10-'):
+ return ObservableTransformation.LOG10
+
+ return ObservableTransformation.LIN
+
+
def noise_distribution_to_cost_function(
- noise_distribution: str
+ noise_distribution: Union[str, Callable]
) -> Callable[[str], str]:
"""
Parse noise distribution string to a cost function definition amici can
diff --git a/python/amici/ode_export.py b/python/amici/ode_export.py
index b5ec800af4..e0376b8791 100644
--- a/python/amici/ode_export.py
+++ b/python/amici/ode_export.py
@@ -45,7 +45,8 @@
)
from .logging import get_logger, log_execution_time, set_log_level
from .constants import SymbolId
-from .import_utils import smart_subs_dict, toposort_symbols
+from .import_utils import smart_subs_dict, toposort_symbols, \
+ ObservableTransformation
# Template for model simulation main.cpp file
CXX_MAIN_TEMPLATE_FILE = os.path.join(amiciSrcPath, 'main.template.cpp')
@@ -555,6 +556,10 @@ class Observable(ModelQuantity):
:ivar _measurement_symbol:
sympy symbol used in the objective function to represent
measurements to this observable
+
+ :ivar trafo:
+ observable transformation, only applies when evaluating objective
+ function or residuals
"""
_measurement_symbol: Union[sp.Symbol, None] = None
@@ -563,7 +568,8 @@ def __init__(self,
identifier: sp.Symbol,
name: str,
value: sp.Expr,
- measurement_symbol: Optional[sp.Symbol] = None):
+ measurement_symbol: Optional[sp.Symbol] = None,
+ transformation: Optional[ObservableTransformation] = 'lin'):
"""
Create a new Observable instance.
@@ -575,9 +581,14 @@ def __init__(self,
:param value:
formula
+
+ :param transformation:
+ observable transformation, only applies when evaluating objective
+ function or residuals
"""
super(Observable, self).__init__(identifier, name, value)
self._measurement_symbol = measurement_symbol
+ self.trafo = transformation
def get_measurement_symbol(self) -> sp.Symbol:
if self._measurement_symbol is None:
@@ -1050,10 +1061,9 @@ def import_from_sbml_importer(self,
nexpr = len(symbols[SymbolId.EXPRESSION])
# assemble fluxes and add them as expressions to the model
- fluxes = []
- for ir, flux in enumerate(si.flux_vector):
- flux_id = generate_flux_symbol(ir)
- fluxes.append(flux_id)
+ assert len(si.flux_ids) == len(si.flux_vector)
+ fluxes = [generate_flux_symbol(ir, name=flux_id)
+ for ir, flux_id in enumerate(si.flux_ids)]
nr = len(fluxes)
# correct time derivatives for compartment changes
@@ -1161,6 +1171,8 @@ def transform_dxdt_to_concentration(species_id, dxdt):
args += ['value']
if symbol_name == SymbolId.EVENT:
args += ['state_update', 'event_observable']
+ if symbol_name == SymbolId.OBSERVABLE:
+ args += ['transformation']
protos = [
{
@@ -1216,7 +1228,8 @@ def transform_dxdt_to_concentration(species_id, dxdt):
# fill in 'self._sym' based on prototypes and components in ode_model
self.generate_basic_variables(from_sbml=True)
self._has_quadratic_nllh = all(
- llh['dist'] in ['normal', 'lin-normal']
+ llh['dist'] in ['normal', 'lin-normal', 'log-normal',
+ 'log10-normal']
for llh in si.symbols[SymbolId.LLHY].values()
)
@@ -1300,6 +1313,17 @@ def add_conservation_law(self,
self._states[ix].set_conservation_law(state_expr)
+
+ def get_observable_transformations(self) -> List[ObservableTransformation]:
+ """
+ List of observable transformations
+
+ :return:
+ list of transformations
+ """
+ return [obs.trafo for obs in self._observables]
+
+
def num_states_rdata(self) -> int:
"""
Number of states.
@@ -2575,9 +2599,6 @@ class ODEExporter:
:ivar model:
ODE definition
- :ivar outdir:
- see :meth:`amici.ode_export.ODEExporter.set_paths`
-
:ivar verbose:
more verbose output if True
@@ -2621,7 +2642,8 @@ def __init__(
assume_pow_positivity: Optional[bool] = False,
compiler: Optional[str] = None,
allow_reinit_fixpar_initcond: Optional[bool] = True,
- generate_sensitivity_code: Optional[bool] = True
+ generate_sensitivity_code: Optional[bool] = True,
+ model_name: Optional[str] = 'model'
):
"""
Generate AMICI C++ files for the ODE provided to the constructor.
@@ -2649,19 +2671,21 @@ def __init__(
:param generate_sensitivity_code specifies whether code required for
sensitivity computation will be generated
+
+ :param model_name:
+ name of the model to be used during code generation
"""
set_log_level(logger, verbose)
- self.outdir: str = outdir
self.verbose: bool = logger.getEffectiveLevel() <= logging.DEBUG
self.assume_pow_positivity: bool = assume_pow_positivity
self.compiler: str = compiler
- self.model_name: str = 'model'
- output_dir = os.path.join(os.getcwd(),
- f'amici-{self.model_name}')
- self.model_path: str = os.path.abspath(output_dir)
- self.model_swig_path: str = os.path.join(self.model_path, 'swig')
+ self.model_path: str = ''
+ self.model_swig_path: str = ''
+
+ self.set_name(model_name)
+ self.set_paths(outdir)
# Signatures and properties of generated model functions (see
# include/amici/model.h for details)
@@ -2702,8 +2726,11 @@ def compile_model(self) -> None:
def _prepare_model_folder(self) -> None:
"""
- Remove all files from the model folder.
+ Create model directory or remove all files if the output directory
+ already exists.
"""
+ os.makedirs(self.model_path, exist_ok=True)
+
for file in os.listdir(self.model_path):
file_path = os.path.join(self.model_path, file)
if os.path.isfile(file_path):
@@ -3290,6 +3317,13 @@ def _write_model_header_cpp(self) -> None:
self._get_symbol_name_initializer_list('k'),
'OBSERVABLE_NAMES_INITIALIZER_LIST':
self._get_symbol_name_initializer_list('y'),
+ 'OBSERVABLE_TRAFO_INITIALIZER_LIST':
+ '\n'.join(
+ f'ObservableScaling::{trafo}, // y[{idx}]'
+ for idx, trafo in enumerate(
+ self.model.get_observable_transformations()
+ )
+ ),
'EXPRESSION_NAMES_INITIALIZER_LIST':
self._get_symbol_name_initializer_list('w'),
'PARAMETER_IDS_INITIALIZER_LIST':
@@ -3472,22 +3506,24 @@ def _write_module_setup(self) -> None:
template_data
)
- def set_paths(self, output_dir: str) -> None:
+ def set_paths(self, output_dir: Optional[str] = None) -> None:
"""
Set output paths for the model and create if necessary
:param output_dir:
relative or absolute path where the generated model
- code is to be placed. will be created if does not exists.
+ code is to be placed. If ``None``, this will default to
+ `amici-{self.model_name}` in the current working directory.
+ will be created if does not exists.
"""
+ if output_dir is None:
+ output_dir = os.path.join(os.getcwd(),
+ f'amici-{self.model_name}')
+
self.model_path = os.path.abspath(output_dir)
self.model_swig_path = os.path.join(self.model_path, 'swig')
- for directory in [self.model_path, self.model_swig_path]:
- if not os.path.exists(directory):
- os.makedirs(directory)
-
def set_name(self, model_name: str) -> None:
"""
Sets the model name
@@ -3873,7 +3909,10 @@ def generate_measurement_symbol(observable_id: Union[str, sp.Symbol]):
return symbol_with_assumptions(f'm{observable_id}')
-def generate_flux_symbol(reaction_index: int) -> sp.Symbol:
+def generate_flux_symbol(
+ reaction_index: int,
+ name: Optional[str] = None
+) -> sp.Symbol:
"""
Generate identifier symbol for a reaction flux.
This function will always return the same unique python object for a
@@ -3881,9 +3920,14 @@ def generate_flux_symbol(reaction_index: int) -> sp.Symbol:
:param reaction_index:
index of the reaction to which the flux corresponds
+ :param name:
+ an optional identifier of the reaction to which the flux corresponds
:return:
identifier symbol
"""
+ if name is not None:
+ return symbol_with_assumptions(name)
+
return symbol_with_assumptions(f'flux_r{reaction_index}')
diff --git a/python/amici/pysb_import.py b/python/amici/pysb_import.py
index 603eb3ef23..6dab3f8a73 100644
--- a/python/amici/pysb_import.py
+++ b/python/amici/pysb_import.py
@@ -11,7 +11,8 @@
)
from .import_utils import (
- noise_distribution_to_cost_function, _get_str_symbol_identifiers
+ noise_distribution_to_cost_function, _get_str_symbol_identifiers,
+ noise_distribution_to_observable_transformation
)
import logging
from .logging import get_logger, log_execution_time, set_log_level
@@ -139,13 +140,12 @@ def pysb2amici(
exporter = ODEExporter(
ode_model,
outdir=output_dir,
+ model_name=model.name,
verbose=verbose,
assume_pow_positivity=assume_pow_positivity,
compiler=compiler,
generate_sensitivity_code=generate_sensitivity_code
)
- exporter.set_name(model.name)
- exporter.set_paths(output_dir)
exporter.generate_model_code()
if compile:
@@ -218,7 +218,7 @@ def ode_model_from_pysb_importer(
_process_pysb_expressions(model, ode, observables, sigmas,
noise_distributions)
ode._has_quadratic_nllh = not noise_distributions or all(
- noise_distr in ['normal', 'lin-normal']
+ noise_distr in ['normal', 'lin-normal', 'log-normal', 'log10-normal']
for noise_distr in noise_distributions.values()
)
@@ -374,8 +374,12 @@ def _add_expression(
)
if name in observables:
+ noise_dist = noise_distributions.get(name, 'normal') \
+ if noise_distributions else 'normal'
+
y = sp.Symbol(f'{name}')
- obs = Observable(y, name, sym)
+ trafo = noise_distribution_to_observable_transformation(noise_dist)
+ obs = Observable(y, name, sym, transformation=trafo)
ode_model.add_component(obs)
sigma_name, sigma_value = _get_sigma_name_and_value(
@@ -385,8 +389,7 @@ def _add_expression(
sigma = sp.Symbol(sigma_name)
ode_model.add_component(SigmaY(sigma, f'{sigma_name}', sigma_value))
- noise_dist = noise_distributions.get(name, 'normal') \
- if noise_distributions else 'normal'
+
cost_fun_str = noise_distribution_to_cost_function(noise_dist)(name)
my = generate_measurement_symbol(obs.get_id())
cost_fun_expr = sp.sympify(cost_fun_str,
diff --git a/python/amici/sbml_import.py b/python/amici/sbml_import.py
index ab9cfd9ba6..c851384d76 100644
--- a/python/amici/sbml_import.py
+++ b/python/amici/sbml_import.py
@@ -20,8 +20,8 @@
from .import_utils import (
smart_subs, smart_subs_dict, toposort_symbols,
- _get_str_symbol_identifiers,
- noise_distribution_to_cost_function
+ _get_str_symbol_identifiers, noise_distribution_to_cost_function,
+ noise_distribution_to_observable_transformation
)
from .ode_export import (
ODEExporter, ODEModel, generate_measurement_symbol,
@@ -87,6 +87,9 @@ class SbmlImporter:
:ivar flux_vector:
reaction kinetic laws
+ :ivar flux_ids:
+ identifiers for elements of flux_vector
+
:ivar _local_symbols:
model symbols for sympy to consider during sympification
see `locals`argument in `sympy.sympify`
@@ -364,6 +367,7 @@ def sbml2amici(self,
self, compute_cls=compute_conservation_laws)
exporter = ODEExporter(
ode_model,
+ model_name=model_name,
outdir=output_dir,
verbose=verbose,
assume_pow_positivity=assume_pow_positivity,
@@ -371,8 +375,6 @@ def sbml2amici(self,
allow_reinit_fixpar_initcond=allow_reinit_fixpar_initcond,
generate_sensitivity_code=generate_sensitivity_code
)
- exporter.set_name(model_name)
- exporter.set_paths(output_dir)
exporter.generate_model_code()
if compile:
@@ -406,28 +408,49 @@ def check_support(self) -> None:
Also ensures that the SBML contains at least one reaction, or rate
rule, or assignment rule, to produce change in the system over time.
"""
- if hasattr(self.sbml, 'all_elements_from_plugins') \
+
+ # Check for required but unsupported SBML extensions
+ if self.sbml_doc.getLevel() != 3 \
+ and hasattr(self.sbml, 'all_elements_from_plugins') \
and self.sbml.all_elements_from_plugins.getSize():
raise SBMLException('SBML extensions are currently not supported!')
- if any([not rule.isAssignment() and not isinstance(
+ if self.sbml_doc.getLevel() == 3:
+ # the "required" attribute is only available in SBML Level 3
+ for i_plugin in range(self.sbml.getNumPlugins()):
+ plugin = self.sbml.getPlugin(i_plugin)
+ if plugin.getPackageName() in ('layout',):
+ # 'layout' plugin does not have the 'required' attribute
+ continue
+ if hasattr(plugin, 'getRequired') and not plugin.getRequired():
+ # if not "required", this has no impact on model
+ # simulation, and we can safely ignore it
+ continue
+ # Check if there are extension elements. If not, we can safely
+ # ignore the enabled package
+ if plugin.getListOfAllElements():
+ raise SBMLException(
+ f'Required SBML extension {plugin.getPackageName()} '
+ f'is currently not supported!')
+
+ if any(not rule.isAssignment() and not isinstance(
self.sbml.getElementBySId(rule.getVariable()),
(sbml.Compartment, sbml.Species, sbml.Parameter)
- ) for rule in self.sbml.getListOfRules()]):
+ ) for rule in self.sbml.getListOfRules()):
raise SBMLException('Algebraic rules are currently not supported, '
'and rate rules are only supported for '
'species, compartments, and parameters.')
- if any([not (rule.isAssignment() or rule.isRate())
+ if any(not (rule.isAssignment() or rule.isRate())
and isinstance(
self.sbml.getElementBySId(rule.getVariable()),
(sbml.Compartment, sbml.Species, sbml.Parameter)
- ) for rule in self.sbml.getListOfRules()]):
+ ) for rule in self.sbml.getListOfRules()):
raise SBMLException('Only assignment and rate rules are '
'currently supported for compartments, '
'species, and parameters!')
- if any([r.getFast() for r in self.sbml.getListOfReactions()]):
+ if any(r.getFast() for r in self.sbml.getListOfReactions()):
raise SBMLException('Fast reactions are currently not supported!')
# Check events for unsupported functionality
@@ -849,6 +872,14 @@ def _process_reactions(self):
# stoichiometric matrix
self.stoichiometric_matrix = sp.SparseMatrix(sp.zeros(nx, nr))
self.flux_vector = sp.zeros(nr, 1)
+ # Use reaction IDs as IDs for flux expressions (note that prior to SBML
+ # level 3 version 2 the ID attribute was not mandatory and may be
+ # unset)
+ self.flux_ids = [
+ f"flux_{reaction.getId()}" if reaction.isSetId()
+ else f"flux_r{reaction_idx}"
+ for reaction_idx, reaction in enumerate(reactions)
+ ] or ['flux_r0']
reaction_ids = [
reaction.getId() for reaction in reactions
@@ -1178,7 +1209,11 @@ def _process_observables(
# former.
'value': self._sympy_from_sbml_math(
definition['formula']
- )
+ ),
+ 'transformation':
+ noise_distribution_to_observable_transformation(
+ noise_distributions.get(obs, 'normal')
+ )
}
for iobs, (obs, definition) in enumerate(observables.items())
}
diff --git a/python/amici/setup.template.py b/python/amici/setup.template.py
index b9b6f69c44..990b5ea232 100644
--- a/python/amici/setup.template.py
+++ b/python/amici/setup.template.py
@@ -30,9 +30,9 @@ def build_extension(self, ext):
# except for Windows, where this seems to be incompatible with
# providing swig files. Not investigated further...
if sys.platform != 'win32':
- import distutils.ccompiler
+ import setuptools._distutils.ccompiler
self.compiler.compile = compile_parallel.__get__(
- self.compiler, distutils.ccompiler.CCompiler)
+ self.compiler, setuptools._distutils.ccompiler.CCompiler)
build_ext.build_extension(self, ext)
@@ -64,7 +64,7 @@ def get_amici_libs() -> List[str]:
def get_extension() -> Extension:
- """Get distutils extension object for this AMICI model package"""
+ """Get setuptools extension object for this AMICI model package"""
cxx_flags = []
linker_flags = []
diff --git a/python/amici/setuptools.py b/python/amici/setuptools.py
index 4c998e318d..7cfcf61fe1 100644
--- a/python/amici/setuptools.py
+++ b/python/amici/setuptools.py
@@ -9,7 +9,6 @@
import shlex
import subprocess
-from distutils import log
from .swig import find_swig, get_swig_version
try:
@@ -143,7 +142,7 @@ def get_hdf5_config() -> PackageInfo:
hdf5_include_dir_found = os.path.isfile(
os.path.join(hdf5_include_dir_hint, 'hdf5.h'))
if hdf5_include_dir_found:
- log.info('hdf5.h found in %s' % hdf5_include_dir_hint)
+ print(f"hdf5.h found in {hdf5_include_dir_hint}")
h5pkgcfg['include_dirs'] = [hdf5_include_dir_hint]
break
@@ -153,7 +152,7 @@ def get_hdf5_config() -> PackageInfo:
hdf5_library_dir_found = os.path.isfile(
os.path.join(hdf5_library_dir_hint, lib_filename))
if hdf5_library_dir_found:
- log.info(f'{lib_filename} found in {hdf5_library_dir_hint}')
+ print(f'{lib_filename} found in {hdf5_library_dir_hint}')
h5pkgcfg['library_dirs'] = [hdf5_library_dir_hint]
break
if hdf5_library_dir_found:
@@ -192,8 +191,8 @@ def add_coverage_flags_if_required(cxx_flags: List[str],
"""
if 'ENABLE_GCOV_COVERAGE' in os.environ and \
os.environ['ENABLE_GCOV_COVERAGE'].upper() == 'TRUE':
- log.info("ENABLE_GCOV_COVERAGE was set to TRUE."
- " Building AMICI with coverage symbols.")
+ print("ENABLE_GCOV_COVERAGE was set to TRUE."
+ " Building AMICI with coverage symbols.")
cxx_flags.extend(['-g', '-O0', '--coverage'])
linker_flags.extend(['--coverage', '-g'])
@@ -212,8 +211,8 @@ def add_debug_flags_if_required(cxx_flags: List[str],
"""
if 'ENABLE_AMICI_DEBUGGING' in os.environ \
and os.environ['ENABLE_AMICI_DEBUGGING'] == 'TRUE':
- log.info("ENABLE_AMICI_DEBUGGING was set to TRUE."
- " Building AMICI with debug symbols.")
+ print("ENABLE_AMICI_DEBUGGING was set to TRUE."
+ " Building AMICI with debug symbols.")
cxx_flags.extend(['-g', '-O0', '-UNDEBUG'])
linker_flags.extend(['-g'])
@@ -237,7 +236,7 @@ def generate_swig_interface_files(swig_outdir: str = None,
f'-Iamici{os.sep}include',
]
- log.info(f"Found SWIG version {swig_version}")
+ print(f"Found SWIG version {swig_version}")
# Are HDF5 includes available to generate the wrapper?
if with_hdf5 is None:
@@ -258,7 +257,7 @@ def generate_swig_interface_files(swig_outdir: str = None,
'-o', os.path.join("amici", "amici_wrap.cxx"),
os.path.join("amici", "swig", "amici.i")]
- log.info(f"Running SWIG: {' '.join(swig_cmd)}")
+ print(f"Running SWIG: {' '.join(swig_cmd)}")
sp = subprocess.run(swig_cmd, stdout=subprocess.PIPE,
stderr=sys.stdout.buffer)
if not sp.returncode == 0:
@@ -271,15 +270,15 @@ def add_openmp_flags(cxx_flags: List, ldflags: List) -> None:
# Enable OpenMP support for Linux / OSX:
if sys.platform == 'linux':
- log.info("Adding OpenMP flags...")
+ print("Adding OpenMP flags...")
cxx_flags.insert(0, "-fopenmp")
ldflags.insert(0, "-fopenmp")
elif sys.platform == 'darwin':
if os.path.exists('/usr/local/lib/libomp.a'):
- log.info("Adding OpenMP flags...")
+ print("Adding OpenMP flags...")
cxx_flags[0:0] = ["-Xpreprocessor", "-fopenmp"]
ldflags[0:0] = ["-Xpreprocessor", "-fopenmp", "-lomp"]
else:
- log.info("Not adding OpenMP flags, because /usr/local/lib/libomp.a"
- " does not exist. To enable, run `brew install libomp` "
- "or add flags manually.")
+ print("Not adding OpenMP flags, because /usr/local/lib/libomp.a"
+ " does not exist. To enable, run `brew install libomp` "
+ "or add flags manually.")
diff --git a/python/sdist/LICENSE.md b/python/sdist/LICENSE.md
new file mode 120000
index 0000000000..f0608a63ae
--- /dev/null
+++ b/python/sdist/LICENSE.md
@@ -0,0 +1 @@
+../../LICENSE.md
\ No newline at end of file
diff --git a/python/sdist/MANIFEST.in b/python/sdist/MANIFEST.in
index d522f9cf98..f53efb3e68 100644
--- a/python/sdist/MANIFEST.in
+++ b/python/sdist/MANIFEST.in
@@ -9,6 +9,7 @@ include amici/src/hdf5.cpp
include amici/swig/CMakeLists_model.cmake
include setup_clibs.py
include version.txt
+include LICENSE.md
exclude amici/*.so
exclude amici/*.dll
diff --git a/python/sdist/setup.cfg b/python/sdist/setup.cfg
index 8e8fdbaeaf..e37885e29c 100644
--- a/python/sdist/setup.cfg
+++ b/python/sdist/setup.cfg
@@ -27,7 +27,7 @@ package_dir =
amici = amici
python_requires = >=3.7
install_requires =
- sympy>=1.7.1
+ sympy>=1.7.1,<1.9
numpy>=1.14.5; python_version=='3.7'
numpy>=1.17.5; python_version=='3.8'
numpy>=1.19.3; python_version>='3.9'
@@ -37,6 +37,7 @@ install_requires =
pkgconfig
wurlitzer
toposort
+ setuptools>=48
include_package_data = True
zip_safe = False
diff --git a/python/tests/test_preequilibration.py b/python/tests/test_preequilibration.py
index 1129c89857..2af6da888b 100644
--- a/python/tests/test_preequilibration.py
+++ b/python/tests/test_preequilibration.py
@@ -236,7 +236,7 @@ def test_parameter_in_expdata(preeq_fixture):
def test_raise_presimulation_with_adjoints(preeq_fixture):
- """Test data replicates"""
+ """Test simulation failures with adjoin+presimulation"""
model, solver, edata, edata_preeq, \
edata_presim, edata_sim, pscales, plists = preeq_fixture
@@ -248,16 +248,15 @@ def test_raise_presimulation_with_adjoints(preeq_fixture):
rdata = amici.runAmiciSimulation(model, solver, edata)
assert rdata['status'] == amici.AMICI_ERROR
- # presimulation and postequilibration with adjoints:
- # this also needs to fail
+ # add postequilibration
y = edata.getObservedData()
stdy = edata.getObservedDataStdDev()
-
- # add infty timepoint
ts = np.hstack([*edata.getTimepoints(), np.inf])
- edata.setTimepoints(sorted(ts))
+ edata.setTimepoints(ts)
edata.setObservedData(np.hstack([y, y[0]]))
edata.setObservedDataStdDev(np.hstack([stdy, stdy[0]]))
+
+ # remove presimulation
edata.t_presim = 0
edata.fixedParametersPresimulation = ()
@@ -267,7 +266,8 @@ def test_raise_presimulation_with_adjoints(preeq_fixture):
def test_equilibration_methods_with_adjoints(preeq_fixture):
- """Test data replicates"""
+ """Test different combinations of equilibration and simulation
+ sensitivity methods"""
model, solver, edata, edata_preeq, \
edata_presim, edata_sim, pscales, plists = preeq_fixture
diff --git a/python/tests/test_pregenerated_models.py b/python/tests/test_pregenerated_models.py
index 6bd63768b2..470cc8ed86 100755
--- a/python/tests/test_pregenerated_models.py
+++ b/python/tests/test_pregenerated_models.py
@@ -3,19 +3,18 @@
"""Run simulations with Matlab-AMICI pre-generated models and verify using
saved expectations."""
-import h5py
-import amici
import os
-from amici.gradient_check import check_derivatives, check_results
-import pytest
+from pathlib import Path
+import amici
+import h5py
import numpy as np
+import pytest
+from amici.gradient_check import check_derivatives, check_results
-
-options_file = os.path.join(os.path.dirname(__file__), '..', '..',
- 'tests', 'cpputest', 'testOptions.h5')
-expected_results_file = os.path.join(os.path.dirname(__file__), '..', '..',
- 'tests', 'cpputest', 'expectedResults.h5')
+cpp_test_dir = Path(__file__).parents[2] / 'tests' / 'cpp'
+options_file = str(cpp_test_dir / 'testOptions.h5')
+expected_results_file = str(cpp_test_dir / 'expectedResults.h5')
expected_results = h5py.File(expected_results_file, 'r')
model_cases = [(sub_test, case)
@@ -43,10 +42,9 @@ def test_pregenerated_model(sub_test, case):
else:
model_name = sub_test
- model_swig_folder = \
- os.path.join(os.path.dirname(__file__), '..', '..', 'build', 'tests',
- 'cpputest', f'external_{model_name}-prefix',
- 'src', f'external_{model_name}-build', 'swig')
+ model_swig_folder = str(Path(__file__).parents[2] / 'build' / 'tests'
+ / 'cpp' / f'external_{model_name}-prefix' / 'src'
+ / f'external_{model_name}-build' / 'swig')
test_model_module = amici.import_model_module(
module_name=model_name, module_path=model_swig_folder)
@@ -64,7 +62,7 @@ def test_pregenerated_model(sub_test, case):
edata = None
if 'data' in expected_results[sub_test][case].keys():
edata = amici.readSimulationExpData(
- expected_results_file,
+ str(expected_results_file),
f'/{sub_test}/{case}/data', model.get()
)
rdata = amici.runAmiciSimulation(model, solver,
diff --git a/scripts/README.md b/scripts/README.md
index 5434d511b9..d656d9d499 100644
--- a/scripts/README.md
+++ b/scripts/README.md
@@ -13,13 +13,8 @@ This directory contains a number of build, installation, and CI scripts.
* `buildBNGL.sh`
Download and build
- [BioNetGen](https://www.csb.pitt.edu/Faculty/Faeder/?page_id=409) (required for some tests)
-
-* `buildCpputest.sh`
-
- Download and build [CppUTest](https://cpputest.github.io/)
- (required for C++ test suite)
-
+ [BioNetGen](https://github.com/RuleWorld/bionetgen) (required for some tests)
+
* `buildSuiteSparse.sh`
Build [SuiteSparse](http://faculty.cse.tamu.edu/davis/suitesparse.html)
@@ -64,7 +59,7 @@ This directory contains a number of build, installation, and CI scripts.
Run static code analysis
-* `run-cpputest.sh`
+* `run-cpp-tests.sh`
Run C++ unit and integration tests
@@ -90,7 +85,3 @@ This directory contains a number of build, installation, and CI scripts.
Run memory leak check using valgrind for all unit and integration tests.
Assumes they have been built before in the default location.
-
-* `travis_wrap.sh`
-
- Wrapper script for Travis CI to enable output folding in Travis CI logs
diff --git a/scripts/buildAmici.sh b/scripts/buildAmici.sh
index 446fe1d8bd..735e05fd3b 100755
--- a/scripts/buildAmici.sh
+++ b/scripts/buildAmici.sh
@@ -2,7 +2,8 @@
#
# Build libamici
#
-set -e
+set -eou pipefail
+
cmake=${CMAKE:-cmake}
make=${MAKE:-make}
@@ -12,28 +13,26 @@ amici_build_dir="${amici_path}/build"
mkdir -p "${amici_build_dir}"
cd "${amici_build_dir}"
-cpputest_build_dir="${amici_path}/ThirdParty/cpputest-master/build/"
-
-if [[ $TRAVIS = true ]] || [[ $GITHUB_ACTIONS = true ]] || [[ $ENABLE_AMICI_DEBUGGING = TRUE ]];
-then
+if [ "${TRAVIS:-}" = true ] ||
+ [ "${GITHUB_ACTIONS:-}" = true ] ||
+ [ "${ENABLE_AMICI_DEBUGGING:-}" = TRUE ]; then
# Running on CI server
build_type="Debug"
else
build_type="RelWithDebInfo"
fi
-CppUTest_DIR=${cpputest_build_dir} \
- ${cmake} \
- -DCMAKE_CXX_FLAGS="-Wall -Wextra -Werror" \
- -DCMAKE_BUILD_TYPE=$build_type \
- -DPython3_EXECUTABLE="$(command -v python3)" ..
+${cmake} \
+ -DCMAKE_CXX_FLAGS="-Wall -Wextra -Werror" \
+ -DCMAKE_BUILD_TYPE=$build_type \
+ -DPython3_EXECUTABLE="$(command -v python3)" ..
# build, with or without sonarcloud wrapper
-if [[ "$CI_SONARCLOUD" == "TRUE" ]]; then
+if [ "${CI_SONARCLOUD:-}" = "TRUE" ]; then
build-wrapper-linux-x86-64 \
--out-dir "${amici_path}/bw-output" \
cmake --build . --parallel
-elif [[ "$TRAVIS" == "true" ]]; then
+elif [ "${TRAVIS:-}" = "true" ]; then
cmake --build .
${make} python-sdist
else
diff --git a/scripts/buildBNGL.sh b/scripts/buildBNGL.sh
index 6429be7aae..ff2bed8ba2 100755
--- a/scripts/buildBNGL.sh
+++ b/scripts/buildBNGL.sh
@@ -10,12 +10,12 @@ amici_path=$(cd "$script_path/.." && pwd)
mkdir -p "${amici_path}/ThirdParty"
cd "${amici_path}/ThirdParty"
-if [ ! -d "BioNetGen-2.5.2" ]; then
+if [ ! -d "BioNetGen-2.7.0" ]; then
if [ ! -e "bionetgen.tar.gz" ]; then
if [[ "$OSTYPE" == "linux-gnu" || "$OSTYPE" == "linux" ]]; then
- wget -q -O bionetgen.tar.gz https://github.com/RuleWorld/bionetgen/releases/download/BioNetGen-2.5.2/BioNetGen-2.5.2-linux.tgz
+ wget -q -O bionetgen.tar.gz https://github.com/RuleWorld/bionetgen/releases/download/BioNetGen-2.7.0/BioNetGen-2.7.0-linux.tgz
elif [[ "$OSTYPE" == "darwin"* ]]; then
- wget -q -O bionetgen.tar.gz https://github.com/RuleWorld/bionetgen/releases/download/BioNetGen-2.5.2/BioNetGen-2.5.2-mac.tgz
+ wget -q -O bionetgen.tar.gz https://github.com/RuleWorld/bionetgen/releases/download/BioNetGen-2.7.0/BioNetGen-2.7.0-mac.tgz
fi
fi
tar -xf bionetgen.tar.gz
diff --git a/scripts/buildCpputest.sh b/scripts/buildCpputest.sh
deleted file mode 100755
index 1d4a8f468c..0000000000
--- a/scripts/buildCpputest.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-#
-# Build CppUTest
-#
-set -e
-
-script_path=$(dirname "$BASH_SOURCE")
-amici_path=$(cd "$script_path/.." && pwd)
-
-cmake=${CMAKE:-cmake}
-make=${MAKE:-make}
-
-# Cpputest
-mkdir -p "${amici_path}/ThirdParty"
-cd "${amici_path}/ThirdParty"
-export CPPUTEST_BUILD_DIR="${amici_path}/ThirdParty/cpputest-master/"
-
-if [ ! -d "cpputest-master" ]; then
- if [ ! -e "cpputest-master.zip" ]; then
- wget -q -O cpputest-master.zip https://codeload.github.com/cpputest/cpputest/zip/master
- fi
- unzip -q cpputest-master.zip
- #cd cpputest-master/ && ./autogen.sh && ./configure && make
-fi
-
-cd cpputest-master
-mkdir -p build
-cd build
-${cmake} -DTESTS=OFF -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release \
- -DC++11=ON -DMEMORY_LEAK_DETECTION=OFF ..
-${make} -j4
-
diff --git a/scripts/buildDependencies.sh b/scripts/buildDependencies.sh
index 51a971a88d..1caaf0eb48 100755
--- a/scripts/buildDependencies.sh
+++ b/scripts/buildDependencies.sh
@@ -9,5 +9,4 @@ script_path=$(cd "$script_path" && pwd)
"${script_path}/buildSuiteSparse.sh"
"${script_path}/buildSundials.sh"
-"${script_path}/buildCpputest.sh"
"${script_path}/buildBNGL.sh"
diff --git a/scripts/buildXcode.sh b/scripts/buildXcode.sh
index d78edde4bd..97dfbf24e7 100755
--- a/scripts/buildXcode.sh
+++ b/scripts/buildXcode.sh
@@ -12,18 +12,16 @@ CMAKE=${CMAKE:-cmake}
${AMICI_PATH}/scripts/buildSuiteSparse.sh
${AMICI_PATH}/scripts/buildSundials.sh
${AMICI_PATH}/scripts/buildAmici.sh
-${AMICI_PATH}/scripts/buildCpputest.sh
-cp ${AMICI_PATH}/tests/cpputest/expectedResults.h5 ./expectedResults.h5
+cp ${AMICI_PATH}/tests/cpp/expectedResults.h5 ./expectedResults.h5
mkdir -p ${AMICI_PATH}/build_xcode
cd ${AMICI_PATH}/build_xcode
-CPPUTEST_BUILD_DIR=${AMICI_PATH}/ThirdParty/cpputest-master/build/
-CppUTest_DIR=${CPPUTEST_BUILD_DIR} ${CMAKE} -G"Xcode" -DCMAKE_BUILD_TYPE=Debug ..
+${CMAKE} -G"Xcode" -DCMAKE_BUILD_TYPE=Debug ..
for model in steadystate robertson neuron neuron_o2 jakstat_adjoint jakstat_adjoint_o2 dirac events nested_events
do
- cp ${AMICI_PATH}/build/tests/cpputest/external_model_${model}-prefix/src/external_model_${model}-build/libmodel_${model}.a \
- ${AMICI_PATH}/build_xcode/tests/cpputest/external_model_${model}-prefix/src/external_model_${model}-build/libmodel_${model}.a
+ cp ${AMICI_PATH}/build/tests/cpp/external_model_${model}-prefix/src/external_model_${model}-build/libmodel_${model}.a \
+ ${AMICI_PATH}/build_xcode/tests/cpp/external_model_${model}-prefix/src/external_model_${model}-build/libmodel_${model}.a
done
diff --git a/scripts/run-codecov.sh b/scripts/run-codecov.sh
index 3db9f0dd8c..42d76f7a0e 100755
--- a/scripts/run-codecov.sh
+++ b/scripts/run-codecov.sh
@@ -8,7 +8,7 @@ source "${amici_path}"/build/venv/bin/activate
pip install coverage pytest pytest-cov
if [[ -z "${BNGPATH}" ]]; then
- export BNGPATH="${amici_path}"/ThirdParty/BioNetGen-2.5.2
+ export BNGPATH="${amici_path}"/ThirdParty/BioNetGen-2.7.0
fi
pytest \
diff --git a/scripts/run-cpp-tests.sh b/scripts/run-cpp-tests.sh
new file mode 100755
index 0000000000..963ef3c51c
--- /dev/null
+++ b/scripts/run-cpp-tests.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+set -eou pipefail
+
+SCRIPT_PATH=$(dirname "$BASH_SOURCE")
+AMICI_PATH=$(cd "$SCRIPT_PATH/.." && pwd)
+
+if [[ "${ENABLE_GCOV_COVERAGE:-}" == TRUE ]]; then
+ lcov --base-directory "${AMICI_PATH}" \
+ --directory "${AMICI_PATH}/build/CMakeFiles/amici.dir/src" \
+ --zerocounters -q
+fi
+
+# run tests
+cd "${AMICI_PATH}/build"
+
+ctest -V
+ret=$?
+if [[ $ret != 0 ]]; then exit $ret; fi
+mv "${AMICI_PATH}/tests/cpp/writeResults.h5" \
+ "${AMICI_PATH}/tests/cpp/writeResults.h5.bak"
diff --git a/scripts/run-cppcheck.sh b/scripts/run-cppcheck.sh
index 82018977ea..57c669439a 100755
--- a/scripts/run-cppcheck.sh
+++ b/scripts/run-cppcheck.sh
@@ -1,15 +1,18 @@
#!/bin/bash
# Check test suite with valgrind
-# Note: CppuTest memcheck should be disabled
# Note: Consider using ctest -T memcheck instead
+set -euo pipefail
+
SCRIPT_PATH=$(dirname "$BASH_SOURCE")
AMICI_PATH=$(cd "$SCRIPT_PATH"/.. && pwd)
-cd ${AMICI_PATH}
+cd "${AMICI_PATH}"
-cppcheck -i"${AMICI_PATH}"/src/doc "${AMICI_PATH}"/src \
- -I$"{AMICI_PATH}"/include/ \
- --enable=style \
- --exitcode-suppressions="${AMICI_PATH}"/.cppcheck-exitcode-suppressions
+cppcheck \
+ "-i${AMICI_PATH}/src/doc" \
+ "${AMICI_PATH}/src" \
+ "-I${AMICI_PATH}/include/" \
+ --enable=style \
+ "--exitcode-suppressions=${AMICI_PATH}/.cppcheck-exitcode-suppressions"
diff --git a/scripts/run-cpputest.sh b/scripts/run-cpputest.sh
deleted file mode 100755
index 781a31d32f..0000000000
--- a/scripts/run-cpputest.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-SCRIPT_PATH=$(dirname $BASH_SOURCE)
-AMICI_PATH=$(cd $SCRIPT_PATH/.. && pwd)
-
-if [[ "$ENABLE_GCOV_COVERAGE" == TRUE ]]; then lcov --base-directory ${AMICI_PATH} --directory ${AMICI_PATH}/build/CMakeFiles/amici.dir/src --zerocounters -q; fi
-
-# run tests
-cd ${AMICI_PATH}/build
-
-ctest -V
-ret=$?
-if [[ $ret != 0 ]]; then exit $ret; fi
-mv ${AMICI_PATH}/tests/cpputest/writeResults.h5 ${AMICI_PATH}/tests/cpputest/writeResults.h5.bak
diff --git a/scripts/run-python-tests.sh b/scripts/run-python-tests.sh
index 045148c63a..e0b33eb47a 100755
--- a/scripts/run-python-tests.sh
+++ b/scripts/run-python-tests.sh
@@ -7,7 +7,7 @@ amici_path=$(cd "$script_path"/.. && pwd)
set -e
if [[ -z "${BNGPATH}" ]]; then
- export BNGPATH=${amici_path}/ThirdParty/BioNetGen-2.5.2
+ export BNGPATH=${amici_path}/ThirdParty/BioNetGen-2.7.0
fi
cd "${amici_path}"/python/tests
diff --git a/scripts/run-valgrind-cpp.sh b/scripts/run-valgrind-cpp.sh
index af0daa95b9..7d868d98ef 100755
--- a/scripts/run-valgrind-cpp.sh
+++ b/scripts/run-valgrind-cpp.sh
@@ -1,19 +1,19 @@
#!/bin/bash
# Check test suite with valgrind
-# Note: CppuTest memcheck should be disabled
# Note: Consider using ctest -T memcheck instead
-SCRIPT_PATH=$(dirname $BASH_SOURCE)
-AMICI_PATH=$(cd $SCRIPT_PATH/.. && pwd)
+SCRIPT_PATH=$(dirname "$BASH_SOURCE")
+AMICI_PATH=$(cd "$SCRIPT_PATH/.." && pwd)
-set -e
+set -eou pipefail
# run tests
-cd ${AMICI_PATH}/build/tests/cpputest/
+cd "${AMICI_PATH}/build/tests/cpp/"
VALGRIND_OPTS="--leak-check=full --error-exitcode=1 --trace-children=yes --show-leak-kinds=definite"
set -x
for MODEL in $(ctest -N | grep "Test[ ]*#" | grep -v unittests | sed --regexp-extended 's/ *Test[ ]*#[0-9]+: model_(.*)_test/\1/')
- do cd ${AMICI_PATH}/build/tests/cpputest/${MODEL}/ && valgrind ${VALGRIND_OPTS} ./model_${MODEL}_test
+ do cd "${AMICI_PATH}/build/tests/cpp/${MODEL}/" && valgrind ${VALGRIND_OPTS} "./model_${MODEL}_test"
done
-cd ${AMICI_PATH}/build/tests/cpputest/unittests/ && valgrind ${VALGRIND_OPTS} ./unittests
+cd "${AMICI_PATH}/build/tests/cpp/unittests/"
+valgrind ${VALGRIND_OPTS} ./unittests
diff --git a/scripts/run-valgrind-py.sh b/scripts/run-valgrind-py.sh
index 7d43861a2d..241a6bd23e 100755
--- a/scripts/run-valgrind-py.sh
+++ b/scripts/run-valgrind-py.sh
@@ -7,7 +7,7 @@ amici_path=$(cd "$script_path"/.. && pwd)
set -e
if [[ -z "${BNGPATH}" ]]; then
- export BNGPATH=${amici_path}/ThirdParty/BioNetGen-2.5.2
+ export BNGPATH=${amici_path}/ThirdParty/BioNetGen-2.7.0
fi
cd "${amici_path}"/python/tests
diff --git a/scripts/travis_wrap.sh b/scripts/travis_wrap.sh
deleted file mode 100755
index 58314beeac..0000000000
--- a/scripts/travis_wrap.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-# wrapper script to enable code folding on travis-ci
-#
-# Note: Expects travis functions exported in before_script
-#
-# Usage: travis_wrap.sh fold-block-id script [script args]
-# fold-block-id should not contain special characters, blanks newlines, ...
-
-set -e
-travis_time_finish
-travis_fold start "$1"
- travis_time_start
- bash -c "${@:2:$#}"
- travis_time_finish
-travis_fold end "$1"
diff --git a/src/model.ODE_template.cpp b/src/model.ODE_template.cpp
index af1e5845cf..5c9f69b6aa 100644
--- a/src/model.ODE_template.cpp
+++ b/src/model.ODE_template.cpp
@@ -21,6 +21,10 @@ std::array observableNames = {
TPL_OBSERVABLE_NAMES_INITIALIZER_LIST
};
+std::array observableScalings = {
+ TPL_OBSERVABLE_TRAFO_INITIALIZER_LIST
+};
+
std::array expressionNames = {
TPL_EXPRESSION_NAMES_INITIALIZER_LIST
};
diff --git a/src/model.cpp b/src/model.cpp
index 2123be7427..e37b7409db 100644
--- a/src/model.cpp
+++ b/src/model.cpp
@@ -791,6 +791,10 @@ void Model::getObservable(gsl::span y, const realtype t,
writeSlice(derived_state_.y_, y);
}
+ObservableScaling Model::getObservableScaling(int /*iy*/) const {
+ return ObservableScaling::lin;
+}
+
void Model::getObservableSensitivity(gsl::span sy, const realtype t,
const AmiVector &x,
const AmiVectorArray &sx) {
diff --git a/src/model_header.ODE_template.h b/src/model_header.ODE_template.h
index 79eec1d51c..c35d07918a 100644
--- a/src/model_header.ODE_template.h
+++ b/src/model_header.ODE_template.h
@@ -19,6 +19,7 @@ extern std::array parameterNames;
extern std::array fixedParameterNames;
extern std::array stateNames;
extern std::array observableNames;
+extern std::array observableScalings;
extern std::array expressionNames;
extern std::array parameterIds;
extern std::array fixedParameterIds;
@@ -565,6 +566,10 @@ class Model_TPL_MODELNAME : public amici::Model_ODE {
virtual bool hasQuadraticLLH() const override {
return TPL_QUADRATIC_LLH;
}
+
+ virtual ObservableScaling getObservableScaling(int iy) const override {
+ return observableScalings.at(iy);
+ }
};
diff --git a/src/rdata.cpp b/src/rdata.cpp
index df64d76eb0..38fcf8562c 100644
--- a/src/rdata.cpp
+++ b/src/rdata.cpp
@@ -777,8 +777,18 @@ void ReturnData::initializeObjectiveFunction(bool enable_chi2) {
chi2 = 0.0;
}
-static realtype fres(realtype y, realtype my, realtype sigma_y) {
- return (y - my) / sigma_y;
+static realtype fres(realtype y, realtype my, realtype sigma_y,
+ ObservableScaling scale) {
+ switch (scale) {
+ case amici::ObservableScaling::lin:
+ return (y - my) / sigma_y;
+ case amici::ObservableScaling::log:
+ return (std::log(y) - std::log(my)) / sigma_y;
+ case amici::ObservableScaling::log10:
+ return (std::log10(y) - std::log10(my)) / sigma_y;
+ default:
+ throw std::invalid_argument("only lin, log, log10 allowed.");
+ }
}
static realtype fres_error(realtype sigma_y, realtype sigma_offset) {
@@ -800,8 +810,11 @@ void ReturnData::fres(const int it, Model &model, const ExpData &edata) {
int iyt = iy + it * edata.nytrue();
if (!edata.isSetObservedData(it, iy))
continue;
+
res.at(iyt) = amici::fres(y_it.at(iy), observedData[iy],
- sigmay_it.at(iy));
+ sigmay_it.at(iy),
+ model.getObservableScaling(iy));
+
if (sigma_res)
res.at(iyt + nt * nytrue) = fres_error(sigmay_it.at(iy),
sigma_offset);
@@ -821,10 +834,20 @@ void ReturnData::fchi2(const int it, const ExpData &edata) {
}
static realtype fsres(realtype y, realtype sy, realtype my,
- realtype sigma_y, realtype ssigma_y) {
- return (sy - ssigma_y * fres(y, my, sigma_y)) / sigma_y;
+ realtype sigma_y, realtype ssigma_y,
+ ObservableScaling scale) {
+ auto res = fres(y, my, sigma_y, scale);
+ switch (scale) {
+ case amici::ObservableScaling::lin:
+ return (sy - ssigma_y * res) / sigma_y;
+ case amici::ObservableScaling::log:
+ return (sy / y - ssigma_y * res) / sigma_y;
+ case amici::ObservableScaling::log10:
+ return (sy / (y * std::log(10)) - ssigma_y * res) / sigma_y;
+ default:
+ throw std::invalid_argument("only lin, log, log10 allowed.");
+ }
}
-
static realtype fsres_error(realtype sigma_y, realtype ssigma_y,
realtype sigma_offset) {
return ssigma_y / ( fres_error(sigma_y, sigma_offset) * sigma_y);
@@ -851,9 +874,12 @@ void ReturnData::fsres(const int it, Model &model, const ExpData &edata) {
continue;
for (int ip = 0; ip < nplist; ++ip) {
int idx = (iy + it * edata.nytrue()) * nplist + ip;
+
sres.at(idx) = amici::fsres(y_it.at(iy), sy_it.at(iy + ny * ip),
observedData[iy], sigmay_it.at(iy),
- ssigmay_it.at(iy + ny * ip));
+ ssigmay_it.at(iy + ny * ip),
+ model.getObservableScaling(iy));
+
if (sigma_res) {
int idx_res =
(iy + it * edata.nytrue() + edata.nytrue() * edata.nt()) *
@@ -941,18 +967,19 @@ void ReturnData::fFIM(int it, Model &model, const ExpData &edata) {
auto y = y_it.at(iy);
auto m = observedData[iy];
auto s = sigmay_it.at(iy);
+ auto os = model.getObservableScaling(iy);
// auto r = amici::fres(y, m, s);
for (int ip = 0; ip < nplist; ++ip) {
auto dy_i = sy_it.at(iy + ny * ip);
auto ds_i = ssigmay_it.at(iy + ny * ip);
- auto sr_i = amici::fsres(y, dy_i, m, s, ds_i);
+ auto sr_i = amici::fsres(y, dy_i, m, s, ds_i, os);
realtype sre_i = 0.0;
if (sigma_res)
sre_i = amici::fsres_error(s, ds_i, sigma_offset);
for (int jp = 0; jp < nplist; ++jp) {
auto dy_j = sy_it.at(iy + ny * jp);
auto ds_j = ssigmay_it.at(iy + ny * jp);
- auto sr_j = amici::fsres(y, dy_j, m, s, ds_j);
+ auto sr_j = amici::fsres(y, dy_j, m, s, ds_j, os);
FIM.at(ip + nplist * jp) += sr_i*sr_j;
if (sigma_res) {
auto sre_j = amici::fsres_error(s, ds_j, sigma_offset);
diff --git a/src/steadystateproblem.cpp b/src/steadystateproblem.cpp
index 969c5723f8..8c433efcc4 100644
--- a/src/steadystateproblem.cpp
+++ b/src/steadystateproblem.cpp
@@ -198,9 +198,15 @@ void SteadystateProblem::findSteadyStateBySimulation(const Solver *solver,
steady_state_status_[1] = SteadyStateStatus::failed_too_long_simulation;
break;
default:
+ model->app->warningF("AMICI:newton",
+ "AMICI newton method failed: %s\n",
+ ex.what());
steady_state_status_[1] = SteadyStateStatus::failed;
}
- } catch (AmiException const &) {
+ } catch (AmiException const &ex) {
+ model->app->warningF("AMICI:equilibration",
+ "AMICI equilibration failed: %s\n",
+ ex.what());
steady_state_status_[1] = SteadyStateStatus::failed;
}
}
@@ -462,7 +468,7 @@ bool SteadystateProblem::checkConvergence(const Solver *solver,
sx_ = solver->getStateSensitivity(t_);
model->fsxdot(t_, x_, dx_, ip, sx_[ip], dx_, xdot_);
wrms_ = getWrmsNorm(
- x_, xdot_, solver->getAbsoluteToleranceSteadyStateSensi(),
+ sx_[ip], xdot_, solver->getAbsoluteToleranceSteadyStateSensi(),
solver->getRelativeToleranceSteadyStateSensi(), ewt_);
converged = wrms_ < RCONST(1.0);
}
@@ -504,8 +510,8 @@ void SteadystateProblem::applyNewtonsMethod(Model *model,
xdot_old_ = xdot_;
wrms_ = getWrmsNorm(x_newton_, xdot_, newtonSolver->atol_,
- newtonSolver->rtol_, ewt_);
- bool converged = wrms_ < RCONST(1.0);
+ newtonSolver->rtol_, ewt_);
+ bool converged = newton_retry ? false : wrms_ < RCONST(1.0);
while (!converged && i_newtonstep < newtonSolver->max_steps) {
/* If Newton steps are necessary, compute the initial search direction */
@@ -594,7 +600,7 @@ void SteadystateProblem::runSteadystateSimulation(const Solver *solver,
/* Do we also have to check for convergence of sensitivities? */
SensitivityMethod sensitivityFlag = SensitivityMethod::none;
if (solver->getSensitivityOrder() > SensitivityOrder::none &&
- solver->getSensitivityMethod() > SensitivityMethod::none)
+ solver->getSensitivityMethod() == SensitivityMethod::forward)
sensitivityFlag = SensitivityMethod::forward;
/* If flag for forward sensitivity computation by simulation is not set,
disable forward sensitivity integration. Sensitivities will be computed
diff --git a/src/sundials_matrix_wrapper.cpp b/src/sundials_matrix_wrapper.cpp
index 4e080958c6..9fb2fee025 100644
--- a/src/sundials_matrix_wrapper.cpp
+++ b/src/sundials_matrix_wrapper.cpp
@@ -6,7 +6,6 @@
#include // bad_alloc
#include
#include // invalid_argument and domain_error
-#include
namespace amici {
@@ -140,7 +139,7 @@ void SUNMatrixWrapper::reallocate(sunindextype NNZ) {
update_ptrs();
capacity_ = NNZ;
- assert((NNZ && columns() && rows()) ^ !matrix_);
+ assert((NNZ && columns() && rows()) || !matrix_);
}
void SUNMatrixWrapper::realloc() {
@@ -153,24 +152,10 @@ void SUNMatrixWrapper::realloc() {
update_ptrs();
capacity_ = num_nonzeros_;
- assert(capacity() ^ !matrix_);
+ assert(capacity() || !matrix_);
}
-sunindextype SUNMatrixWrapper::rows() const {
- assert(!matrix_ ||
- (matrix_id() == SUNMATRIX_SPARSE ?
- num_rows_ == SM_ROWS_S(matrix_) :
- num_rows_ == SM_ROWS_D(matrix_)));
- return num_rows_;
-}
-sunindextype SUNMatrixWrapper::columns() const {
- assert(!matrix_ ||
- (matrix_id() == SUNMATRIX_SPARSE ?
- num_columns_ == SM_COLUMNS_S(matrix_) :
- num_columns_ == SM_COLUMNS_D(matrix_)));
- return num_columns_;
-}
sunindextype SUNMatrixWrapper::num_indexptrs() const {
assert(matrix_id() == SUNMATRIX_SPARSE);
@@ -195,40 +180,6 @@ sunindextype SUNMatrixWrapper::num_nonzeros() const {
return num_nonzeros_;
}
-realtype SUNMatrixWrapper::get_data(sunindextype idx) const{
- assert(matrix_);
- assert(matrix_id() == SUNMATRIX_SPARSE);
- assert(idx < capacity());
- assert(SM_DATA_S(matrix_) == data_);
- return data_[idx];
-};
-
-realtype SUNMatrixWrapper::get_data(sunindextype irow, sunindextype icol) const{
- assert(matrix_);
- assert(matrix_id() == SUNMATRIX_DENSE);
- assert(irow < rows());
- assert(icol < columns());
- return SM_ELEMENT_D(matrix_, irow, icol);
-};
-
-
-void SUNMatrixWrapper::set_data(sunindextype idx, realtype data) {
- assert(matrix_);
- assert(matrix_id() == SUNMATRIX_SPARSE);
- assert(idx < capacity());
- assert(SM_DATA_S(matrix_) == data_);
- data_[idx] = data;
-}
-
-void SUNMatrixWrapper::set_data(sunindextype irow, sunindextype icol,
- realtype data) {
- assert(matrix_);
- assert(matrix_id() == SUNMATRIX_DENSE);
- assert(irow < rows());
- assert(icol < columns());
- SM_ELEMENT_D(matrix_, irow, icol) = data;
-}
-
const realtype *SUNMatrixWrapper::data() const {
return data_;
}
@@ -237,58 +188,6 @@ realtype *SUNMatrixWrapper::data() {
return data_;
}
-sunindextype SUNMatrixWrapper::get_indexval(sunindextype idx) const {
- assert(matrix_);
- assert(matrix_id() == SUNMATRIX_SPARSE);
- assert(idx < capacity());
- assert(indexvals_ == SM_INDEXVALS_S(matrix_));
- return indexvals_[idx];
-}
-
-void SUNMatrixWrapper::set_indexval(sunindextype idx, sunindextype val) {
- assert(matrix_);
- assert(matrix_id() == SUNMATRIX_SPARSE);
- assert(idx < capacity());
- assert(indexvals_ == SM_INDEXVALS_S(matrix_));
- indexvals_[idx] = val;
-}
-
-void SUNMatrixWrapper::set_indexvals(const gsl::span vals) {
- assert(matrix_);
- assert(matrix_id() == SUNMATRIX_SPARSE);
- assert(static_cast(vals.size()) == capacity());
- assert(indexvals_ == SM_INDEXVALS_S(matrix_));
- std::copy_n(vals.begin(), capacity(), indexvals_);
-}
-
-sunindextype SUNMatrixWrapper::get_indexptr(sunindextype ptr_idx) const {
- assert(matrix_);
- assert(matrix_id() == SUNMATRIX_SPARSE);
- assert(ptr_idx <= num_indexptrs());
- assert(indexptrs_ == SM_INDEXPTRS_S(matrix_));
- return indexptrs_[ptr_idx];
-}
-
-void SUNMatrixWrapper::set_indexptr(sunindextype ptr_idx, sunindextype ptr) {
- assert(matrix_);
- assert(matrix_id() == SUNMATRIX_SPARSE);
- assert(ptr_idx <= num_indexptrs());
- assert(ptr <= capacity());
- assert(indexptrs_ == SM_INDEXPTRS_S(matrix_));
- indexptrs_[ptr_idx] = ptr;
- if (ptr_idx == num_indexptrs())
- num_nonzeros_ = ptr;
-}
-
-void SUNMatrixWrapper::set_indexptrs(const gsl::span ptrs) {
- assert(matrix_);
- assert(matrix_id() == SUNMATRIX_SPARSE);
- assert(static_cast(ptrs.size()) == num_indexptrs() + 1);
- assert(indexptrs_ == SM_INDEXPTRS_S(matrix_));
- std::copy_n(ptrs.begin(), num_indexptrs() + 1, indexptrs_);
- num_nonzeros_ = indexptrs_[num_indexptrs()];
-}
-
int SUNMatrixWrapper::sparsetype() const {
assert(matrix_);
assert(matrix_id() == SUNMATRIX_SPARSE);
@@ -297,7 +196,8 @@ int SUNMatrixWrapper::sparsetype() const {
void SUNMatrixWrapper::scale(realtype a) {
if (matrix_) {
- for (sunindextype idx = 0; idx < capacity(); ++idx)
+ auto cap = capacity();
+ for (sunindextype idx = 0; idx < cap; ++idx)
data_[idx] *= a;
}
}
@@ -382,17 +282,38 @@ void SUNMatrixWrapper::multiply(gsl::span c,
return;
/* Carry out actual multiplication */
- sunindextype idx;
+ auto c_ptr = c.data();
+ auto b_ptr = b.data();
+
if (transpose) {
- for (int icols = 0; icols < (int)cols.size(); ++icols)
- for (idx = get_indexptr(cols.at(icols));
- idx < get_indexptr(cols.at(icols) + 1); ++idx)
- c.at(icols) += get_data(idx) * b.at(get_indexval(idx));
+ auto cols_size = cols.size();
+ for (std::size_t icols = 0; icols < cols_size; ++icols) {
+ auto idx_next_col = get_indexptr(cols.at(icols) + 1);
+ for (sunindextype idx = get_indexptr(cols.at(icols));
+ idx < idx_next_col; ++idx) {
+
+ auto idx_val = get_indexval(idx);
+ assert(icols < c.size());
+ assert(static_cast(idx_val) < b.size());
+
+ c_ptr[icols] += get_data(idx) * b_ptr[idx_val];
+ }
+ }
} else {
- for (sunindextype icols = 0; icols < columns(); ++icols)
- for (idx = get_indexptr(cols.at(icols));
- idx < get_indexptr(cols.at(icols)+1); ++idx)
- c.at(get_indexval(idx)) += get_data(idx) * b.at(icols);
+ auto num_cols = static_cast(columns());
+ for (std::size_t icols = 0; icols < num_cols; ++icols) {
+ auto idx_next_col = get_indexptr(cols.at(icols) + 1);
+
+ for (sunindextype idx = get_indexptr(cols.at(icols));
+ idx < idx_next_col; ++idx) {
+ auto idx_val = get_indexval(idx);
+
+ assert(icols < b.size());
+ assert(static_cast(idx_val) < c.size());
+
+ c_ptr[idx_val] += get_data(idx) * b_ptr[icols];
+ }
+ }
}
}
@@ -512,7 +433,9 @@ void SUNMatrixWrapper::sparse_add(const SUNMatrixWrapper &A, realtype alpha,
nnz);
// no reallocation should happen here
for (cidx = get_indexptr(ccol); cidx < nnz; cidx++) {
- set_data(cidx, x.at(get_indexval(cidx))); // copy data to C
+ auto x_idx = get_indexval(cidx);
+ assert(x_idx >= 0 && static_cast(x_idx) < x.size());
+ set_data(cidx, x[x_idx]); // copy data to C
}
}
set_indexptr(num_indexptrs(), nnz);
@@ -522,7 +445,11 @@ void SUNMatrixWrapper::sparse_add(const SUNMatrixWrapper &A, realtype alpha,
void SUNMatrixWrapper::sparse_sum(const std::vector &mats) {
// matrix_ == nullptr is allowed on the first call
- if (std::all_of(mats.begin(), mats.end(), [](const SUNMatrixWrapper &m){return !m.matrix_;}))
+ auto all_empty = std::all_of(mats.begin(), mats.end(),
+ [](const SUNMatrixWrapper &m){
+ return !m.matrix_;
+ });
+ if (all_empty)
return;
check_csc(this);
@@ -558,13 +485,15 @@ void SUNMatrixWrapper::sparse_sum(const std::vector &mats) {
for (acol = 0; acol < columns(); acol++)
{
- set_indexptr(acol, nnz); /* column j of A starts here */
+ set_indexptr(acol, nnz); /* column j of A starts here */
for (auto & mat : mats)
nnz = mat.scatter(acol, 1.0, w.data(), gsl::make_span(x), acol+1,
this, nnz);
// no reallocation should happen here
for (aidx = get_indexptr(acol); aidx < nnz; aidx++) {
- set_data(aidx, x.at(get_indexval(aidx))); // copy data to C
+ auto x_idx = get_indexval(aidx);
+ assert(x_idx >= 0 && static_cast(x_idx) < x.size());
+ set_data(aidx, x[x_idx]); // copy data to C
}
}
set_indexptr(num_indexptrs(), nnz);
@@ -572,8 +501,6 @@ void SUNMatrixWrapper::sparse_sum(const std::vector &mats) {
realloc(); // resize if necessary
}
-static const std::string scatter_name = "scatter";
-
sunindextype SUNMatrixWrapper::scatter(const sunindextype acol,
const realtype beta,
sunindextype *w,
@@ -597,13 +524,15 @@ sunindextype SUNMatrixWrapper::scatter(const sunindextype acol,
for (aidx = get_indexptr(acol); aidx < get_indexptr(acol+1); aidx++)
{
auto arow = get_indexval(aidx); /* A(arow,acol) is nonzero */
+ assert(arow >= 0 && static_cast(arow) <= x.size());
if (w && w[arow] < mark) {
w[arow] = mark; /* arow is new entry in C(:,*) */
if (C)
C->set_indexval(nnz++, arow); /* add arow to pattern of C(:,*) */
- x.at(arow) = beta * get_data(aidx); /* x(arow) = beta*A(arow,acol) */
- } else
- x.at(arow) += beta * get_data(aidx); /* arow exists in C(:,*) already */
+ x[arow] = beta * get_data(aidx); /* x(arow) = beta*A(arow,acol) */
+ } else {
+ x[arow] += beta * get_data(aidx); /* arow exists in C(:,*) already */
+ }
}
assert(!C || nnz <= C->capacity());
return nnz;
@@ -612,10 +541,9 @@ sunindextype SUNMatrixWrapper::scatter(const sunindextype acol,
// https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/master/CSparse/Source/cs_cumsum.c
/* p [0..n] = cumulative sum of c[0..n-1], and then copy p [0..n-1] into c */
static void cumsum(gsl::span p, std::vector &c) {
- sunindextype i;
sunindextype nz = 0;
assert(p.size() == c.size() + 1);
- for (i = 0; i < static_cast(c.size()); i++)
+ for (sunindextype i = 0; i < static_cast(c.size()); i++)
{
p[i] = nz;
nz += c[i];
@@ -652,45 +580,62 @@ void SUNMatrixWrapper::transpose(SUNMatrixWrapper &C, const realtype alpha,
// see https://github.com/DrTimothyAldenDavis/SuiteSparse/blob/master/CSparse/Source/cs_transpose.c
- std::vector w;
auto nrows = rows();
+
if (C_matrix_id == SUNMATRIX_SPARSE) {
- w = std::vector(columns());
+ std::vector w(columns());
+ auto w_data = w.data();
+
for (sunindextype acol = 0; acol < nrows; acol++) { /* row counts */
auto next_indexptr = get_indexptr(acol+1);
+ auto widx_offset = (acol/blocksize)*blocksize;
for (sunindextype aidx = get_indexptr(acol);
aidx < next_indexptr; aidx++) {
- sunindextype widx = (acol/blocksize)*blocksize + get_indexval(aidx) % blocksize;
+ sunindextype widx = widx_offset + get_indexval(aidx) % blocksize;
assert(widx >= 0 && widx < (sunindextype)w.size());
- w[widx]++;
- assert(w[widx] <= nrows);
+ w_data[widx]++;
+ assert(w_data[widx] <= nrows);
}
}
/* row pointers */
cumsum(gsl::make_span(C.indexptrs_, C.columns()+1), w);
- }
-
- for (sunindextype acol = 0; acol < nrows; acol++)
- {
- auto next_indexptr = get_indexptr(acol+1);
- for (sunindextype aidx = get_indexptr(acol); aidx < next_indexptr; aidx++)
+ for (sunindextype acol = 0; acol < nrows; acol++)
{
- sunindextype ccol = (acol/blocksize)*blocksize + get_indexval(aidx) % blocksize;
- sunindextype crow = (get_indexval(aidx)/blocksize)*blocksize + acol % blocksize;
- assert(crow < nrows);
- assert(ccol < columns());
- if (C_matrix_id == SUNMATRIX_SPARSE) {
+ auto next_indexptr = get_indexptr(acol+1);
+ auto ccol_offset = (acol/blocksize)*blocksize;
+ auto crow_offset = acol % blocksize;
+ for (sunindextype aidx = get_indexptr(acol); aidx < next_indexptr; aidx++)
+ {
+ auto indexval_aidx = get_indexval(aidx);
+ sunindextype ccol = ccol_offset + indexval_aidx % blocksize;
+ sunindextype crow = (indexval_aidx/blocksize)*blocksize + crow_offset;
+ assert(crow < nrows);
+ assert(ccol < columns());
assert(aidx < capacity());
assert(ccol >= 0 && ccol < (sunindextype)w.size());
- sunindextype cidx = w[ccol]++;
+ sunindextype cidx = w_data[ccol]++;
C.set_indexval(cidx, crow); /* place A(i,j) as entry C(j,i) */
C.set_data(cidx, alpha * get_data(aidx));
- } else {
+ }
+ }
+ } else {
+
+ for (sunindextype acol = 0; acol < nrows; acol++)
+ {
+ auto next_indexptr = get_indexptr(acol+1);
+
+ for (sunindextype aidx = get_indexptr(acol); aidx < next_indexptr; aidx++)
+ {
+ sunindextype ccol = (acol/blocksize)*blocksize + get_indexval(aidx) % blocksize;
+ sunindextype crow = (get_indexval(aidx)/blocksize)*blocksize + acol % blocksize;
+ assert(crow < nrows);
+ assert(ccol < columns());
C.set_data(crow, ccol, alpha * get_data(aidx));
}
}
}
+
}
void SUNMatrixWrapper::to_dense(SUNMatrixWrapper &D) const {
diff --git a/swig/amici.i b/swig/amici.i
index 80a7c0d4e8..7253545d60 100644
--- a/swig/amici.i
+++ b/swig/amici.i
@@ -161,6 +161,7 @@ def enum(prefix):
values = {k[len(prefix)+1:]:v for k,v in values.items()}
return IntEnum(prefix, values)
ParameterScaling = enum('ParameterScaling')
+ObservableScaling = enum('ObservableScaling')
SecondOrderMode = enum('SecondOrderMode')
SensitivityOrder = enum('SensitivityOrder')
SensitivityMethod = enum('SensitivityMethod')
diff --git a/tests/README.md b/tests/README.md
index 488d136f05..0758daf484 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -2,7 +2,7 @@
This directory contains:
-- C++ unit tests and integration tests (`cpputest/`)
+- C++ unit tests and integration tests (`cpp/`)
- Scripts for running the SBML semantic test suite, exercising the Python
interface
- Scripts for running the PEtab test suite, exercising the Python interface
diff --git a/tests/cpputest/CMakeLists.txt b/tests/cpp/CMakeLists.txt
similarity index 60%
rename from tests/cpputest/CMakeLists.txt
rename to tests/cpp/CMakeLists.txt
index fa441f3245..6586fb3ec4 100644
--- a/tests/cpputest/CMakeLists.txt
+++ b/tests/cpp/CMakeLists.txt
@@ -1,15 +1,43 @@
-project(amiciIntegrationTests)
+# ------------------------------------------------------------------------------
+# Set up google test
+# ------------------------------------------------------------------------------
+
+# Download and unpack googletest at configure time
+configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
+execute_process(
+ COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download)
+if(result)
+ message(FATAL_ERROR "CMake step for googletest failed: ${result}")
+endif()
+execute_process(
+ COMMAND ${CMAKE_COMMAND} --build .
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download)
+if(result)
+ message(FATAL_ERROR "Build step for googletest failed: ${result}")
+endif()
+
+# Prevent overriding the parent project's compiler/linker settings on Windows
+set(gtest_force_shared_crt
+ ON
+ CACHE BOOL "" FORCE)
-find_package(CppUTest REQUIRED)
-# because Cpputest doesn't seem to care about MEMORY_LEAK_DETECTION=OFF
-add_definitions(-DD_MemoryLeakWarningPlugin_h)
+# Add googletest directly to our build. This defines the gtest and gtest_main
+# targets.
+add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
+ ${CMAKE_CURRENT_BINARY_DIR}/googletest-build EXCLUDE_FROM_ALL)
+
+# ------------------------------------------------------------------------------
+# AMICI C++ tests
+# ------------------------------------------------------------------------------
+
+project(amiciIntegrationTests)
# models depend on Upstream::amici
add_library(Upstream::amici ALIAS amici)
-set(CMAKE_CXX_FLAGS_OLD "${CMAKE_CXX_FLAGS}")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include sstream -include functional")
-
# Amici testing library
add_library(amici-testing testfunctions.cpp)
target_compile_definitions(amici-testing
@@ -19,11 +47,10 @@ target_compile_definitions(amici-testing
)
target_include_directories(amici-testing
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
- PUBLIC ${CppUTest_INCLUDE_DIRS}
)
target_link_libraries(amici-testing
PUBLIC Upstream::amici
- PUBLIC ${CppUTest_LIBRARIES}
+ PUBLIC gtest_main
)
# Names of models for which tests are to be run
diff --git a/tests/cpp/CMakeLists.txt.in b/tests/cpp/CMakeLists.txt.in
new file mode 100644
index 0000000000..6e80530a76
--- /dev/null
+++ b/tests/cpp/CMakeLists.txt.in
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 2.8.12)
+
+project(googletest-download NONE)
+
+include(ExternalProject)
+ExternalProject_Add(googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG master
+ SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
+ BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ TEST_COMMAND ""
+)
diff --git a/tests/cpputest/calvetti/CMakeLists.txt b/tests/cpp/calvetti/CMakeLists.txt
similarity index 67%
rename from tests/cpputest/calvetti/CMakeLists.txt
rename to tests/cpp/calvetti/CMakeLists.txt
index 388fdf9152..5f3db0773e 100644
--- a/tests/cpputest/calvetti/CMakeLists.txt
+++ b/tests/cpp/calvetti/CMakeLists.txt
@@ -2,18 +2,20 @@ get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
project(model_${MODEL_NAME}_test)
set(SRC_LIST
- ../main.cpp
tests1.cpp
)
-include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CppUTest_INCLUDE_DIRS})
-
add_executable(${PROJECT_NAME} ${SRC_LIST})
+
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
add_dependencies(${PROJECT_NAME} external_model_${MODEL_NAME})
target_link_libraries(${PROJECT_NAME}
amici-testing
model_${MODEL_NAME}
+ gtest_main
)
-add_test(NAME ${PROJECT_NAME} COMMAND ./${PROJECT_NAME} -c)
+include(GoogleTest)
+gtest_discover_tests(${PROJECT_NAME})
diff --git a/tests/cpputest/calvetti/tests1.cpp b/tests/cpp/calvetti/tests1.cpp
similarity index 50%
rename from tests/cpputest/calvetti/tests1.cpp
rename to tests/cpp/calvetti/tests1.cpp
index dfde30fd9d..fb093dc5bc 100644
--- a/tests/cpputest/calvetti/tests1.cpp
+++ b/tests/cpp/calvetti/tests1.cpp
@@ -1,13 +1,10 @@
-#include "CppUTest/TestHarness.h"
-#include "CppUTestExt/MockSupport.h"
-
#include "wrapfunctions.h"
#include
#include
-TEST_GROUP(groupCalvetti){};
+#include
-TEST(groupCalvetti, testSimulation)
+TEST(ExampleCalvetti, Simulation)
{
amici::simulateVerifyWrite("/model_calvetti/nosensi/");
}
diff --git a/tests/cpputest/robertson/CMakeLists.txt b/tests/cpp/dirac/CMakeLists.txt
similarity index 67%
rename from tests/cpputest/robertson/CMakeLists.txt
rename to tests/cpp/dirac/CMakeLists.txt
index 388fdf9152..5f3db0773e 100644
--- a/tests/cpputest/robertson/CMakeLists.txt
+++ b/tests/cpp/dirac/CMakeLists.txt
@@ -2,18 +2,20 @@ get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
project(model_${MODEL_NAME}_test)
set(SRC_LIST
- ../main.cpp
tests1.cpp
)
-include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CppUTest_INCLUDE_DIRS})
-
add_executable(${PROJECT_NAME} ${SRC_LIST})
+
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
add_dependencies(${PROJECT_NAME} external_model_${MODEL_NAME})
target_link_libraries(${PROJECT_NAME}
amici-testing
model_${MODEL_NAME}
+ gtest_main
)
-add_test(NAME ${PROJECT_NAME} COMMAND ./${PROJECT_NAME} -c)
+include(GoogleTest)
+gtest_discover_tests(${PROJECT_NAME})
diff --git a/tests/cpputest/dirac/tests1.cpp b/tests/cpp/dirac/tests1.cpp
similarity index 54%
rename from tests/cpputest/dirac/tests1.cpp
rename to tests/cpp/dirac/tests1.cpp
index 89d71f1f9b..d1d79af7fc 100644
--- a/tests/cpputest/dirac/tests1.cpp
+++ b/tests/cpp/dirac/tests1.cpp
@@ -1,19 +1,17 @@
-#include "CppUTest/TestHarness.h"
-#include "CppUTestExt/MockSupport.h"
-
#include
#include "wrapfunctions.h"
#include
-TEST_GROUP(groupDirac){};
+#include
+
-TEST(groupDirac, testSimulation)
+TEST(ExampleDirac, Simulation)
{
amici::simulateVerifyWrite("/model_dirac/nosensi/");
}
-TEST(groupDirac, testSensitivityForward)
+TEST(ExampleDirac, SensitivityForward)
{
amici::simulateVerifyWrite("/model_dirac/sensiforward/");
}
diff --git a/tests/cpputest/dirac/CMakeLists.txt b/tests/cpp/events/CMakeLists.txt
similarity index 54%
rename from tests/cpputest/dirac/CMakeLists.txt
rename to tests/cpp/events/CMakeLists.txt
index 99958ded68..5f3db0773e 100644
--- a/tests/cpputest/dirac/CMakeLists.txt
+++ b/tests/cpp/events/CMakeLists.txt
@@ -2,17 +2,20 @@ get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
project(model_${MODEL_NAME}_test)
set(SRC_LIST
- ../main.cpp
tests1.cpp
)
-include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CppUTest_INCLUDE_DIRS})
-
add_executable(${PROJECT_NAME} ${SRC_LIST})
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_dependencies(${PROJECT_NAME} external_model_${MODEL_NAME})
+
target_link_libraries(${PROJECT_NAME}
amici-testing
model_${MODEL_NAME}
+ gtest_main
)
-add_test(NAME ${PROJECT_NAME} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME} -c)
+include(GoogleTest)
+gtest_discover_tests(${PROJECT_NAME})
diff --git a/tests/cpputest/events/tests1.cpp b/tests/cpp/events/tests1.cpp
similarity index 55%
rename from tests/cpputest/events/tests1.cpp
rename to tests/cpp/events/tests1.cpp
index f5ee53a7fb..a9b1ff8448 100644
--- a/tests/cpputest/events/tests1.cpp
+++ b/tests/cpp/events/tests1.cpp
@@ -1,24 +1,21 @@
-#include "CppUTest/TestHarness.h"
-#include "CppUTestExt/MockSupport.h"
-
#include "testfunctions.h"
#include "wrapfunctions.h"
#include
-TEST_GROUP(groupEvents){};
+#include
-TEST(groupEvents, testDefault)
+TEST(ExampleEvents, Default)
{
amici::simulateWithDefaultOptions();
}
-TEST(groupEvents, testSimulation)
+TEST(ExampleEvents, Simulation)
{
amici::simulateVerifyWrite("/model_events/nosensi/");
}
-TEST(groupEvents, testSensitivityForward)
+TEST(ExampleEvents, SensitivityForward)
{
amici::simulateVerifyWrite("/model_events/sensiforward/");
}
diff --git a/tests/cpputest/expectedResults.h5 b/tests/cpp/expectedResults.h5
similarity index 100%
rename from tests/cpputest/expectedResults.h5
rename to tests/cpp/expectedResults.h5
diff --git a/tests/cpputest/jakstat_adjoint/CMakeLists.txt b/tests/cpp/jakstat_adjoint/CMakeLists.txt
similarity index 54%
rename from tests/cpputest/jakstat_adjoint/CMakeLists.txt
rename to tests/cpp/jakstat_adjoint/CMakeLists.txt
index 3197ed7a72..5f3db0773e 100644
--- a/tests/cpputest/jakstat_adjoint/CMakeLists.txt
+++ b/tests/cpp/jakstat_adjoint/CMakeLists.txt
@@ -2,17 +2,20 @@ get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
project(model_${MODEL_NAME}_test)
set(SRC_LIST
- ../main.cpp
tests1.cpp
)
-include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CppUTest_INCLUDE_DIRS})
-
add_executable(${PROJECT_NAME} ${SRC_LIST})
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_dependencies(${PROJECT_NAME} external_model_${MODEL_NAME})
+
target_link_libraries(${PROJECT_NAME}
amici-testing
model_${MODEL_NAME}
+ gtest_main
)
-add_test(NAME ${PROJECT_NAME} COMMAND ./${PROJECT_NAME} -c)
+include(GoogleTest)
+gtest_discover_tests(${PROJECT_NAME})
diff --git a/tests/cpputest/jakstat_adjoint/tests1.cpp b/tests/cpp/jakstat_adjoint/tests1.cpp
similarity index 84%
rename from tests/cpputest/jakstat_adjoint/tests1.cpp
rename to tests/cpp/jakstat_adjoint/tests1.cpp
index a69ed24a50..2ca15fdf6e 100644
--- a/tests/cpputest/jakstat_adjoint/tests1.cpp
+++ b/tests/cpp/jakstat_adjoint/tests1.cpp
@@ -1,46 +1,43 @@
-#include "CppUTest/TestHarness.h"
-#include "CppUTestExt/MockSupport.h"
-
#include "testfunctions.h"
#include "wrapfunctions.h"
#include
-TEST_GROUP(groupJakstatAdjoint){};
+#include
-TEST(groupJakstatAdjoint, testSimulation)
+TEST(ExampleJakstatAdjoint, Simulation)
{
amici::simulateVerifyWrite("/model_jakstat_adjoint/nosensi/");
}
-TEST(groupJakstatAdjoint, testSensitivityForward)
+TEST(ExampleJakstatAdjoint, SensitivityForward)
{
amici::simulateVerifyWrite("/model_jakstat_adjoint/sensiforward/");
}
-TEST(groupJakstatAdjoint, testSensitivityForwardLogParam)
+TEST(ExampleJakstatAdjoint, SensitivityForwardLogParam)
{
amici::simulateVerifyWrite("/model_jakstat_adjoint/sensiforwardlogparam/");
}
-TEST(groupJakstatAdjoint, testSensitivityAdjoint)
+TEST(ExampleJakstatAdjoint, SensitivityAdjoint)
{
amici::simulateVerifyWrite("/model_jakstat_adjoint/sensiadjoint/");
}
-TEST(groupJakstatAdjoint, testSensitivityForwardEmptySensInd)
+TEST(ExampleJakstatAdjoint, SensitivityForwardEmptySensInd)
{
amici::simulateVerifyWrite(
"/model_jakstat_adjoint/sensiforwardemptysensind/");
}
-TEST(groupJakstatAdjoint, testSensitivityAdjointEmptySensInd)
+TEST(ExampleJakstatAdjoint, SensitivityAdjointEmptySensInd)
{
amici::simulateVerifyWrite(
"/model_jakstat_adjoint/sensiadjointemptysensind/");
}
-IGNORE_TEST(groupJakstatAdjoint, testSensitivityAdjointUnusedNanOutputs)
+TEST(ExampleJakstatAdjoint, DISABLED_SensitivityAdjointUnusedNanOutputs)
{
/* UN-IGNORE ONCE THIS MODEL HAS BEEN IMPORTED VIA PYTHON INTERFACE */
auto model = amici::generic_model::getModel();
@@ -71,10 +68,10 @@ IGNORE_TEST(groupJakstatAdjoint, testSensitivityAdjointUnusedNanOutputs)
auto rdata = runAmiciSimulation(*solver, edata.get(), *model);
for (int i = 0; i < model->nplist(); ++i)
- CHECK_FALSE(std::isnan(rdata->sllh[i]));
+ ASSERT_FALSE(std::isnan(rdata->sllh[i]));
}
-TEST(groupJakstatAdjoint, testSensitivityReplicates)
+TEST(ExampleJakstatAdjoint, SensitivityReplicates)
{
// Check that we can handle replicates correctly
@@ -124,11 +121,11 @@ TEST(groupJakstatAdjoint, testSensitivityReplicates)
solver->setSensitivityMethod(amici::SensitivityMethod::forward);
auto rdata2 = runAmiciSimulation(*solver, &edata, *model);
auto llh2 = rdata2->llh;
- DOUBLES_EQUAL(2.0 * llh1, llh2, 1e-6);
+ ASSERT_NEAR(2.0 * llh1, llh2, 1e-6);
// adjoint + replicates
solver->setSensitivityMethod(amici::SensitivityMethod::adjoint);
auto rdata3 = runAmiciSimulation(*solver, &edata, *model);
auto llh3 = rdata3->llh;
- DOUBLES_EQUAL(llh2, llh3, 1e-6);
+ ASSERT_NEAR(llh2, llh3, 1e-6);
}
diff --git a/tests/cpp/jakstat_adjoint_o2/CMakeLists.txt b/tests/cpp/jakstat_adjoint_o2/CMakeLists.txt
new file mode 100644
index 0000000000..5f3db0773e
--- /dev/null
+++ b/tests/cpp/jakstat_adjoint_o2/CMakeLists.txt
@@ -0,0 +1,21 @@
+get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
+project(model_${MODEL_NAME}_test)
+
+set(SRC_LIST
+ tests1.cpp
+)
+
+add_executable(${PROJECT_NAME} ${SRC_LIST})
+
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_dependencies(${PROJECT_NAME} external_model_${MODEL_NAME})
+
+target_link_libraries(${PROJECT_NAME}
+ amici-testing
+ model_${MODEL_NAME}
+ gtest_main
+)
+
+include(GoogleTest)
+gtest_discover_tests(${PROJECT_NAME})
diff --git a/tests/cpputest/jakstat_adjoint_o2/tests1.cpp b/tests/cpp/jakstat_adjoint_o2/tests1.cpp
similarity index 53%
rename from tests/cpputest/jakstat_adjoint_o2/tests1.cpp
rename to tests/cpp/jakstat_adjoint_o2/tests1.cpp
index 1331c0c369..b03b9da138 100644
--- a/tests/cpputest/jakstat_adjoint_o2/tests1.cpp
+++ b/tests/cpp/jakstat_adjoint_o2/tests1.cpp
@@ -1,24 +1,22 @@
-#include "CppUTest/TestHarness.h"
-#include "CppUTestExt/MockSupport.h"
-
#include "testfunctions.h"
#include "wrapfunctions.h"
#include
-TEST_GROUP(groupJakstatAdjointO2){};
+#include
+
-TEST(groupJakstatAdjointO2, testSensitivityForward2)
+TEST(ExampleJakstatAdjointO2, SensitivityForward2)
{
amici::simulateVerifyWrite("/model_jakstat_adjoint/sensi2forward/");
}
-TEST(groupJakstatAdjointO2, testSensitivityForward2LogParam)
+TEST(ExampleJakstatAdjointO2, SensitivityForward2LogParam)
{
amici::simulateVerifyWrite("/model_jakstat_adjoint/sensi2forwardlogparam/");
}
-TEST(groupJakstatAdjointO2, testSensitivityAdjoint2)
+TEST(ExampleJakstatAdjointO2, SensitivityAdjoint2)
{
amici::simulateVerifyWrite("/model_jakstat_adjoint/sensi2adjoint/");
}
diff --git a/tests/cpp/nested_events/CMakeLists.txt b/tests/cpp/nested_events/CMakeLists.txt
new file mode 100644
index 0000000000..5f3db0773e
--- /dev/null
+++ b/tests/cpp/nested_events/CMakeLists.txt
@@ -0,0 +1,21 @@
+get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
+project(model_${MODEL_NAME}_test)
+
+set(SRC_LIST
+ tests1.cpp
+)
+
+add_executable(${PROJECT_NAME} ${SRC_LIST})
+
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_dependencies(${PROJECT_NAME} external_model_${MODEL_NAME})
+
+target_link_libraries(${PROJECT_NAME}
+ amici-testing
+ model_${MODEL_NAME}
+ gtest_main
+)
+
+include(GoogleTest)
+gtest_discover_tests(${PROJECT_NAME})
diff --git a/tests/cpputest/nested_events/tests1.cpp b/tests/cpp/nested_events/tests1.cpp
similarity index 55%
rename from tests/cpputest/nested_events/tests1.cpp
rename to tests/cpp/nested_events/tests1.cpp
index 7208b713de..7a74f5465b 100644
--- a/tests/cpputest/nested_events/tests1.cpp
+++ b/tests/cpp/nested_events/tests1.cpp
@@ -1,19 +1,16 @@
-#include "CppUTest/TestHarness.h"
-#include "CppUTestExt/MockSupport.h"
-
#include "testfunctions.h"
#include "wrapfunctions.h"
#include
-TEST_GROUP(groupEvents){};
+#include
-TEST(groupEvents, testSimulation)
+TEST(ExampleNestedEvents, Simulation)
{
amici::simulateVerifyWrite("/model_nested_events/nosensi/");
}
-TEST(groupEvents, testSensitivityForward)
+TEST(ExampleNestedEvents, SensitivityForward)
{
amici::simulateVerifyWrite("/model_nested_events/sensiforward/");
}
diff --git a/tests/cpp/neuron/CMakeLists.txt b/tests/cpp/neuron/CMakeLists.txt
new file mode 100644
index 0000000000..5f3db0773e
--- /dev/null
+++ b/tests/cpp/neuron/CMakeLists.txt
@@ -0,0 +1,21 @@
+get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
+project(model_${MODEL_NAME}_test)
+
+set(SRC_LIST
+ tests1.cpp
+)
+
+add_executable(${PROJECT_NAME} ${SRC_LIST})
+
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_dependencies(${PROJECT_NAME} external_model_${MODEL_NAME})
+
+target_link_libraries(${PROJECT_NAME}
+ amici-testing
+ model_${MODEL_NAME}
+ gtest_main
+)
+
+include(GoogleTest)
+gtest_discover_tests(${PROJECT_NAME})
diff --git a/tests/cpputest/neuron/tests1.cpp b/tests/cpp/neuron/tests1.cpp
similarity index 62%
rename from tests/cpputest/neuron/tests1.cpp
rename to tests/cpp/neuron/tests1.cpp
index f5d645b7af..cfdc04ef9c 100644
--- a/tests/cpputest/neuron/tests1.cpp
+++ b/tests/cpp/neuron/tests1.cpp
@@ -1,20 +1,17 @@
-#include "CppUTest/TestHarness.h"
-#include "CppUTestExt/MockSupport.h"
-
#include "testfunctions.h"
#include "wrapfunctions.h"
#include
-TEST_GROUP(groupNeuron){};
+#include
-TEST(groupNeuron, testSimulation)
+TEST(ExampleNeuron, Simulation)
{
amici::simulateVerifyWrite(
"/model_neuron/nosensi/", 100 * TEST_ATOL, 100 * TEST_RTOL);
}
-TEST(groupNeuron, testSensitivityForward)
+TEST(ExampleNeuron, SensitivityForward)
{
amici::simulateVerifyWrite(
"/model_neuron/sensiforward/", 10 * TEST_ATOL, 10 * TEST_RTOL);
diff --git a/tests/cpp/neuron_o2/CMakeLists.txt b/tests/cpp/neuron_o2/CMakeLists.txt
new file mode 100644
index 0000000000..5f3db0773e
--- /dev/null
+++ b/tests/cpp/neuron_o2/CMakeLists.txt
@@ -0,0 +1,21 @@
+get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
+project(model_${MODEL_NAME}_test)
+
+set(SRC_LIST
+ tests1.cpp
+)
+
+add_executable(${PROJECT_NAME} ${SRC_LIST})
+
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_dependencies(${PROJECT_NAME} external_model_${MODEL_NAME})
+
+target_link_libraries(${PROJECT_NAME}
+ amici-testing
+ model_${MODEL_NAME}
+ gtest_main
+)
+
+include(GoogleTest)
+gtest_discover_tests(${PROJECT_NAME})
diff --git a/tests/cpputest/neuron_o2/tests1.cpp b/tests/cpp/neuron_o2/tests1.cpp
similarity index 56%
rename from tests/cpputest/neuron_o2/tests1.cpp
rename to tests/cpp/neuron_o2/tests1.cpp
index 57b9ac47ba..5be0c9ea54 100644
--- a/tests/cpputest/neuron_o2/tests1.cpp
+++ b/tests/cpp/neuron_o2/tests1.cpp
@@ -1,14 +1,11 @@
-#include "CppUTest/TestHarness.h"
-#include "CppUTestExt/MockSupport.h"
-
#include "testfunctions.h"
#include "wrapfunctions.h"
#include
-TEST_GROUP(groupNeuronO2){};
+#include
-TEST(groupNeuronO2, testSensitivity2)
+TEST(ExampleNeuronO2, Sensitivity2)
{
amici::simulateVerifyWrite(
"/model_neuron/sensi2forward/", 10 * TEST_ATOL, 10 * TEST_RTOL);
diff --git a/tests/cpp/robertson/CMakeLists.txt b/tests/cpp/robertson/CMakeLists.txt
new file mode 100644
index 0000000000..5f3db0773e
--- /dev/null
+++ b/tests/cpp/robertson/CMakeLists.txt
@@ -0,0 +1,21 @@
+get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
+project(model_${MODEL_NAME}_test)
+
+set(SRC_LIST
+ tests1.cpp
+)
+
+add_executable(${PROJECT_NAME} ${SRC_LIST})
+
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_dependencies(${PROJECT_NAME} external_model_${MODEL_NAME})
+
+target_link_libraries(${PROJECT_NAME}
+ amici-testing
+ model_${MODEL_NAME}
+ gtest_main
+)
+
+include(GoogleTest)
+gtest_discover_tests(${PROJECT_NAME})
diff --git a/tests/cpputest/robertson/tests1.cpp b/tests/cpp/robertson/tests1.cpp
similarity index 63%
rename from tests/cpputest/robertson/tests1.cpp
rename to tests/cpp/robertson/tests1.cpp
index 441eac0b14..3cb74ada3a 100644
--- a/tests/cpputest/robertson/tests1.cpp
+++ b/tests/cpp/robertson/tests1.cpp
@@ -1,30 +1,27 @@
-#include "CppUTest/TestHarness.h"
-#include "CppUTestExt/MockSupport.h"
-
#include "wrapfunctions.h"
#include
#include
-TEST_GROUP(groupRobertson){};
+#include
-TEST(groupRobertson, testSimulation)
+TEST(ExampleRobertson, Simulation)
{
amici::simulateVerifyWrite("/model_robertson/nosensi/");
}
-TEST(groupRobertson, testSensitivityForward)
+TEST(ExampleRobertson, SensitivityForward)
{
amici::simulateVerifyWrite(
"/model_robertson/sensiforward/", 1e6 * TEST_ATOL, 1e2 * TEST_RTOL);
}
-TEST(groupRobertson, testSensitivityForwardDense)
+TEST(ExampleRobertson, SensitivityForwardDense)
{
amici::simulateVerifyWrite(
"/model_robertson/sensiforwarddense/", 1e6 * TEST_ATOL, 1e2 * TEST_RTOL);
}
-TEST(groupRobertson, testSensitivityForwardSPBCG)
+TEST(ExampleRobertson, SensitivityForwardSPBCG)
{
amici::simulateVerifyWrite(
"/model_robertson/sensiforwardSPBCG/", 1e7 * TEST_ATOL, 1e2 * TEST_RTOL);
diff --git a/tests/cpp/steadystate/CMakeLists.txt b/tests/cpp/steadystate/CMakeLists.txt
new file mode 100644
index 0000000000..5f3db0773e
--- /dev/null
+++ b/tests/cpp/steadystate/CMakeLists.txt
@@ -0,0 +1,21 @@
+get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
+project(model_${MODEL_NAME}_test)
+
+set(SRC_LIST
+ tests1.cpp
+)
+
+add_executable(${PROJECT_NAME} ${SRC_LIST})
+
+target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_dependencies(${PROJECT_NAME} external_model_${MODEL_NAME})
+
+target_link_libraries(${PROJECT_NAME}
+ amici-testing
+ model_${MODEL_NAME}
+ gtest_main
+)
+
+include(GoogleTest)
+gtest_discover_tests(${PROJECT_NAME})
diff --git a/tests/cpputest/steadystate/tests1.cpp b/tests/cpp/steadystate/tests1.cpp
similarity index 64%
rename from tests/cpputest/steadystate/tests1.cpp
rename to tests/cpp/steadystate/tests1.cpp
index 78be0a2adc..505f8f15ed 100644
--- a/tests/cpputest/steadystate/tests1.cpp
+++ b/tests/cpp/steadystate/tests1.cpp
@@ -1,19 +1,16 @@
-#include "CppUTest/TestHarness.h"
-#include "CppUTestExt/MockSupport.h"
-
#include "testfunctions.h"
#include "wrapfunctions.h"
#include
-TEST_GROUP(groupSteadystate){};
+#include
-TEST(groupSteadystate, testDefault)
+TEST(ExampleSteadystate, Default)
{
amici::simulateWithDefaultOptions();
}
-TEST(groupSteadystate, testModelFromHDF5)
+TEST(ExampleSteadystate, ModelFromHDF5)
{
// Test reading some python-written options
std::vector pExp{ 1, 0.5, 0.4, 2, 0.1 };
@@ -25,43 +22,43 @@ TEST(groupSteadystate, testModelFromHDF5)
amici::checkEqualArray(
kExp, model->getFixedParameters(), TEST_ATOL, TEST_RTOL, "k");
- CHECK_EQUAL(51, model->nt());
- CHECK_EQUAL(0.0, model->getTimepoint(0));
- CHECK_EQUAL(100.0, model->getTimepoint(model->nt() - 2));
- CHECK_EQUAL(INFINITY, model->getTimepoint(model->nt() - 1));
+ ASSERT_EQ(51, model->nt());
+ ASSERT_EQ(0.0, model->getTimepoint(0));
+ ASSERT_EQ(100.0, model->getTimepoint(model->nt() - 2));
+ ASSERT_EQ(INFINITY, model->getTimepoint(model->nt() - 1));
for (int i = 0; i < model->np(); ++i) {
- CHECK_EQUAL(pExp[i], model->getUnscaledParameters()[i]);
- CHECK_EQUAL(log10(pExp[i]), model->getParameters()[i]);
+ ASSERT_EQ(pExp[i], model->getUnscaledParameters()[i]);
+ ASSERT_EQ(log10(pExp[i]), model->getParameters()[i]);
}
}
-TEST(groupSteadystate, testInequality)
+TEST(ExampleSteadystate, Inequality)
{
auto modelA = amici::generic_model::getModel();
- auto modelB = std::unique_ptr(new amici::Model_Test());
+ auto modelB = std::make_unique();
- CHECK_FALSE(*modelA == *modelB);
+ ASSERT_FALSE(*modelA == *modelB);
}
-TEST(groupSteadystate, testCopyModel)
+TEST(ExampleSteadystate, CopyModel)
{
auto modelA = amici::generic_model::getModel();
auto modelB = std::unique_ptr(modelA->clone());
- CHECK_TRUE(*modelA == *modelB);
+ ASSERT_EQ(*modelA, *modelB);
}
-TEST(groupSteadystate, testCloneModel)
+TEST(ExampleSteadystate, CloneModel)
{
auto modelA = amici::generic_model::getModel();
- auto modelB = std::unique_ptr(
- new amici::model_model_steadystate::Model_model_steadystate());
+ auto modelB = std::make_unique<
+ amici::model_model_steadystate::Model_model_steadystate>();
- CHECK_TRUE(*modelA == *modelB);
+ ASSERT_EQ(*modelA, *modelB);
}
-TEST(groupSteadystate, testExpDataFromReturnData)
+TEST(ExampleSteadystate, ExpDataFromReturnData)
{
auto model = amici::generic_model::getModel();
auto solver = model->getSolver();
@@ -76,7 +73,7 @@ TEST(groupSteadystate, testExpDataFromReturnData)
runAmiciSimulation(*solver, &edata, *model);
}
-TEST(groupSteadystate, testReuseSolver)
+TEST(ExampleSteadystate, ReuseSolver)
{
auto model = amici::generic_model::getModel();
auto solver = model->getSolver();
@@ -90,7 +87,7 @@ TEST(groupSteadystate, testReuseSolver)
runAmiciSimulation(*solver, nullptr, *model);
}
-TEST(groupSteadystate, testRethrow)
+TEST(ExampleSteadystate, Rethrow)
{
auto model = amici::generic_model::getModel();
auto solver = model->getSolver();
@@ -109,12 +106,12 @@ TEST(groupSteadystate, testRethrow)
runAmiciSimulation(*solver, nullptr, *model);
// must throw
- CHECK_THROWS(amici::IntegrationFailure,
- runAmiciSimulation(*solver, nullptr, *model, true));
+ ASSERT_THROW(runAmiciSimulation(*solver, nullptr, *model, true),
+ amici::IntegrationFailure);
}
-TEST(groupSteadystate, testMaxtime)
+TEST(ExampleSteadystate, Maxtime)
{
auto model = amici::generic_model::getModel();
auto solver = model->getSolver();
@@ -125,100 +122,100 @@ TEST(groupSteadystate, testMaxtime)
NEW_OPTION_FILE, *solver, "/model_steadystate/nosensi/options");
auto rdata = runAmiciSimulation(*solver, nullptr, *model);
- CHECK_EQUAL(amici::AMICI_SUCCESS, rdata->status);
+ ASSERT_EQ(amici::AMICI_SUCCESS, rdata->status);
solver->setMaxTime(0.000001);
// must throw
rdata = runAmiciSimulation(*solver, nullptr, *model);
- CHECK_EQUAL(amici::AMICI_MAX_TIME_EXCEEDED, rdata->status);
+ ASSERT_EQ(amici::AMICI_MAX_TIME_EXCEEDED, rdata->status);
}
-TEST(groupSteadystate, testInitialStatesNonEmpty)
+TEST(ExampleSteadystate, InitialStatesNonEmpty)
{
auto model = amici::generic_model::getModel();
- CHECK_FALSE(model->getInitialStates().empty());
+ ASSERT_FALSE(model->getInitialStates().empty());
}
-TEST(groupSteadystate, testInitialStateSensitivitiesNonEmpty)
+TEST(ExampleSteadystate, InitialStateSensitivitiesNonEmpty)
{
auto model = amici::generic_model::getModel();
- CHECK_FALSE(model->getInitialStateSensitivities().empty());
+ ASSERT_FALSE(model->getInitialStateSensitivities().empty());
}
-TEST(groupSteadystate, testSimulation)
+TEST(ExampleSteadystate, Simulation)
{
amici::simulateVerifyWrite(
"/model_steadystate/nosensi/", 100 * TEST_ATOL, 100 * TEST_RTOL);
}
-TEST(groupSteadystate, testSensitivityForward)
+TEST(ExampleSteadystate, SensitivityForward)
{
amici::simulateVerifyWrite("/model_steadystate/sensiforward/");
}
-TEST(groupSteadystate, testSensitivityForwardPlist)
+TEST(ExampleSteadystate, SensitivityForwardPlist)
{
amici::simulateVerifyWrite("/model_steadystate/sensiforwardplist/");
}
-TEST(groupSteadystate, testSensitivityForwardErrorInt)
+TEST(ExampleSteadystate, SensitivityForwardErrorInt)
{
amici::simulateVerifyWrite("/model_steadystate/sensiforwarderrorint/");
}
-TEST(groupSteadystate, testSensitivityForwardErrorNewt)
+TEST(ExampleSteadystate, SensitivityForwardErrorNewt)
{
amici::simulateVerifyWrite("/model_steadystate/sensiforwarderrornewt/");
}
-TEST(groupSteadystate, testSensitivityForwardDense)
+TEST(ExampleSteadystate, SensitivityForwardDense)
{
amici::simulateVerifyWrite("/model_steadystate/sensiforwarddense/");
}
-TEST(groupSteadystate, testSensitivityForwardSPBCG)
+TEST(ExampleSteadystate, SensitivityForwardSPBCG)
{
amici::simulateVerifyWrite(
"/model_steadystate/nosensiSPBCG/", 10 * TEST_ATOL, 10 * TEST_RTOL);
}
-TEST(groupSteadystate, testSensiFwdNewtonPreeq)
+TEST(ExampleSteadystate, SensiFwdNewtonPreeq)
{
amici::simulateVerifyWrite("/model_steadystate/sensifwdnewtonpreeq/");
}
-TEST(groupSteadystate, testSensiAdjNewtonPreeq)
+TEST(ExampleSteadystate, SensiAdjNewtonPreeq)
{
amici::simulateVerifyWrite("/model_steadystate/sensiadjnewtonpreeq/");
}
-TEST(groupSteadystate, testSensiFwdSimPreeq)
+TEST(ExampleSteadystate, SensiFwdSimPreeq)
{
amici::simulateVerifyWrite("/model_steadystate/sensifwdsimpreeq/");
}
-TEST(groupSteadystate, testSensiFwdSimPreeqFSA)
+TEST(ExampleSteadystate, SensiFwdSimPreeqFSA)
{
amici::simulateVerifyWrite("/model_steadystate/sensifwdsimpreeqFSA/");
}
-TEST(groupSteadystate, testSensiAdjSimPreeq)
+TEST(ExampleSteadystate, SensiAdjSimPreeq)
{
amici::simulateVerifyWrite("/model_steadystate/sensiadjsimpreeq/");
}
-TEST(groupSteadystate, testSensiAdjSimPreeqFSA)
+TEST(ExampleSteadystate, SensiAdjSimPreeqFSA)
{
amici::simulateVerifyWrite("/model_steadystate/sensiadjsimpreeqFSA/");
}
-TEST(groupSteadystate, testSensiFwdByhandPreeq)
+TEST(ExampleSteadystate, SensiFwdByhandPreeq)
{
amici::simulateVerifyWrite("/model_steadystate/sensifwdbyhandpreeq/");
}
-TEST(groupSteadystate, testSensiAdjByhandPreeq)
+TEST(ExampleSteadystate, SensiAdjByhandPreeq)
{
amici::simulateVerifyWrite("/model_steadystate/sensiadjbyhandpreeq/");
}
diff --git a/tests/cpputest/testOptions.h5 b/tests/cpp/testOptions.h5
similarity index 100%
rename from tests/cpputest/testOptions.h5
rename to tests/cpp/testOptions.h5
diff --git a/tests/cpputest/testfunctions.cpp b/tests/cpp/testfunctions.cpp
similarity index 91%
rename from tests/cpputest/testfunctions.cpp
rename to tests/cpp/testfunctions.cpp
index 382e7fa43d..3e03eedfc4 100644
--- a/tests/cpputest/testfunctions.cpp
+++ b/tests/cpp/testfunctions.cpp
@@ -9,8 +9,7 @@
#include
#include
-#include
-#include
+#include "gtest/gtest.h"
namespace amici {
@@ -120,12 +119,12 @@ bool withinTolerance(double expected, double actual, double atol, double rtol, i
void checkEqualArray(std::vector const& expected, std::vector const& actual,
double atol, double rtol, std::string const& name) {
- CHECK_EQUAL(expected.size(), actual.size());
+ ASSERT_EQ(expected.size(), actual.size());
for(int i = 0; (unsigned) i < expected.size(); ++i)
{
bool withinTol = withinTolerance(expected[i], actual[i], atol, rtol, i, name.c_str());
- CHECK_TRUE(withinTol);
+ ASSERT_TRUE(withinTol);
}
}
@@ -135,12 +134,12 @@ void checkEqualArray(const double *expected, const double *actual, const int len
if(!length)
return;
- CHECK_TRUE(expected && actual);
+ ASSERT_TRUE(expected && actual);
for(int i = 0; i < length; ++i)
{
bool withinTol = withinTolerance(expected[i], actual[i], atol, rtol, i, name);
- CHECK_TRUE(withinTol);
+ ASSERT_TRUE(withinTol);
}
}
@@ -148,19 +147,19 @@ void checkEqualArrayStrided(const double *expected, const double *actual, int le
if(!expected && !actual)
return;
- CHECK_TRUE(expected && actual);
+ ASSERT_TRUE(expected && actual);
for(int i = 0; i < length; ++i)
{
bool withinTol = withinTolerance(expected[i * strideExpected], actual[i * strideActual], atol, rtol, i, name);
- CHECK_TRUE(withinTol);
+ ASSERT_TRUE(withinTol);
}
}
void verifyReturnData(std::string const& hdffile, std::string const& resultPath,
const ReturnData *rdata, const Model *model, double atol, double rtol) {
- CHECK_FALSE(rdata == nullptr);
+ ASSERT_FALSE(rdata == nullptr);
if(!hdf5::locationExists(hdffile, resultPath)) {
fprintf(stderr, "ERROR: No results available for %s!\n",
@@ -176,19 +175,19 @@ void verifyReturnData(std::string const& hdffile, std::string const& resultPath,
std::vector expected;
auto statusExp = hdf5::getIntScalarAttribute(file, resultPath, "status");
- CHECK_EQUAL(statusExp, rdata->status);
+ ASSERT_EQ(statusExp, rdata->status);
double llhExp = hdf5::getDoubleScalarAttribute(file, resultPath, "llh");
- CHECK_TRUE(withinTolerance(llhExp, rdata->llh, atol, rtol, 1, "llh"));
+ ASSERT_TRUE(withinTolerance(llhExp, rdata->llh, atol, rtol, 1, "llh"));
double chi2Exp = hdf5::getDoubleScalarAttribute(file, resultPath, "chi2");
- CHECK_TRUE(withinTolerance(chi2Exp, rdata->chi2, atol, rtol, 1, "chi2"));
+ ASSERT_TRUE(withinTolerance(chi2Exp, rdata->chi2, atol, rtol, 1, "chi2"));
if(hdf5::locationExists(file, resultPath + "/x")) {
expected = hdf5::getDoubleDataset2D(file, resultPath + "/x", m, n);
checkEqualArray(expected, rdata->x, atol, rtol, "x");
} else {
- CHECK_TRUE(rdata->x.empty());
+ ASSERT_TRUE(rdata->x.empty());
}
// CHECK_EQUAL(AMICI_O2MODE_FULL, udata->o2mode);
@@ -197,42 +196,42 @@ void verifyReturnData(std::string const& hdffile, std::string const& resultPath,
expected = hdf5::getDoubleDataset2D(file, resultPath + "/diagnosis/J", m, n);
checkEqualArray(expected, rdata->J, atol, rtol, "J");
} else {
- CHECK_TRUE(rdata->J.empty());
+ ASSERT_TRUE(rdata->J.empty());
}
if(hdf5::locationExists(file, resultPath + "/y")) {
expected = hdf5::getDoubleDataset2D(file, resultPath + "/y", m, n);
checkEqualArray(expected, rdata->y, atol, rtol, "y");
} else {
- CHECK_TRUE(rdata->y.empty());
+ ASSERT_TRUE(rdata->y.empty());
}
if(hdf5::locationExists(file, resultPath + "/res")) {
expected = hdf5::getDoubleDataset1D(file, resultPath + "/res");
checkEqualArray(expected, rdata->res, atol, rtol, "res");
} else {
- CHECK_TRUE(rdata->res.empty());
+ ASSERT_TRUE(rdata->res.empty());
}
if(hdf5::locationExists(file, resultPath + "/z")) {
expected = hdf5::getDoubleDataset2D(file, resultPath + "/z", m, n);
checkEqualArray(expected, rdata->z, atol, rtol, "z");
} else {
- CHECK_TRUE(rdata->z.empty());
+ ASSERT_TRUE(rdata->z.empty());
}
if(hdf5::locationExists(file, resultPath + "/rz")) {
expected = hdf5::getDoubleDataset2D(file, resultPath + "/rz", m, n);
checkEqualArray(expected, rdata->rz, atol, rtol, "rz");
} else {
- CHECK_TRUE(rdata->rz.empty());
+ ASSERT_TRUE(rdata->rz.empty());
}
if(hdf5::locationExists(file, resultPath + "/sigmaz")) {
expected = hdf5::getDoubleDataset2D(file, resultPath + "/sigmaz", m, n);
checkEqualArray(expected, rdata->sigmaz, atol, rtol, "sigmaz");
} else {
- CHECK_TRUE(rdata->sigmaz.empty());
+ ASSERT_TRUE(rdata->sigmaz.empty());
}
expected = hdf5::getDoubleDataset1D(file, resultPath + "/diagnosis/xdot");
@@ -244,8 +243,8 @@ void verifyReturnData(std::string const& hdffile, std::string const& resultPath,
if(rdata->sensi >= SensitivityOrder::first) {
verifyReturnDataSensitivities(file, resultPath, rdata, model, atol, rtol);
} else {
- CHECK_EQUAL(0, rdata->sllh.size());
- CHECK_EQUAL(0, rdata->s2llh.size());
+ ASSERT_EQ(0, rdata->sllh.size());
+ ASSERT_EQ(0, rdata->s2llh.size());
}
}
@@ -257,7 +256,7 @@ void verifyReturnDataSensitivities(H5::H5File const& file, std::string const& re
expected = hdf5::getDoubleDataset1D(file, resultPath + "/sllh");
checkEqualArray(expected, rdata->sllh, atol, rtol, "sllh");
} else {
- CHECK_TRUE(rdata->sllh.empty());
+ ASSERT_TRUE(rdata->sllh.empty());
}
if(rdata->sensi_meth == SensitivityMethod::forward) {
@@ -266,21 +265,21 @@ void verifyReturnDataSensitivities(H5::H5File const& file, std::string const& re
expected = hdf5::getDoubleDataset2D(file, resultPath + "/sx0", m, n);
checkEqualArray(expected, rdata->sx0, atol, rtol, "sx0");
} else {
- CHECK_TRUE(rdata->sx0.empty());
+ ASSERT_TRUE(rdata->sx0.empty());
}
if(hdf5::locationExists(file, resultPath + "/sres")) {
expected = hdf5::getDoubleDataset2D(file, resultPath + "/sres", m, n);
checkEqualArray(expected, rdata->sres, atol, rtol, "sres");
} else {
- CHECK_TRUE(rdata->sres.empty());
+ ASSERT_TRUE(rdata->sres.empty());
}
if(hdf5::locationExists(file, resultPath + "/FIM")) {
expected = hdf5::getDoubleDataset2D(file, resultPath + "/FIM", m, n);
checkEqualArray(expected, rdata->FIM, atol, rtol, "FIM");
} else {
- CHECK_TRUE(rdata->FIM.empty());
+ ASSERT_TRUE(rdata->FIM.empty());
}
@@ -295,7 +294,7 @@ void verifyReturnDataSensitivities(H5::H5File const& file, std::string const& re
&rdata->sx[ip * model->nt() * rdata->nx],
model->nt() * model->nxtrue_rdata, atol, rtol, "sx");
} else {
- CHECK_TRUE(rdata->sx.empty());
+ ASSERT_TRUE(rdata->sx.empty());
}
if(hdf5::locationExists(file, resultPath + "/sy")) {
@@ -305,7 +304,7 @@ void verifyReturnDataSensitivities(H5::H5File const& file, std::string const& re
&rdata->sy[ip * model->nt() * model->ny],
model->nt() * model->nytrue, atol, rtol, "sy");
} else {
- CHECK_TRUE(rdata->sy.empty());
+ ASSERT_TRUE(rdata->sy.empty());
}
@@ -349,8 +348,8 @@ void verifyReturnDataSensitivities(H5::H5File const& file, std::string const& re
expected = hdf5::getDoubleDataset2D(file, resultPath + "/s2llh", m, n);
checkEqualArray(expected, rdata->s2llh, atol, rtol, "s2llh");
} else {
- CHECK_EQUAL(0, rdata->s2llh.size());
- CHECK_EQUAL(0, rdata->s2rz.size());
+ ASSERT_EQ(0, rdata->s2llh.size());
+ ASSERT_EQ(0, rdata->s2rz.size());
}
}
diff --git a/tests/cpputest/testfunctions.h b/tests/cpp/testfunctions.h
similarity index 98%
rename from tests/cpputest/testfunctions.h
rename to tests/cpp/testfunctions.h
index 2cb4658220..2d1d0f7325 100644
--- a/tests/cpputest/testfunctions.h
+++ b/tests/cpp/testfunctions.h
@@ -10,12 +10,9 @@
#include
#endif
-#include // make std::ostringstream available (needs to come before TestHarness.h)
+#include
#include
-#include
-#include
-
namespace amici {
class ReturnData;
diff --git a/tests/cpp/unittests/CMakeLists.txt b/tests/cpp/unittests/CMakeLists.txt
new file mode 100644
index 0000000000..5e1f7e68ac
--- /dev/null
+++ b/tests/cpp/unittests/CMakeLists.txt
@@ -0,0 +1,27 @@
+project(unittests)
+
+find_package(Boost COMPONENTS serialization)
+
+set(SRC_LIST
+ testMisc.cpp
+ testExpData.cpp
+)
+
+add_executable(${PROJECT_NAME} ${SRC_LIST})
+
+target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+
+if(Boost_FOUND)
+ target_sources(${PROJECT_NAME} PRIVATE testSerialization.cpp)
+ target_include_directories(${PROJECT_NAME} PRIVATE "${Boost_INCLUDE_DIR}")
+endif()
+target_link_libraries(${PROJECT_NAME}
+ amici-testing
+ Upstream::amici
+ ${Boost_LIBRARIES}
+ gtest_main
+ )
+
+include(GoogleTest)
+
+gtest_discover_tests(${PROJECT_NAME})
diff --git a/tests/cpp/unittests/testExpData.cpp b/tests/cpp/unittests/testExpData.cpp
new file mode 100644
index 0000000000..4cf947341c
--- /dev/null
+++ b/tests/cpp/unittests/testExpData.cpp
@@ -0,0 +1,328 @@
+#include "testfunctions.h"
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+namespace amici {
+namespace generic_model {
+std::unique_ptr getModel();
+} // namespace generic_model
+} // namespace amici
+
+using namespace amici;
+
+namespace {
+
+class ExpDataTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ model->setTimepoints(timepoints);
+ model->setNMaxEvent(nmaxevent);
+ testModel.setTimepoints(timepoints);
+ testModel.setNMaxEvent(nmaxevent);
+ }
+
+ int nx = 1, ny = 2, nz = 3, nmaxevent = 4;
+ std::vector timepoints = { 1, 2, 3, 4 };
+
+ std::unique_ptr model = generic_model::getModel();
+
+ Model_Test testModel = Model_Test(
+ ModelDimensions(
+ nx, // nx_rdata
+ nx, // nxtrue_rdata
+ nx, // nx_solver
+ nx, // nxtrue_solver
+ 0, // nx_solver_reinit
+ 1, // np
+ 3, // nk
+ ny, // ny
+ ny, // nytrue
+ nz, // nz
+ nz, // nztrue
+ nmaxevent, // ne
+ 0, // nJ
+ 0, // nw
+ 0, // ndwdx
+ 0, // ndwdp
+ 0, // dwdw
+ 0, // ndxdotdw
+ {}, // ndJydy
+ 0, // nnz
+ 0, // ubw
+ 0 // lbw
+ ),
+ SimulationParameters(
+ std::vector(3, 0.0),
+ std::vector(1, 0.0),
+ std::vector(2, 1)
+ ),
+ SecondOrderMode::none,
+ std::vector(),
+ std::vector());
+};
+
+TEST_F(ExpDataTest, DefaultConstructable)
+{
+ ExpData edata{};
+ ASSERT_EQ(edata.nytrue(), 0);
+ ASSERT_EQ(edata.nztrue(), 0);
+ ASSERT_EQ(edata.nmaxevent(), 0);
+}
+TEST_F(ExpDataTest, ModelCtor)
+{
+ ExpData edata(model->nytrue, model->nztrue, model->nMaxEvent());
+ ASSERT_EQ(edata.nytrue(), model->nytrue);
+ ASSERT_EQ(edata.nztrue(), model->nztrue);
+ ASSERT_EQ(edata.nmaxevent(), model->nMaxEvent());
+}
+
+TEST_F(ExpDataTest, DimensionCtor)
+{
+ ExpData edata(model->nytrue, model->nztrue, model->nMaxEvent(), timepoints);
+ ASSERT_EQ(edata.nytrue(), model->nytrue);
+ ASSERT_EQ(edata.nztrue(), model->nztrue);
+ ASSERT_EQ(edata.nmaxevent(), model->nMaxEvent());
+ ASSERT_EQ(edata.nt(), model->nt());
+ checkEqualArray(
+ timepoints, edata.getTimepoints(), TEST_ATOL, TEST_RTOL, "ts");
+}
+
+TEST_F(ExpDataTest, MeasurementCtor)
+{
+ std::vector y(ny * timepoints.size(), 0.0);
+ std::vector y_std(ny * timepoints.size(), 0.1);
+ std::vector z(nz * nmaxevent, 0.0);
+ std::vector z_std(nz * nmaxevent, 0.1);
+
+ ExpData edata(testModel.nytrue,
+ testModel.nztrue,
+ testModel.nMaxEvent(),
+ timepoints,
+ y,
+ y_std,
+ z,
+ z_std);
+ ASSERT_EQ(edata.nytrue(), testModel.nytrue);
+ ASSERT_EQ(edata.nztrue(), testModel.nztrue);
+ ASSERT_EQ(edata.nmaxevent(), testModel.nMaxEvent());
+ ASSERT_EQ(edata.nt(), testModel.nt());
+ checkEqualArray(
+ timepoints, edata.getTimepoints(), TEST_ATOL, TEST_RTOL, "ts");
+ checkEqualArray(
+ y, edata.getObservedData(), TEST_ATOL, TEST_RTOL, "observedData");
+ checkEqualArray(y_std,
+ edata.getObservedDataStdDev(),
+ TEST_ATOL,
+ TEST_RTOL,
+ "observedDataStdDev");
+ checkEqualArray(
+ z, edata.getObservedEvents(), TEST_ATOL, TEST_RTOL, "observedEvents");
+ checkEqualArray(z_std,
+ edata.getObservedEventsStdDev(),
+ TEST_ATOL,
+ TEST_RTOL,
+ "observedEventsStdDev");
+
+ ExpData edata_copy(edata);
+ ASSERT_EQ(edata.nytrue(), edata_copy.nytrue());
+ ASSERT_EQ(edata.nztrue(), edata_copy.nztrue());
+ ASSERT_EQ(edata.nmaxevent(), edata_copy.nmaxevent());
+ ASSERT_EQ(edata.nt(), edata_copy.nt());
+ checkEqualArray(edata_copy.getTimepoints(),
+ edata.getTimepoints(),
+ TEST_ATOL,
+ TEST_RTOL,
+ "ts");
+ checkEqualArray(edata_copy.getObservedData(),
+ edata.getObservedData(),
+ TEST_ATOL,
+ TEST_RTOL,
+ "observedData");
+ checkEqualArray(edata_copy.getObservedDataStdDev(),
+ edata.getObservedDataStdDev(),
+ TEST_ATOL,
+ TEST_RTOL,
+ "observedDataStdDev");
+ checkEqualArray(edata_copy.getObservedEvents(),
+ edata.getObservedEvents(),
+ TEST_ATOL,
+ TEST_RTOL,
+ "observedEvents");
+ checkEqualArray(edata_copy.getObservedEventsStdDev(),
+ edata.getObservedEventsStdDev(),
+ TEST_ATOL,
+ TEST_RTOL,
+ "observedEventsStdDev");
+}
+
+TEST_F(ExpDataTest, CopyConstructable)
+{
+ testModel.setTimepoints(timepoints);
+ auto edata = ExpData(testModel);
+ ASSERT_EQ(edata.nytrue(), testModel.nytrue);
+ ASSERT_EQ(edata.nztrue(), testModel.nztrue);
+ ASSERT_EQ(edata.nmaxevent(), testModel.nMaxEvent());
+ ASSERT_EQ(edata.nt(), testModel.nt());
+ checkEqualArray(testModel.getTimepoints(),
+ edata.getTimepoints(),
+ TEST_ATOL,
+ TEST_RTOL,
+ "ts");
+}
+
+TEST_F(ExpDataTest, DimensionChecks)
+{
+ std::vector bad_std(ny, -0.1);
+ std::vector y(ny * timepoints.size(), 0.0);
+ std::vector y_std(ny * timepoints.size(), 0.1);
+ std::vector z(nz * nmaxevent, 0.0);
+ std::vector z_std(nz * nmaxevent, 0.1);
+
+ ASSERT_THROW(ExpData(testModel.nytrue,
+ testModel.nztrue,
+ testModel.nMaxEvent(),
+ timepoints,
+ z,
+ z_std,
+ z,
+ z_std),
+ AmiException);
+
+ ASSERT_THROW(ExpData(testModel.nytrue,
+ testModel.nztrue,
+ testModel.nMaxEvent(),
+ timepoints,
+ z,
+ bad_std,
+ z,
+ z_std),
+ AmiException);
+
+ ExpData edata(testModel);
+
+ std::vector bad_y(ny * timepoints.size() + 1, 0.0);
+ std::vector bad_y_std(ny * timepoints.size() + 1, 0.1);
+ std::vector bad_z(nz * nmaxevent + 1, 0.0);
+ std::vector bad_z_std(nz * nmaxevent + 1, 0.1);
+
+ ASSERT_THROW(edata.setObservedData(bad_y), AmiException);
+ ASSERT_THROW(edata.setObservedDataStdDev(bad_y_std), AmiException);
+ ASSERT_THROW(edata.setObservedEvents(bad_z), AmiException);
+ ASSERT_THROW(edata.setObservedEventsStdDev(bad_y_std), AmiException);
+
+ std::vector bad_single_y(edata.nt() + 1, 0.0);
+ std::vector bad_single_y_std(edata.nt() + 1, 0.1);
+ std::vector bad_single_z(edata.nmaxevent() + 1, 0.0);
+ std::vector bad_single_z_std(edata.nmaxevent() + 1, 0.1);
+
+ ASSERT_THROW(edata.setObservedData(bad_single_y, 0),
+ AmiException);
+ ASSERT_THROW(edata.setObservedDataStdDev(bad_single_y_std, 0),
+ AmiException);
+ ASSERT_THROW(edata.setObservedEvents(bad_single_z, 0),
+ AmiException);
+ ASSERT_THROW(edata.setObservedEventsStdDev(bad_single_y_std, 0),
+ AmiException);
+
+ ASSERT_THROW(edata.setTimepoints(std::vector{ 0.0, 1.0, 0.5 }),
+ AmiException);
+}
+
+TEST_F(ExpDataTest, SettersGetters)
+{
+ ExpData edata(testModel);
+
+ std::vector y(ny * timepoints.size(), 0.0);
+ std::vector y_std(ny * timepoints.size(), 0.1);
+ std::vector z(nz * nmaxevent, 0.0);
+ std::vector z_std(nz * nmaxevent, 0.1);
+
+ edata.setObservedData(y);
+ checkEqualArray(
+ edata.getObservedData(), y, TEST_ATOL, TEST_RTOL, "ObservedData");
+ edata.setObservedDataStdDev(y_std);
+ checkEqualArray(edata.getObservedDataStdDev(),
+ y_std,
+ TEST_ATOL,
+ TEST_RTOL,
+ "ObservedDataStdDev");
+ edata.setObservedEvents(z);
+ checkEqualArray(
+ edata.getObservedEvents(), z, TEST_ATOL, TEST_RTOL, "ObservedEvents");
+ edata.setObservedEventsStdDev(z_std);
+ checkEqualArray(edata.getObservedEventsStdDev(),
+ z_std,
+ TEST_ATOL,
+ TEST_RTOL,
+ "ObservedEventsStdDev");
+
+ std::vector single_y(edata.nt(), 0.0);
+ std::vector single_y_std(edata.nt(), 0.1);
+
+ for (int iy = 0; iy < ny; ++iy) {
+ edata.setObservedData(single_y, iy);
+ edata.setObservedDataStdDev(single_y_std, iy);
+ }
+ ASSERT_THROW(edata.setObservedData(single_y, ny), std::exception);
+ ASSERT_THROW(edata.setObservedData(single_y, -1), std::exception);
+ ASSERT_THROW(edata.setObservedDataStdDev(single_y_std, ny), std::exception);
+ ASSERT_THROW(edata.setObservedDataStdDev(single_y_std, -1), std::exception);
+
+ std::vector single_z(edata.nmaxevent(), 0.0);
+ std::vector single_z_std(edata.nmaxevent(), 0.1);
+
+ for (int iz = 0; iz < nz; ++iz) {
+ edata.setObservedEvents(single_z, iz);
+ edata.setObservedEventsStdDev(single_z_std, iz);
+ }
+
+ ASSERT_THROW(edata.setObservedEvents(single_z, nz), std::exception);
+ ASSERT_THROW(edata.setObservedEvents(single_z, -1), std::exception);
+ ASSERT_THROW(edata.setObservedEventsStdDev(single_z_std, nz),
+ std::exception);
+ ASSERT_THROW(edata.setObservedEventsStdDev(single_z_std, -1),
+ std::exception);
+
+ ASSERT_TRUE(edata.getObservedDataPtr(0));
+ ASSERT_TRUE(edata.getObservedDataStdDevPtr(0));
+ ASSERT_TRUE(edata.getObservedEventsPtr(0));
+ ASSERT_TRUE(edata.getObservedEventsStdDevPtr(0));
+
+ std::vector empty(0, 0.0);
+
+ edata.setObservedData(empty);
+ edata.setObservedDataStdDev(empty);
+ edata.setObservedEvents(empty);
+ edata.setObservedEventsStdDev(empty);
+
+ ASSERT_TRUE(!edata.getObservedDataPtr(0));
+ ASSERT_TRUE(!edata.getObservedDataStdDevPtr(0));
+ ASSERT_TRUE(!edata.getObservedEventsPtr(0));
+ ASSERT_TRUE(!edata.getObservedEventsStdDevPtr(0));
+
+ checkEqualArray(
+ edata.getObservedData(), empty, TEST_ATOL, TEST_RTOL, "ObservedData");
+ checkEqualArray(edata.getObservedDataStdDev(),
+ empty,
+ TEST_ATOL,
+ TEST_RTOL,
+ "ObservedDataStdDev");
+ checkEqualArray(
+ edata.getObservedEvents(), empty, TEST_ATOL, TEST_RTOL, "ObservedEvents");
+ checkEqualArray(edata.getObservedEventsStdDev(),
+ empty,
+ TEST_ATOL,
+ TEST_RTOL,
+ "ObservedEventsStdDev");
+}
+
+} // namespace
diff --git a/tests/cpp/unittests/testMisc.cpp b/tests/cpp/unittests/testMisc.cpp
new file mode 100644
index 0000000000..4106742dbd
--- /dev/null
+++ b/tests/cpp/unittests/testMisc.cpp
@@ -0,0 +1,597 @@
+#include "testfunctions.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+namespace amici {
+namespace generic_model {
+
+std::unique_ptr getModel()
+{
+ return std::make_unique();
+}
+
+} // namespace generic_model
+} // namespace amici
+
+using namespace amici;
+
+namespace {
+
+void
+testSolverGetterSetters(CVodeSolver solver,
+ SensitivityMethod sensi_meth,
+ SensitivityOrder sensi,
+ InternalSensitivityMethod ism,
+ InterpolationType interp,
+ NonlinearSolverIteration iter,
+ LinearMultistepMethod lmm,
+ int steps,
+ int badsteps,
+ double tol,
+ double badtol);
+
+class ModelTest : public ::testing::Test {
+ protected:
+ int nx = 1, ny = 2, nz = 3, nmaxevent = 4;
+ std::vector p{ 1.0 };
+ std::vector k{ 0.5, 0.4, 0.7 };
+ std::vector plist{ 1 };
+ std::vector idlist{ 0 };
+ std::vector z2event{ 0, 0, 0 };
+ Model_Test model = Model_Test(
+ ModelDimensions(
+ nx, // nx_rdata
+ nx, // nxtrue_rdata
+ nx, // nx_solver
+ nx, // nxtrue_solver
+ 0, // nx_solver_reinit
+ static_cast(p.size()), // np
+ static_cast(k.size()), // nk
+ ny, // ny
+ ny, // nytrue
+ nz, // nz
+ nz, // nztrue
+ nmaxevent, // ne
+ 0, // nJ
+ 0, // nw
+ 0, // ndwdx
+ 0, // ndwdp
+ 0, // dwdw
+ 0, // ndxdotdw
+ {}, // ndJydy
+ 0, // nnz
+ 0, // ubw
+ 0 // lbw
+ ),
+ SimulationParameters(k, p, plist),
+ SecondOrderMode::none,
+ idlist,
+ z2event);
+ std::vector unscaled{ NAN };
+};
+
+
+TEST_F(ModelTest, LinScaledParameterIsNotTransformed)
+{
+ model.setParameterScale(ParameterScaling::none);
+
+ ASSERT_EQ(p[0], model.getParameters()[0]);
+}
+
+TEST_F(ModelTest, LogScaledParameterIsTransformed)
+{
+ model.setParameterScale(ParameterScaling::ln);
+
+ ASSERT_NEAR(std::log(p[0]), model.getParameters()[0], 1e-16);
+}
+
+TEST_F(ModelTest, Log10ScaledParameterIsTransformed)
+{
+ model.setParameterScale(ParameterScaling::log10);
+
+ ASSERT_NEAR(std::log10(p[0]), model.getParameters()[0], 1e-16);
+}
+
+TEST_F(ModelTest, ParameterScaleTooShort)
+{
+ std::vector pscale(p.size() - 1,
+ ParameterScaling::log10);
+ ASSERT_THROW(model.setParameterScale(pscale), AmiException);
+
+}
+
+TEST_F(ModelTest, ParameterScaleTooLong)
+{
+ std::vector pscale (p.size() + 1,
+ ParameterScaling::log10);
+ ASSERT_THROW(model.setParameterScale(pscale), AmiException);
+}
+
+TEST_F(ModelTest, UnsortedTimepointsThrow){
+ ASSERT_THROW(model.setTimepoints(std::vector{ 0.0, 1.0, 0.5 }),
+ AmiException);
+}
+
+TEST_F(ModelTest, ParameterNameIdGetterSetter)
+{
+ model.setParameterById("p0", 3.0);
+ ASSERT_NEAR(model.getParameterById("p0"), 3.0, 1e-16);
+ ASSERT_THROW(model.getParameterById("p1"), AmiException);
+ ASSERT_NEAR(
+ model.setParametersByIdRegex("p[\\d]+", 5.0), p.size(), 1e-16);
+ for (const auto& ip : model.getParameters())
+ ASSERT_NEAR(ip, 5.0, 1e-16);
+ ASSERT_THROW(model.setParametersByIdRegex("k[\\d]+", 5.0), AmiException);
+
+ model.setParameterByName("p0", 3.0);
+ ASSERT_NEAR(model.getParameterByName("p0"), 3.0, 1e-16);
+ ASSERT_THROW(model.getParameterByName("p1"), AmiException);
+ ASSERT_NEAR(
+ model.setParametersByNameRegex("p[\\d]+", 5.0), p.size(), 1e-16);
+ for (const auto& ip : model.getParameters())
+ ASSERT_NEAR(ip, 5.0, 1e-16);
+ ASSERT_THROW(model.setParametersByNameRegex("k[\\d]+", 5.0), AmiException);
+
+ model.setFixedParameterById("k0", 3.0);
+ ASSERT_NEAR(model.getFixedParameterById("k0"), 3.0, 1e-16);
+ ASSERT_THROW(model.getFixedParameterById("k4"), AmiException);
+ ASSERT_NEAR(
+ model.setFixedParametersByIdRegex("k[\\d]+", 5.0), k.size(), 1e-16);
+ for (const auto& ik : model.getFixedParameters())
+ ASSERT_NEAR(ik, 5.0, 1e-16);
+ ASSERT_THROW(model.setFixedParametersByIdRegex("p[\\d]+", 5.0), AmiException);
+
+ model.setFixedParameterByName("k0", 3.0);
+ ASSERT_NEAR(model.getFixedParameterByName("k0"), 3.0, 1e-16);
+ ASSERT_THROW(model.getFixedParameterByName("k4"), AmiException);
+ ASSERT_NEAR(
+ model.setFixedParametersByNameRegex("k[\\d]+", 5.0), k.size(), 1e-16);
+ for (const auto& ik : model.getFixedParameters())
+ ASSERT_NEAR(ik, 5.0, 1e-16);
+ ASSERT_THROW(model.setFixedParametersByNameRegex("p[\\d]+", 5.0),
+ AmiException);
+}
+
+TEST_F(ModelTest, ReinitializeFixedParameterInitialStates)
+{
+ ASSERT_THROW(model.setReinitializeFixedParameterInitialStates(true),
+ AmiException);
+ model.setReinitializeFixedParameterInitialStates(false);
+ ASSERT_TRUE(!model.getReinitializeFixedParameterInitialStates());
+ AmiVector x(nx);
+ AmiVectorArray sx(model.np(), nx);
+}
+
+TEST(SymbolicFunctionsTest, Sign)
+{
+ ASSERT_EQ(-1, sign(-2));
+ ASSERT_EQ(0, sign(0));
+ ASSERT_EQ(1, sign(2));
+}
+
+TEST(SymbolicFunctionsTest, Heaviside)
+{
+ ASSERT_EQ(0, heaviside(-1));
+ ASSERT_EQ(1, heaviside(0));
+ ASSERT_EQ(1, heaviside(1));
+}
+
+TEST(SymbolicFunctionsTest, Min)
+{
+ ASSERT_EQ(-1, min(-1, 2, 0));
+ ASSERT_EQ(-2, min(1, -2, 0));
+ ASSERT_TRUE(isNaN(min(getNaN(), getNaN(), 0)));
+ ASSERT_EQ(-1, min(-1, getNaN(), 0));
+ ASSERT_EQ(-1, min(getNaN(), -1, 0));
+}
+
+TEST(SymbolicFunctionsTest, Max)
+{
+ ASSERT_EQ(2, max(-1, 2, 0));
+ ASSERT_EQ(1, max(1, -2, 0));
+ ASSERT_TRUE(isNaN(max(getNaN(), getNaN(), 0)));
+ ASSERT_EQ(-1, max(-1, getNaN(), 0));
+ ASSERT_EQ(-1, max(getNaN(), -1, 0));
+}
+
+TEST(SymbolicFunctionsTest, DMin)
+{
+ ASSERT_EQ(0, Dmin(1, -1, -2, 0));
+ ASSERT_EQ(1, Dmin(1, -1, 2, 0));
+ ASSERT_EQ(1, Dmin(2, -1, -2, 0));
+ ASSERT_EQ(0, Dmin(2, -1, 2, 0));
+}
+
+TEST(SymbolicFunctionsTest, DMax)
+{
+ ASSERT_EQ(1, Dmax(1, -1, -2, 0));
+ ASSERT_EQ(0, Dmax(1, -1, 2, 0));
+ ASSERT_EQ(0, Dmax(2, -1, -2, 0));
+ ASSERT_EQ(1, Dmax(2, -1, 2, 0));
+}
+
+TEST(SymbolicFunctionsTest, pos_pow)
+{
+ ASSERT_EQ(0, pos_pow(-0.1, 3));
+ ASSERT_EQ(pow(0.1, 3), pos_pow(0.1, 3));
+}
+
+TEST(SolverTestBasic, Equality)
+{
+ IDASolver i1, i2;
+ CVodeSolver c1, c2;
+
+ ASSERT_EQ(i1, i2);
+ ASSERT_EQ(c1, c2);
+ ASSERT_FALSE(i1 == c1);
+}
+
+TEST(SolverTestBasic, Clone)
+{
+ IDASolver i1;
+ std::unique_ptr i2(i1.clone());
+ ASSERT_EQ(i1, *i2);
+
+ CVodeSolver c1;
+ std::unique_ptr c2(c1.clone());
+ ASSERT_TRUE(c1 == *c2);
+ ASSERT_FALSE(*i2 == *c2);
+}
+
+TEST(SolverIdasTest, DefaultConstructableAndNotLeaky)
+{
+ IDASolver solver;
+}
+
+
+class SolverTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ tol = 0.01;
+ badtol = -0.01;
+ sensi_meth = SensitivityMethod::adjoint;
+ sensi = SensitivityOrder::first;
+ steps = 1000;
+ badsteps = -1;
+ lmm = LinearMultistepMethod::adams;
+ iter = NonlinearSolverIteration::fixedpoint;
+ ism = InternalSensitivityMethod::staggered1;
+ interp = InterpolationType::polynomial;
+ }
+
+ int nx = 1, ny = 2, nz = 3, ne = 0;
+ double tol, badtol;
+ std::vector timepoints = { 1, 2, 3, 4 };
+
+ std::unique_ptr model = generic_model::getModel();
+ SensitivityMethod sensi_meth;
+ SensitivityOrder sensi;
+ int steps, badsteps;
+ LinearMultistepMethod lmm;
+ NonlinearSolverIteration iter;
+ InternalSensitivityMethod ism;
+ InterpolationType interp;
+
+ Model_Test testModel = Model_Test(
+ ModelDimensions(
+ nx, // nx_rdata
+ nx, // nxtrue_rdata
+ nx, // nx_solver
+ nx, // nxtrue_solver
+ 0, // nx_solver_reinit
+ 1, // np
+ 3, // nk
+ ny, // ny
+ ny, // nytrue
+ nz, // nz
+ nz, // nztrue
+ ne, // ne
+ 0, // nJ
+ 0, // nw
+ 0, // ndwdx
+ 0, // ndwdp
+ 0, // dwdw
+ 0, // ndxdotdw
+ {}, // ndJydy
+ 1, // nnz
+ 0, // ubw
+ 0 // lbw
+ ),
+ SimulationParameters(
+ std::vector(3, 0.0),
+ std::vector(1, 0.0),
+ std::vector(2, 1)
+ ),
+ SecondOrderMode::none,
+ std::vector(0, 0.0),
+ std::vector());
+
+ CVodeSolver solver = CVodeSolver();
+};
+
+TEST_F(SolverTest, SettersGettersNoSetup)
+{
+ testSolverGetterSetters(solver,
+ sensi_meth,
+ sensi,
+ ism,
+ interp,
+ iter,
+ lmm,
+ steps,
+ badsteps,
+ tol,
+ badtol);
+}
+
+TEST_F(SolverTest, SettersGettersWithSetup)
+{
+
+ solver.setSensitivityMethod(sensi_meth);
+ ASSERT_EQ(static_cast(solver.getSensitivityMethod()),
+ static_cast(sensi_meth));
+
+ auto rdata = std::make_unique(solver, testModel);
+ AmiVector x(nx), dx(nx);
+ AmiVectorArray sx(nx, 1), sdx(nx, 1);
+
+ testModel.setInitialStates(std::vector{ 0 });
+
+ solver.setup(0, &testModel, x, dx, sx, sdx);
+
+ testSolverGetterSetters(solver,
+ sensi_meth,
+ sensi,
+ ism,
+ interp,
+ iter,
+ lmm,
+ steps,
+ badsteps,
+ tol,
+ badtol);
+}
+
+void
+testSolverGetterSetters(CVodeSolver solver,
+ SensitivityMethod sensi_meth,
+ SensitivityOrder sensi,
+ InternalSensitivityMethod ism,
+ InterpolationType interp,
+ NonlinearSolverIteration iter,
+ LinearMultistepMethod lmm,
+ int steps,
+ int badsteps,
+ double tol,
+ double badtol)
+{
+
+ solver.setSensitivityMethod(sensi_meth);
+ ASSERT_EQ(static_cast(solver.getSensitivityMethod()),
+ static_cast(sensi_meth));
+
+ solver.setSensitivityOrder(sensi);
+ ASSERT_EQ(static_cast(solver.getSensitivityOrder()),
+ static_cast(sensi));
+
+ solver.setInternalSensitivityMethod(ism);
+ ASSERT_EQ(static_cast(solver.getInternalSensitivityMethod()),
+ static_cast(ism));
+
+ solver.setInterpolationType(interp);
+ ASSERT_EQ(static_cast(solver.getInterpolationType()),
+ static_cast(interp));
+
+ solver.setNonlinearSolverIteration(iter);
+ ASSERT_EQ(static_cast(solver.getNonlinearSolverIteration()),
+ static_cast(iter));
+
+ solver.setLinearMultistepMethod(lmm);
+ ASSERT_EQ(static_cast(solver.getLinearMultistepMethod()),
+ static_cast(lmm));
+
+ solver.setPreequilibration(true);
+ ASSERT_EQ(solver.getPreequilibration(), true);
+
+ solver.setStabilityLimitFlag(true);
+ ASSERT_EQ(solver.getStabilityLimitFlag(), true);
+
+ ASSERT_THROW(solver.setNewtonMaxSteps(badsteps), AmiException);
+ solver.setNewtonMaxSteps(steps);
+ ASSERT_EQ(solver.getNewtonMaxSteps(), steps);
+
+ ASSERT_THROW(solver.setNewtonMaxLinearSteps(badsteps), AmiException);
+ solver.setNewtonMaxLinearSteps(steps);
+ ASSERT_EQ(solver.getNewtonMaxLinearSteps(), steps);
+
+ ASSERT_THROW(solver.setMaxSteps(badsteps), AmiException);
+ solver.setMaxSteps(steps);
+ ASSERT_EQ(solver.getMaxSteps(), steps);
+
+ ASSERT_THROW(solver.setMaxStepsBackwardProblem(badsteps), AmiException);
+ solver.setMaxStepsBackwardProblem(steps);
+ ASSERT_EQ(solver.getMaxStepsBackwardProblem(), steps);
+
+ ASSERT_THROW(solver.setRelativeTolerance(badtol), AmiException);
+ solver.setRelativeTolerance(tol);
+ ASSERT_EQ(solver.getRelativeTolerance(), tol);
+
+ ASSERT_THROW(solver.setAbsoluteTolerance(badtol), AmiException);
+ solver.setAbsoluteTolerance(tol);
+ ASSERT_EQ(solver.getAbsoluteTolerance(), tol);
+
+ ASSERT_THROW(solver.setRelativeToleranceQuadratures(badtol), AmiException);
+ solver.setRelativeToleranceQuadratures(tol);
+ ASSERT_EQ(solver.getRelativeToleranceQuadratures(), tol);
+
+ ASSERT_THROW(solver.setAbsoluteToleranceQuadratures(badtol), AmiException);
+ solver.setAbsoluteToleranceQuadratures(tol);
+ ASSERT_EQ(solver.getAbsoluteToleranceQuadratures(), tol);
+
+ ASSERT_THROW(solver.setRelativeToleranceSteadyState(badtol), AmiException);
+ solver.setRelativeToleranceSteadyState(tol);
+ ASSERT_EQ(solver.getRelativeToleranceSteadyState(), tol);
+
+ ASSERT_THROW(solver.setAbsoluteToleranceSteadyState(badtol), AmiException);
+ solver.setAbsoluteToleranceSteadyState(tol);
+ ASSERT_EQ(solver.getAbsoluteToleranceSteadyState(), tol);
+}
+
+class AmiVectorTest : public ::testing::Test {
+ protected:
+ std::vector vec1{ 1, 2, 4, 3 };
+ std::vector vec2{ 4, 1, 2, 3 };
+ std::vector vec3{ 4, 4, 2, 1 };
+};
+
+TEST_F(AmiVectorTest, Vector)
+{
+ AmiVector av(vec1);
+ N_Vector nvec = av.getNVector();
+ for (int i = 0; i < av.getLength(); ++i)
+ ASSERT_EQ(av.at(i), NV_Ith_S(nvec, i));
+}
+
+TEST_F(AmiVectorTest, VectorArray)
+{
+ AmiVectorArray ava(4, 3);
+ AmiVector av1(vec1), av2(vec2), av3(vec3);
+ std::vector avs{ av1, av2, av3 };
+ for (int i = 0; i < ava.getLength(); ++i)
+ ava[i] = avs.at(i);
+
+ std::vector badLengthVector(13, 0.0);
+ std::vector flattened(12, 0.0);
+
+ ASSERT_THROW(ava.flatten_to_vector(badLengthVector), AmiException);
+ ava.flatten_to_vector(flattened);
+ for (int i = 0; i < ava.getLength(); ++i) {
+ const AmiVector av = ava[i];
+ for (int j = 0; j < av.getLength(); ++j)
+ ASSERT_EQ(flattened.at(i * av.getLength() + j), av.at(j));
+ }
+}
+
+class SunMatrixWrapperTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ A.set_data(0, 0, 0.69);
+ A.set_data(1, 0, 0.32);
+ A.set_data(2, 0, 0.95);
+ A.set_data(0, 1, 0.03);
+ A.set_data(1, 1, 0.44);
+ A.set_data(2, 1, 0.38);
+
+ B.set_indexptr(0, 0);
+ B.set_indexptr(1, 2);
+ B.set_indexptr(2, 4);
+ B.set_indexptr(3, 5);
+ B.set_indexptr(4, 7);
+ B.set_data(0, 3);
+ B.set_data(1, 1);
+ B.set_data(2, 3);
+ B.set_data(3, 7);
+ B.set_data(4, 1);
+ B.set_data(5, 2);
+ B.set_data(6, 9);
+ B.set_indexval(0, 1);
+ B.set_indexval(1, 3);
+ B.set_indexval(2, 0);
+ B.set_indexval(3, 2);
+ B.set_indexval(4, 0);
+ B.set_indexval(5, 1);
+ B.set_indexval(6, 3);
+ }
+
+ //inputs
+ std::vector a{0.82, 0.91, 0.13};
+ std::vector b{0.77, 0.80};
+ SUNMatrixWrapper A = SUNMatrixWrapper(3, 2);
+ SUNMatrixWrapper B = SUNMatrixWrapper(4, 4, 7, CSC_MAT);
+ // result
+ std::vector d{1.3753, 1.5084, 1.1655};
+};
+
+TEST_F(SunMatrixWrapperTest, SparseMultiply)
+{
+
+ auto A_sparse = SUNMatrixWrapper(A, 0.0, CSC_MAT);
+ auto c(a); //copy c
+ A_sparse.multiply(c, b);
+ checkEqualArray(d, c, TEST_ATOL, TEST_RTOL, "multiply");
+}
+
+TEST_F(SunMatrixWrapperTest, SparseMultiplyEmpty)
+{
+ // Ensure empty Matrix vector multiplication succeeds
+ auto A_sparse = SUNMatrixWrapper(1, 1, 0, CSR_MAT);
+ std::vector b {0.1};
+ std::vector c {0.1};
+ A_sparse.multiply(c, b);
+ ASSERT_TRUE(c[0] == 0.1);
+
+ A_sparse = SUNMatrixWrapper(1, 1, 0, CSC_MAT);
+ A_sparse.multiply(c, b);
+ ASSERT_TRUE(c[0] == 0.1);
+}
+
+TEST_F(SunMatrixWrapperTest, DenseMultiply)
+{
+ auto c(a); //copy c
+ A.multiply(c, b);
+ checkEqualArray(d, c, TEST_ATOL, TEST_RTOL, "multiply");
+}
+
+TEST_F(SunMatrixWrapperTest, StdVectorCtor)
+{
+ auto b_amivector = AmiVector(b);
+ auto a_amivector = AmiVector(a);
+}
+
+TEST_F(SunMatrixWrapperTest, TransformThrows)
+{
+ ASSERT_THROW(SUNMatrixWrapper(A, 0.0, 13), std::invalid_argument);
+ auto A_sparse = SUNMatrixWrapper(A, 0.0, CSR_MAT);
+ ASSERT_THROW(SUNMatrixWrapper(A_sparse, 0.0, CSR_MAT),
+ std::invalid_argument);
+}
+
+TEST_F(SunMatrixWrapperTest, BlockTranspose)
+{
+ SUNMatrixWrapper B_sparse(4, 4, 7, CSR_MAT);
+ ASSERT_THROW(B.transpose(B_sparse, 1.0, 4), std::domain_error);
+
+ B_sparse = SUNMatrixWrapper(4, 4, 7, CSC_MAT);
+ B.transpose(B_sparse, -1.0, 2);
+ for (int idx = 0; idx < 7; idx++) {
+ ASSERT_EQ(SM_INDEXVALS_S(B.get())[idx],
+ SM_INDEXVALS_S(B_sparse.get())[idx]);
+ if (idx == 1) {
+ ASSERT_EQ(SM_DATA_S(B.get())[idx],
+ -SM_DATA_S(B_sparse.get())[3]);
+ } else if (idx == 3) {
+ ASSERT_EQ(SM_DATA_S(B.get())[idx],
+ -SM_DATA_S(B_sparse.get())[1]);
+ } else {
+ ASSERT_EQ(SM_DATA_S(B.get())[idx],
+ -SM_DATA_S(B_sparse.get())[idx]);
+ }
+ }
+ for (int icol = 0; icol <= 4; icol++)
+ ASSERT_EQ(SM_INDEXPTRS_S(B.get())[icol],
+ SM_INDEXPTRS_S(B_sparse.get())[icol]);
+}
+
+} // namespace
diff --git a/tests/cpp/unittests/testSerialization.cpp b/tests/cpp/unittests/testSerialization.cpp
new file mode 100644
index 0000000000..15c8df9d84
--- /dev/null
+++ b/tests/cpp/unittests/testSerialization.cpp
@@ -0,0 +1,257 @@
+#include
+#include
+#include
+
+#include "testfunctions.h"
+
+#include
+
+#include
+
+void
+checkReturnDataEqual(amici::ReturnData const& r, amici::ReturnData const& s)
+{
+ ASSERT_EQ(r.np, s.np);
+ ASSERT_EQ(r.nk, s.nk);
+ ASSERT_EQ(r.nx, s.nx);
+ ASSERT_EQ(r.nxtrue, s.nxtrue);
+ ASSERT_EQ(r.nx_solver, s.nx_solver);
+ ASSERT_EQ(r.nx_solver_reinit, s.nx_solver_reinit);
+ ASSERT_EQ(r.ny, s.ny);
+ ASSERT_EQ(r.nytrue, s.nytrue);
+ ASSERT_EQ(r.nz, s.nz);
+ ASSERT_EQ(r.nztrue, s.nztrue);
+ ASSERT_EQ(r.ne, s.ne);
+ ASSERT_EQ(r.nJ, s.nJ);
+ ASSERT_EQ(r.nplist, s.nplist);
+ ASSERT_EQ(r.nmaxevent, s.nmaxevent);
+ ASSERT_EQ(r.nt, s.nt);
+ ASSERT_EQ(r.newton_maxsteps, s.newton_maxsteps);
+ ASSERT_EQ(r.pscale, s.pscale);
+ ASSERT_EQ(static_cast(r.o2mode), static_cast(s.o2mode));
+ ASSERT_EQ(static_cast(r.sensi), static_cast(s.sensi));
+ ASSERT_EQ(static_cast(r.sensi_meth), static_cast(s.sensi_meth));
+
+ using amici::checkEqualArray;
+ checkEqualArray(r.ts, s.ts, 1e-16, 1e-16, "ts");
+ checkEqualArray(r.xdot, s.xdot, 1e-16, 1e-16, "xdot");
+ checkEqualArray(r.J, s.J, 1e-16, 1e-16, "J");
+ checkEqualArray(r.z, s.z, 1e-16, 1e-16, "z");
+ checkEqualArray(r.sigmaz, s.sigmaz, 1e-16, 1e-16, "sigmaz");
+ checkEqualArray(r.sz, s.sz, 1e-16, 1e-16, "sz");
+ checkEqualArray(r.ssigmaz, s.ssigmaz, 1e-16, 1e-16, "ssigmaz");
+ checkEqualArray(r.rz, s.rz, 1e-16, 1e-16, "rz");
+ checkEqualArray(r.srz, s.srz, 1e-16, 1e-16, "srz");
+ checkEqualArray(r.s2rz, s.s2rz, 1e-16, 1e-16, "s2rz");
+ checkEqualArray(r.x, s.x, 1e-16, 1e-16, "x");
+ checkEqualArray(r.sx, s.sx, 1e-16, 1e-16, "sx");
+
+ checkEqualArray(r.y, s.y, 1e-16, 1e-16, "y");
+ checkEqualArray(r.sigmay, s.sigmay, 1e-16, 1e-16, "sigmay");
+ checkEqualArray(r.sy, s.sy, 1e-16, 1e-16, "sy");
+ checkEqualArray(r.ssigmay, s.ssigmay, 1e-16, 1e-16, "ssigmay");
+
+ ASSERT_EQ(r.numsteps, s.numsteps);
+ ASSERT_EQ(r.numstepsB, s.numstepsB);
+ ASSERT_EQ(r.numrhsevals, s.numrhsevals);
+ ASSERT_EQ(r.numrhsevalsB, s.numrhsevalsB);
+ ASSERT_EQ(r.numerrtestfails, s.numerrtestfails);
+ ASSERT_EQ(r.numerrtestfailsB, s.numerrtestfailsB);
+ ASSERT_EQ(r.numnonlinsolvconvfails, s.numnonlinsolvconvfails);
+ ASSERT_EQ(r.numnonlinsolvconvfailsB, s.numnonlinsolvconvfailsB);
+ ASSERT_EQ(r.order, s.order);
+ ASSERT_EQ(r.cpu_time, s.cpu_time);
+ ASSERT_EQ(r.cpu_timeB, s.cpu_timeB);
+
+ ASSERT_EQ(r.preeq_status, s.preeq_status);
+ ASSERT_TRUE(r.preeq_t == s.preeq_t ||
+ (std::isnan(r.preeq_t) && std::isnan(s.preeq_t)));
+ ASSERT_TRUE(r.preeq_wrms == s.preeq_wrms ||
+ (std::isnan(r.preeq_wrms) && std::isnan(s.preeq_wrms)));
+ ASSERT_EQ(r.preeq_numsteps, s.preeq_numsteps);
+ ASSERT_EQ(r.preeq_numlinsteps, s.preeq_numlinsteps);
+ EXPECT_NEAR(r.preeq_cpu_time, s.preeq_cpu_time, 1e-16);
+
+ ASSERT_EQ(r.posteq_status, s.posteq_status);
+ ASSERT_TRUE(r.posteq_t == s.posteq_t ||
+ (std::isnan(r.posteq_t) && std::isnan(s.posteq_t)));
+ ASSERT_TRUE(r.posteq_wrms == s.posteq_wrms ||
+ (std::isnan(r.posteq_wrms) && std::isnan(s.posteq_wrms)));
+ ASSERT_EQ(r.posteq_numsteps, s.posteq_numsteps);
+ ASSERT_EQ(r.posteq_numlinsteps, s.posteq_numlinsteps);
+ EXPECT_NEAR(r.posteq_cpu_time, s.posteq_cpu_time, 1e-16);
+
+ checkEqualArray(r.x0, s.x0, 1e-16, 1e-16, "x0");
+ checkEqualArray(r.sx0, s.sx0, 1e-16, 1e-16, "sx0");
+
+ ASSERT_TRUE(r.llh == s.llh || (std::isnan(r.llh) && std::isnan(s.llh)));
+ ASSERT_TRUE(r.chi2 == s.chi2 || (std::isnan(r.llh) && std::isnan(s.llh)));
+ ASSERT_EQ(r.status, s.status);
+
+ checkEqualArray(r.sllh, s.sllh, 1e-5, 1e-5, "sllh");
+ checkEqualArray(r.s2llh, s.s2llh, 1e-5, 1e-5, "s2llh");
+}
+
+class SolverSerializationTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ // set non-default values for all members
+ solver.setAbsoluteTolerance(1e-4);
+ solver.setRelativeTolerance(1e-5);
+ solver.setAbsoluteToleranceQuadratures(1e-6);
+ solver.setRelativeToleranceQuadratures(1e-7);
+ solver.setAbsoluteToleranceSteadyState(1e-8);
+ solver.setRelativeToleranceSteadyState(1e-9);
+ solver.setSensitivityMethod(amici::SensitivityMethod::adjoint);
+ solver.setSensitivityOrder(amici::SensitivityOrder::second);
+ solver.setMaxSteps(1e1);
+ solver.setMaxStepsBackwardProblem(1e2);
+ solver.setNewtonMaxSteps(1e3);
+ solver.setNewtonMaxLinearSteps(1e4);
+ solver.setPreequilibration(true);
+ solver.setStateOrdering(static_cast(amici::SUNLinSolKLU::StateOrdering::COLAMD));
+ solver.setInterpolationType(amici::InterpolationType::polynomial);
+ solver.setStabilityLimitFlag(false);
+ solver.setLinearSolver(amici::LinearSolver::dense);
+ solver.setLinearMultistepMethod(amici::LinearMultistepMethod::adams);
+ solver.setNonlinearSolverIteration(amici::NonlinearSolverIteration::newton);
+ solver.setInternalSensitivityMethod(amici::InternalSensitivityMethod::staggered);
+ solver.setReturnDataReportingMode(amici::RDataReporting::likelihood);
+ }
+
+ amici::CVodeSolver solver;
+};
+
+TEST(ModelSerializationTest, ToFile)
+{
+ int np = 1;
+ int nk = 2;
+ int nx = 3;
+ int ny = 4;
+ int nz = 5;
+ int ne = 6;
+ amici::CVodeSolver solver;
+ amici::Model_Test m = amici::Model_Test(
+ amici::ModelDimensions(
+ nx, // nx_rdata
+ nx, // nxtrue_rdata
+ nx, // nx_solver
+ nx, // nxtrue_solver
+ 0, // nx_solver_reinit
+ np, // np
+ nk, // nk
+ ny, // ny
+ ny, // nytrue
+ nz, // nz
+ nz, // nztrue
+ ne, // ne
+ 0, // nJ
+ 9, // nw
+ 2, // ndwdx
+ 2, // ndwdp
+ 2, // dwdw
+ 13, // ndxdotdw
+ {}, // ndJydy
+ 15, // nnz
+ 16, // ubw
+ 17 // lbw
+ ),
+ amici::SimulationParameters(
+ std::vector(nk, 0.0),
+ std::vector(np, 0.0),
+ std::vector(np, 0)
+ ),
+ amici::SecondOrderMode::none,
+ std::vector(nx, 0.0),
+ std::vector(nz, 0));
+
+ {
+ std::ofstream ofs("sstore.dat");
+ boost::archive::text_oarchive oar(ofs);
+ // oar & static_cast(solver);
+ oar& static_cast(m);
+ }
+ {
+ std::ifstream ifs("sstore.dat");
+ boost::archive::text_iarchive iar(ifs);
+ amici::CVodeSolver v;
+ amici::Model_Test n;
+ // iar &static_cast(v);
+ iar& static_cast(n);
+ // CHECK_TRUE(solver == v);
+ ASSERT_EQ(m, n);
+ }
+}
+
+TEST(ReturnDataSerializationTest, ToString)
+{
+ int np = 1;
+ int nk = 2;
+ int nx = 3;
+ int ny = 4;
+ int nz = 5;
+ int ne = 6;
+ amici::CVodeSolver solver;
+ amici::Model_Test m = amici::Model_Test(
+ amici::ModelDimensions(
+ nx, // nx_rdata
+ nx, // nxtrue_rdata
+ nx, // nx_solver
+ nx, // nxtrue_solver
+ 0, // nx_solver_reinit
+ np, // np
+ nk, // nk
+ ny, // ny
+ ny, // nytrue
+ nz, // nz
+ nz, // nztrue
+ ne, // ne
+ 0, // nJ
+ 9, // nw
+ 10, // ndwdx
+ 2, // ndwdp
+ 12, // dwdw
+ 13, // ndxdotdw
+ {}, // ndJydy
+ 15, // nnz
+ 16, // ubw
+ 17 // lbw
+ ),
+ amici::SimulationParameters(
+ std::vector(nk, 0.0),
+ std::vector(np, 0.0),
+ std::vector(np, 0)
+ ),
+ amici::SecondOrderMode::none,
+ std::vector(nx, 0.0),
+ std::vector(nz, 0));
+
+ amici::ReturnData r(solver, m);
+
+ std::string serialized = amici::serializeToString(r);
+
+ checkReturnDataEqual(
+ r, amici::deserializeFromString(serialized));
+}
+
+TEST_F(SolverSerializationTest, ToChar)
+{
+ int length;
+ char* buf = amici::serializeToChar(solver, &length);
+
+ amici::CVodeSolver v =
+ amici::deserializeFromChar(buf, length);
+
+ delete[] buf;
+ ASSERT_EQ(solver, v);
+}
+
+TEST_F(SolverSerializationTest, ToStdVec)
+{
+
+ auto buf = amici::serializeToStdVec(solver);
+ amici::CVodeSolver v =
+ amici::deserializeFromChar(buf.data(), buf.size());
+
+ ASSERT_EQ(solver, v);
+}
diff --git a/tests/cpputest/wrapTestModels.m b/tests/cpp/wrapTestModels.m
similarity index 100%
rename from tests/cpputest/wrapTestModels.m
rename to tests/cpp/wrapTestModels.m
diff --git a/tests/cpputest/events/CMakeLists.txt b/tests/cpputest/events/CMakeLists.txt
deleted file mode 100644
index 3197ed7a72..0000000000
--- a/tests/cpputest/events/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
-project(model_${MODEL_NAME}_test)
-
-set(SRC_LIST
- ../main.cpp
- tests1.cpp
-)
-
-include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CppUTest_INCLUDE_DIRS})
-
-add_executable(${PROJECT_NAME} ${SRC_LIST})
-
-target_link_libraries(${PROJECT_NAME}
- amici-testing
- model_${MODEL_NAME}
-)
-
-add_test(NAME ${PROJECT_NAME} COMMAND ./${PROJECT_NAME} -c)
diff --git a/tests/cpputest/jakstat_adjoint_o2/CMakeLists.txt b/tests/cpputest/jakstat_adjoint_o2/CMakeLists.txt
deleted file mode 100644
index 3197ed7a72..0000000000
--- a/tests/cpputest/jakstat_adjoint_o2/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
-project(model_${MODEL_NAME}_test)
-
-set(SRC_LIST
- ../main.cpp
- tests1.cpp
-)
-
-include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CppUTest_INCLUDE_DIRS})
-
-add_executable(${PROJECT_NAME} ${SRC_LIST})
-
-target_link_libraries(${PROJECT_NAME}
- amici-testing
- model_${MODEL_NAME}
-)
-
-add_test(NAME ${PROJECT_NAME} COMMAND ./${PROJECT_NAME} -c)
diff --git a/tests/cpputest/main.cpp b/tests/cpputest/main.cpp
deleted file mode 100644
index e588e8ed53..0000000000
--- a/tests/cpputest/main.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#include
-#include
-
-#include "CppUTest/CommandLineTestRunner.h"
-#include "CppUTest/TestHarness.h"
-
-int main(int argc, char** argv)
-{
- return CommandLineTestRunner::RunAllTests(argc, argv);
-}
diff --git a/tests/cpputest/nested_events/CMakeLists.txt b/tests/cpputest/nested_events/CMakeLists.txt
deleted file mode 100644
index 3197ed7a72..0000000000
--- a/tests/cpputest/nested_events/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
-project(model_${MODEL_NAME}_test)
-
-set(SRC_LIST
- ../main.cpp
- tests1.cpp
-)
-
-include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CppUTest_INCLUDE_DIRS})
-
-add_executable(${PROJECT_NAME} ${SRC_LIST})
-
-target_link_libraries(${PROJECT_NAME}
- amici-testing
- model_${MODEL_NAME}
-)
-
-add_test(NAME ${PROJECT_NAME} COMMAND ./${PROJECT_NAME} -c)
diff --git a/tests/cpputest/neuron/CMakeLists.txt b/tests/cpputest/neuron/CMakeLists.txt
deleted file mode 100644
index 3197ed7a72..0000000000
--- a/tests/cpputest/neuron/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
-project(model_${MODEL_NAME}_test)
-
-set(SRC_LIST
- ../main.cpp
- tests1.cpp
-)
-
-include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CppUTest_INCLUDE_DIRS})
-
-add_executable(${PROJECT_NAME} ${SRC_LIST})
-
-target_link_libraries(${PROJECT_NAME}
- amici-testing
- model_${MODEL_NAME}
-)
-
-add_test(NAME ${PROJECT_NAME} COMMAND ./${PROJECT_NAME} -c)
diff --git a/tests/cpputest/neuron_o2/CMakeLists.txt b/tests/cpputest/neuron_o2/CMakeLists.txt
deleted file mode 100644
index 3197ed7a72..0000000000
--- a/tests/cpputest/neuron_o2/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
-project(model_${MODEL_NAME}_test)
-
-set(SRC_LIST
- ../main.cpp
- tests1.cpp
-)
-
-include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CppUTest_INCLUDE_DIRS})
-
-add_executable(${PROJECT_NAME} ${SRC_LIST})
-
-target_link_libraries(${PROJECT_NAME}
- amici-testing
- model_${MODEL_NAME}
-)
-
-add_test(NAME ${PROJECT_NAME} COMMAND ./${PROJECT_NAME} -c)
diff --git a/tests/cpputest/steadystate/CMakeLists.txt b/tests/cpputest/steadystate/CMakeLists.txt
deleted file mode 100644
index 3197ed7a72..0000000000
--- a/tests/cpputest/steadystate/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-get_filename_component(MODEL_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
-project(model_${MODEL_NAME}_test)
-
-set(SRC_LIST
- ../main.cpp
- tests1.cpp
-)
-
-include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CppUTest_INCLUDE_DIRS})
-
-add_executable(${PROJECT_NAME} ${SRC_LIST})
-
-target_link_libraries(${PROJECT_NAME}
- amici-testing
- model_${MODEL_NAME}
-)
-
-add_test(NAME ${PROJECT_NAME} COMMAND ./${PROJECT_NAME} -c)
diff --git a/tests/cpputest/unittests/CMakeLists.txt b/tests/cpputest/unittests/CMakeLists.txt
deleted file mode 100644
index 376a2dbe96..0000000000
--- a/tests/cpputest/unittests/CMakeLists.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-project(unittests)
-
-# cannot mix CppuTest new override togeter with Boost
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_OLD}")
-find_package(Boost COMPONENTS serialization)
-
-set(SRC_LIST
- ../main.cpp
- tests1.cpp
-)
-
-if(Boost_FOUND)
- set(SRC_LIST ${SRC_LIST} testsSerialization.cpp)
- include_directories("${Boost_INCLUDE_DIR}")
-endif()
-
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-
-add_executable(${PROJECT_NAME} ${SRC_LIST})
-
-target_link_libraries(${PROJECT_NAME}
- amici-testing
- Upstream::amici
- ${Boost_LIBRARIES}
- )
-
-add_test(NAME unittests COMMAND ./unittests -c)
diff --git a/tests/cpputest/unittests/tests1.cpp b/tests/cpputest/unittests/tests1.cpp
deleted file mode 100644
index 869c6dc9fd..0000000000
--- a/tests/cpputest/unittests/tests1.cpp
+++ /dev/null
@@ -1,911 +0,0 @@
-#include "testfunctions.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-
-#include "CppUTest/TestHarness.h"
-#include "CppUTestExt/MockSupport.h"
-
-namespace amici {
-namespace generic_model {
-
-std::unique_ptr getModel()
-{
- return std::make_unique();
-}
-
-} // namespace generic_model
-} // namespace amici
-
-using namespace amici;
-
-void
-testSolverGetterSetters(CVodeSolver solver,
- SensitivityMethod sensi_meth,
- SensitivityOrder sensi,
- InternalSensitivityMethod ism,
- InterpolationType interp,
- NonlinearSolverIteration iter,
- LinearMultistepMethod lmm,
- int steps,
- int badsteps,
- double tol,
- double badtol);
-
-TEST_GROUP(amici){};
-
-TEST_GROUP(model)
-{
- int nx = 1, ny = 2, nz = 3, nmaxevent = 4;
- std::vector p{ 1.0 };
- std::vector k{ 0.5, 0.4, 0.7 };
- std::vector plist{ 1 };
- std::vector idlist{ 0 };
- std::vector z2event{ 0, 0, 0 };
- Model_Test model = Model_Test(
- ModelDimensions(
- nx, // nx_rdata
- nx, // nxtrue_rdata
- nx, // nx_solver
- nx, // nxtrue_solver
- 0, // nx_solver_reinit
- static_cast(p.size()), // np
- static_cast(k.size()), // nk
- ny, // ny
- ny, // nytrue
- nz, // nz
- nz, // nztrue
- nmaxevent, // ne
- 0, // nJ
- 0, // nw
- 0, // ndwdx
- 0, // ndwdp
- 0, // dwdw
- 0, // ndxdotdw
- {}, // ndJydy
- 0, // nnz
- 0, // ubw
- 0 // lbw
- ),
- SimulationParameters(k, p, plist),
- SecondOrderMode::none,
- idlist,
- z2event);
-
- std::vector unscaled{ NAN };
-};
-
-TEST(model, testScalingLin)
-{
- model.setParameterScale(ParameterScaling::none);
-
- CHECK_EQUAL(p[0], model.getParameters()[0]);
-}
-
-TEST(model, testScalingLog)
-{
- model.setParameterScale(ParameterScaling::ln);
-
- DOUBLES_EQUAL(std::log(p[0]), model.getParameters()[0], 1e-16);
-}
-
-TEST(model, testScalingLog10)
-{
- model.setParameterScale(ParameterScaling::log10);
-
- DOUBLES_EQUAL(std::log10(p[0]), model.getParameters()[0], 1e-16);
-}
-
-TEST(model, testParameterScalingLengthMismatch)
-{
- // too short
- auto pscale =
- std::vector(p.size() - 1, ParameterScaling::log10);
- CHECK_THROWS(AmiException, model.setParameterScale(pscale));
-
- // too long
- pscale =
- std::vector(p.size() + 1, ParameterScaling::log10);
- CHECK_THROWS(AmiException, model.setParameterScale(pscale));
-}
-
-TEST(model, testSetTimepoints){
- CHECK_THROWS(AmiException,
- model.setTimepoints(std::vector{ 0.0, 1.0, 0.5 }));
-}
-
-TEST(model, testNameIdGetterSetter)
-{
- model.setParameterById("p0", 3.0);
- DOUBLES_EQUAL(model.getParameterById("p0"), 3.0, 1e-16);
- CHECK_THROWS(AmiException, model.getParameterById("p1"));
- DOUBLES_EQUAL(
- model.setParametersByIdRegex("p[\\d]+", 5.0), p.size(), 1e-16);
- for (const auto& ip : model.getParameters())
- DOUBLES_EQUAL(ip, 5.0, 1e-16);
- CHECK_THROWS(AmiException, model.setParametersByIdRegex("k[\\d]+", 5.0));
-
- model.setParameterByName("p0", 3.0);
- DOUBLES_EQUAL(model.getParameterByName("p0"), 3.0, 1e-16);
- CHECK_THROWS(AmiException, model.getParameterByName("p1"));
- DOUBLES_EQUAL(
- model.setParametersByNameRegex("p[\\d]+", 5.0), p.size(), 1e-16);
- for (const auto& ip : model.getParameters())
- DOUBLES_EQUAL(ip, 5.0, 1e-16);
- CHECK_THROWS(AmiException, model.setParametersByNameRegex("k[\\d]+", 5.0));
-
- model.setFixedParameterById("k0", 3.0);
- DOUBLES_EQUAL(model.getFixedParameterById("k0"), 3.0, 1e-16);
- CHECK_THROWS(AmiException, model.getFixedParameterById("k4"));
- DOUBLES_EQUAL(
- model.setFixedParametersByIdRegex("k[\\d]+", 5.0), k.size(), 1e-16);
- for (const auto& ik : model.getFixedParameters())
- DOUBLES_EQUAL(ik, 5.0, 1e-16);
- CHECK_THROWS(AmiException,
- model.setFixedParametersByIdRegex("p[\\d]+", 5.0));
-
- model.setFixedParameterByName("k0", 3.0);
- DOUBLES_EQUAL(model.getFixedParameterByName("k0"), 3.0, 1e-16);
- CHECK_THROWS(AmiException, model.getFixedParameterByName("k4"));
- DOUBLES_EQUAL(
- model.setFixedParametersByNameRegex("k[\\d]+", 5.0), k.size(), 1e-16);
- for (const auto& ik : model.getFixedParameters())
- DOUBLES_EQUAL(ik, 5.0, 1e-16);
- CHECK_THROWS(AmiException,
- model.setFixedParametersByNameRegex("p[\\d]+", 5.0));
-}
-
-TEST(model, reinitializeFixedParameterInitialStates)
-{
- CHECK_THROWS(AmiException,
- model.setReinitializeFixedParameterInitialStates(true));
- model.setReinitializeFixedParameterInitialStates(false);
- CHECK_TRUE(!model.getReinitializeFixedParameterInitialStates());
- AmiVector x(nx);
- AmiVectorArray sx(model.np(), nx);
-}
-
-TEST_GROUP(symbolicFunctions){};
-
-TEST(symbolicFunctions, testSign)
-{
- CHECK_EQUAL(-1, sign(-2));
- CHECK_EQUAL(0, sign(0));
- CHECK_EQUAL(1, sign(2));
-}
-
-TEST(symbolicFunctions, testHeaviside)
-{
- CHECK_EQUAL(0, heaviside(-1));
- CHECK_EQUAL(1, heaviside(0));
- CHECK_EQUAL(1, heaviside(1));
-}
-
-TEST(symbolicFunctions, testMin)
-{
- CHECK_EQUAL(-1, amici::min(-1, 2, 0));
- CHECK_EQUAL(-2, amici::min(1, -2, 0));
- CHECK_TRUE(amici::isNaN(amici::min(amici::getNaN(), amici::getNaN(), 0)));
- CHECK_EQUAL(-1, amici::min(-1, amici::getNaN(), 0));
- CHECK_EQUAL(-1, amici::min(amici::getNaN(), -1, 0));
-}
-
-TEST(symbolicFunctions, testMax)
-{
- CHECK_EQUAL(2, amici::max(-1, 2, 0));
- CHECK_EQUAL(1, amici::max(1, -2, 0));
- CHECK_TRUE(amici::isNaN(amici::max(amici::getNaN(), amici::getNaN(), 0)));
- CHECK_EQUAL(-1, amici::max(-1, amici::getNaN(), 0));
- CHECK_EQUAL(-1, amici::max(amici::getNaN(), -1, 0));
-}
-
-TEST(symbolicFunctions, testDMin)
-{
- CHECK_EQUAL(0, amici::Dmin(1, -1, -2, 0));
- CHECK_EQUAL(1, amici::Dmin(1, -1, 2, 0));
- CHECK_EQUAL(1, amici::Dmin(2, -1, -2, 0));
- CHECK_EQUAL(0, amici::Dmin(2, -1, 2, 0));
-}
-
-TEST(symbolicFunctions, testDMax)
-{
- CHECK_EQUAL(1, amici::Dmax(1, -1, -2, 0));
- CHECK_EQUAL(0, amici::Dmax(1, -1, 2, 0));
- CHECK_EQUAL(0, amici::Dmax(2, -1, -2, 0));
- CHECK_EQUAL(1, amici::Dmax(2, -1, 2, 0));
-}
-
-TEST(symbolicFunctions, testpos_pow)
-{
- CHECK_EQUAL(0, amici::pos_pow(-0.1, 3));
- CHECK_EQUAL(pow(0.1, 3), amici::pos_pow(0.1, 3));
-}
-
-TEST_GROUP(amiciSolver){ };
-
-TEST(amiciSolver, testEquality)
-{
- IDASolver i1, i2;
- CVodeSolver c1, c2;
-
- CHECK_TRUE(i1 == i2);
- CHECK_TRUE(c1 == c2);
- CHECK_FALSE(i1 == c1);
-}
-
-TEST(amiciSolver, testClone)
-{
- IDASolver i1;
- auto i2 = std::unique_ptr(i1.clone());
- CHECK_TRUE(i1 == *i2);
-
- CVodeSolver c1;
- auto c2 = std::unique_ptr(c1.clone());
- CHECK_TRUE(c1 == *c2);
- CHECK_FALSE(*i2 == *c2);
-}
-
-TEST_GROUP(amiciSolverIdas){};
-
-TEST(amiciSolverIdas, testConstructionDestruction)
-{
- IDASolver solver;
-}
-
-TEST_GROUP(edata)
-{
- int nx = 1, ny = 2, nz = 3, nmaxevent = 4;
- std::vector timepoints = { 1, 2, 3, 4 };
-
- std::unique_ptr model = amici::generic_model::getModel();
-
- Model_Test testModel = Model_Test(
- ModelDimensions(
- nx, // nx_rdata
- nx, // nxtrue_rdata
- nx, // nx_solver
- nx, // nxtrue_solver
- 0, // nx_solver_reinit
- 1, // np
- 3, // nk
- ny, // ny
- ny, // nytrue
- nz, // nz
- nz, // nztrue
- nmaxevent, // ne
- 0, // nJ
- 0, // nw
- 0, // ndwdx
- 0, // ndwdp
- 0, // dwdw
- 0, // ndxdotdw
- {}, // ndJydy
- 0, // nnz
- 0, // ubw
- 0 // lbw
- ),
- SimulationParameters(
- std::vector