svZeroDSolver
Loading...
Searching...
No Matches
ActivationFunction.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the
2// University of California, and others. SPDX-License-Identifier: BSD-3-Clause
3
4/**
5 * @file ActivationFunction.h
6 * @brief Activation function classes for cardiac chamber models
7 */
8
9#ifndef SVZERODSOLVER_MODEL_ACTIVATIONFUNCTION_HPP_
10#define SVZERODSOLVER_MODEL_ACTIVATIONFUNCTION_HPP_
11
12#include <memory>
13#include <string>
14#include <vector>
15
16#include "Parameter.h"
17#include "ParameterHolder.h"
18
19/**
20 * @brief Type of signal an ActivationFunction produces
21 *
22 * Used to catch mismatched activation_function/active_stress pairings:
23 * an ActiveStress model declares the signal type it needs via
24 * \ref ActiveStress::required_signal_type, and ChamberSphere::setup_dofs
25 * checks it against the chamber's ActivationFunction::signal_type.
26 */
28 normalized, ///< f(t) in [0, 1], used by most activation functions
29 rate ///< Signed reaction-rate signal u(t), used by \ref
30 ///< PiecewiseRateActivation
31};
32
33/**
34 * @brief Base class for activation functions
35 *
36 * Activation functions compute the activation value (between 0 and 1) at a
37 * given time point within a cardiac cycle. These are used to modulate
38 * chamber elastance over time.
39 */
41 public:
42 /**
43 * @brief Construct activation function
44 *
45 * @param cardiac_period Cardiac cycle period
46 * @param input_param_properties Properties of the input parameters
47 * [(name, InputParameter), ...] for this activation function
48 */
49 ActivationFunction(double cardiac_period,
50 const std::vector<std::pair<std::string, InputParameter>>&
52
53 /**
54 * @brief Compute activation value at given time
55 *
56 * @param time Current time
57 * @return Activation value between 0 and 1
58 */
59 virtual double compute(double time) = 0;
60
61 /**
62 * @brief Type of signal this activation function's compute() produces
63 *
64 * @return \ref ActivationSignalType::normalized for all activation
65 * functions with function values between [0;1] and \ref
66 * ActivationSignalType::rate for \ref PiecewiseRateActivation, which returns
67 * a signed reaction-rate signal.
68 */
72
73 /**
74 * @brief Create a default activation function from activation function type
75 *
76 * @param type_str One of: "half_cosine", "piecewise_cosine", "two_hill",
77 * "double_tanh", "wrapping_cosine", "fourier", "piecewise_rate"
78 * @param cardiac_period Cardiac cycle period
79 * @return Unique pointer to the created activation function
80 */
81 static std::unique_ptr<ActivationFunction> create_default(
82 const std::string& type_str, double cardiac_period);
83
84 protected:
85 /**
86 * @brief Time duration of one cardiac cycle
87 */
89};
90
91/**
92 * @brief Half cosine activation function
93 *
94 * This implements the activation function used in the original
95 * ChamberElastanceInductor. The activation follows a half cosine wave
96 * during the contraction period.
97 *
98 * \f[
99 * A(t) = \begin{cases}
100 * -\frac{1}{2}\cos(2\pi t_{contract}/t_{twitch}) + \frac{1}{2}, & \text{if }
101 * t_{contract} \le t_{twitch} \\ 0, & \text{otherwise}
102 * \end{cases}
103 * \f]
104 *
105 * where \f$t_{contract} = \max(0, t_{in\_cycle} - t_{active})\f$
106 */
108 public:
109 /**
110 * @brief Construct with default parameter values (loader fills via
111 * set_param).
112 *
113 * @param cardiac_period Cardiac cycle period
114 */
115 explicit HalfCosineActivation(double cardiac_period)
116 : ActivationFunction(cardiac_period, {{"t_active", InputParameter()},
117 {"t_twitch", InputParameter()}}) {}
118
119 double compute(double time) override;
120};
121
122/**
123 * @brief Piecewise cosine activation function
124 *
125 * This implements the activation function from the LinearElastanceChamber
126 * (Regazzoni chamber model). The activation consists of separate contraction
127 * and relaxation phases, each following a cosine curve.
128 *
129 * \f[
130 * \phi(t, t_C, t_R, T_C, T_R) = \begin{cases}
131 * \frac{1}{2}\left[1 - \cos\left(\frac{\pi}{T_C} \bmod(t - t_C,
132 * T_{HB})\right)\right],
133 * & \text{if } 0 \le \bmod(t - t_C, T_{HB}) < T_C \\
134 * \frac{1}{2}\left[1 + \cos\left(\frac{\pi}{T_R} \bmod(t - t_R,
135 * T_{HB})\right)\right],
136 * & \text{if } 0 \le \bmod(t - t_R, T_{HB}) < T_R \\
137 * 0, & \text{otherwise}
138 * \end{cases}
139 * \f]
140 */
142 public:
143 /**
144 * @brief Construct with default parameter values (loader fills via
145 * set_param).
146 *
147 * @param cardiac_period Cardiac cycle period
148 */
149 explicit PiecewiseCosineActivation(double cardiac_period)
150 : ActivationFunction(cardiac_period,
151 {{"contract_start", InputParameter()},
152 {"relax_start", InputParameter()},
153 {"contract_duration", InputParameter()},
154 {"relax_duration", InputParameter()}}) {}
155
156 double compute(double time) override;
157};
158
159/**
160 * @brief Two hill activation function
161 *
162 * This implements the two-hill activation function which provides more
163 * flexible and physiologically realistic waveforms. See
164 * https://link.springer.com/article/10.1007/s10439-022-03047-3
165 *
166 * The activation is computed as:
167 * \f[
168 * A(t) = C \cdot \frac{g_1(t)}{1 + g_1(t)} \cdot \frac{1}{1 + g_2(t)}
169 * \f]
170 *
171 * where:
172 * \f[
173 * g_1(t) = \left(\frac{t_{shifted}}{\tau_1}\right)^{m_1}, \quad
174 * g_2(t) = \left(\frac{t_{shifted}}{\tau_2}\right)^{m_2}
175 * \f]
176 *
177 * and \f$t_{shifted} = (t - t_{shift}) \bmod T_{cardiac}\f$, and \f$C\f$ is a
178 * normalization constant to ensure max activation is 1.
179 */
181 public:
182 /**
183 * @brief Construct with default parameter values (loader fills via
184 * set_param).
185 *
186 * @param cardiac_period Cardiac cycle period
187 */
188 explicit TwoHillActivation(double cardiac_period)
189 : ActivationFunction(cardiac_period, {{"t_shift", InputParameter()},
190 {"tau_1", InputParameter()},
191 {"tau_2", InputParameter()},
192 {"m1", InputParameter()},
193 {"m2", InputParameter()}}),
194 normalization_factor_(1.0),
195 normalization_initialized_(false) {}
196
197 double compute(double time) override;
198
199 void finalize() override;
200
201 private:
202 void calculate_normalization_factor();
203
204 double normalization_factor_;
205 bool normalization_initialized_;
206};
207
208/**
209 * @brief Double tanh (systole/diastole sigmoid product) activation function
210 *
211 * This implements the original ChamberSphere activation: a smooth indicator
212 * function built from the product of two tanh sigmoids, one rising at
213 * systole and one falling at diastole.
214 *
215 * \f[
216 * f(t) = S_+ (t_{in\_cycle} - t_sys) \cdot S_- (t_{in\_cycle} - t_dias), \quad
217 * S_\pm (\Delta t) = \frac{1}{2} \left(1.0 \pm
218 * \text{tanh}\left( \frac{\Delta t} {\gamma}
219 * \right) \right)
220 * \f]
221 */
223 public:
224 /**
225 * @brief Construct with default parameter values (loader fills via
226 * set_param).
227 *
228 * @param cardiac_period Cardiac cycle period
229 */
230 explicit DoubleTanhActivation(double cardiac_period)
231 : ActivationFunction(cardiac_period, {{"tsys", InputParameter()},
232 {"tdias", InputParameter()},
233 {"steepness", InputParameter()}}) {}
234
235 double compute(double time) override;
236};
237
238/**
239 * @brief Wrapping cosine activation function
240 *
241 * Reproduces the atrial activation AA(t) from \cite sankaran2012patient
242 * and \cite menon2023predictors. The cosine wraps across the cardiac
243 * cycle boundary, which half_cosine and piecewise_cosine cannot do.
244 *
245 * Parameters:
246 * * `Tsa` — Atrial systole time fraction (multiplied by cardiac period)
247 * * `tpwave` — P-wave divisor (cardiac period is divided by this)
248 */
250 public:
251 /**
252 * @brief Construct a new WrappingCosineActivation object
253 *
254 * @param cardiac_period Cardiac cycle period
255 */
256 explicit WrappingCosineActivation(double cardiac_period)
257 : ActivationFunction(cardiac_period, {{"Tsa", InputParameter()},
258 {"tpwave", InputParameter()}}) {}
259
260 double compute(double time) override;
261};
262
263/**
264 * @brief Fourier series activation function
265 *
266 * 25-mode Fourier series for ventricular elastance activation, based on
267 * J. Tran's tuning framework (\cite menon2023predictors). The series is
268 * normalized so the output ranges from 0 (diastole) to 1 (peak systole).
269 * No user parameters are needed; the Fourier coefficients are hardcoded.
270 *
271 * Combined with ChamberElastanceInductor using
272 * Emax = Fourier_peak * E_scale, Emin = Fourier_trough * E_scale
273 * this reproduces E(t) = Fourier(t) * E_scale exactly.
274 */
276 public:
277 /**
278 * @brief Construct a new FourierActivation object
279 *
280 * @param cardiac_period Cardiac cycle period
281 */
282 explicit FourierActivation(double cardiac_period)
283 : ActivationFunction(cardiac_period, {}),
284 norm_min_(0.0),
285 norm_range_(1.0),
286 normalization_initialized_(false) {}
287
288 double compute(double time) override;
289 void finalize() override;
290
291 private:
292 void calculate_normalization();
293 double compute_raw(double t_in_cycle) const;
294
295 double norm_min_;
296 double norm_range_;
297 bool normalization_initialized_;
298};
299
300/**
301 * @brief Piecewise-linear activation rate activation signal
302 *
303 * Reproduces an activation rate signal \f$u(t)\f$ which is a
304 * piecewise-linear function of time within the cardiac cycle, given as a
305 * break point table (`u_t`/`u_values`) and evaluated with \ref
306 * linear_interpolate. The sign is meaningful for the strain-dependent active
307 * stress model (which splits the signal into positive/negative parts).
308 *
309 * `u_t` must be normalized to the cardiac cycle length, i.e.
310 * \f$u_t \in [0, 1]\f$, where 0 is the start of the cycle and 1 is the end.
311 * This way, the break point table does not need to be recomputed if the
312 * cardiac cycle length changes.
313 *
314 * Parameters: `u_t`, `u_values`
315 */
317 public:
318 /**
319 * @brief Construct a new PiecewiseRateActivation object
320 *
321 * @param cardiac_period Cardiac cycle period
322 */
323 explicit PiecewiseRateActivation(double cardiac_period)
324 : ActivationFunction(cardiac_period,
325 {{"u_t", InputParameter(false, true)},
326 {"u_values", InputParameter(false, true)}}) {}
327
328 double compute(double time) override;
329
330 void finalize() override;
331
334 }
335};
336
337#endif // SVZERODSOLVER_MODEL_ACTIVATIONFUNCTION_HPP_
ActivationSignalType
Type of signal an ActivationFunction produces.
Definition ActivationFunction.h:27
@ normalized
f(t) in [0, 1], used by most activation functions
Definition ActivationFunction.h:28
@ rate
Definition ActivationFunction.h:29
model::Parameter source file
Common base for chamber sub-components configured from input parameters.
static std::unique_ptr< ActivationFunction > create_default(const std::string &type_str, double cardiac_period)
Create a default activation function from activation function type.
Definition ActivationFunction.cpp:21
double cardiac_period_
Time duration of one cardiac cycle.
Definition ActivationFunction.h:88
virtual double compute(double time)=0
Compute activation value at given time.
virtual ActivationSignalType signal_type() const
Type of signal this activation function's compute() produces.
Definition ActivationFunction.h:69
ActivationFunction(double cardiac_period, const std::vector< std::pair< std::string, InputParameter > > &input_param_properties)
Construct activation function.
Definition ActivationFunction.cpp:14
DoubleTanhActivation(double cardiac_period)
Construct with default parameter values (loader fills via set_param).
Definition ActivationFunction.h:230
double compute(double time) override
Compute activation value at given time.
Definition ActivationFunction.cpp:153
FourierActivation(double cardiac_period)
Construct a new FourierActivation object.
Definition ActivationFunction.h:282
double compute(double time) override
Compute activation value at given time.
Definition ActivationFunction.cpp:238
void finalize() override
Called once after all parameters have been read from the input file (e.g. by the loader).
Definition ActivationFunction.cpp:236
HalfCosineActivation(double cardiac_period)
Construct with default parameter values (loader fills via set_param).
Definition ActivationFunction.h:115
double compute(double time) override
Compute activation value at given time.
Definition ActivationFunction.cpp:50
ParameterHolder(const std::vector< std::pair< std::string, InputParameter > > &props)
Construct a ParameterHolder.
Definition ParameterHolder.cpp:5
const std::vector< std::pair< std::string, InputParameter > > input_param_properties
Properties of the input parameters for this component [(name, InputParameter), ......
Definition ParameterHolder.h:32
double compute(double time) override
Compute activation value at given time.
Definition ActivationFunction.cpp:68
PiecewiseCosineActivation(double cardiac_period)
Construct with default parameter values (loader fills via set_param).
Definition ActivationFunction.h:149
void finalize() override
Called once after all parameters have been read from the input file (e.g. by the loader).
Definition ActivationFunction.cpp:252
ActivationSignalType signal_type() const override
Type of signal this activation function's compute() produces.
Definition ActivationFunction.h:332
double compute(double time) override
Compute activation value at given time.
Definition ActivationFunction.cpp:281
PiecewiseRateActivation(double cardiac_period)
Construct a new PiecewiseRateActivation object.
Definition ActivationFunction.h:323
TwoHillActivation(double cardiac_period)
Construct with default parameter values (loader fills via set_param).
Definition ActivationFunction.h:188
double compute(double time) override
Compute activation value at given time.
Definition ActivationFunction.cpp:131
void finalize() override
Called once after all parameters have been read from the input file (e.g. by the loader).
Definition ActivationFunction.cpp:129
WrappingCosineActivation(double cardiac_period)
Construct a new WrappingCosineActivation object.
Definition ActivationFunction.h:256
double compute(double time) override
Compute activation value at given time.
Definition ActivationFunction.cpp:173
InputParameter(bool is_optional=false, bool is_array=false, bool is_number=true, double default_val=0.0)
Handles input parameters.
Definition Parameter.h:114