svZeroDSolver
Loading...
Searching...
No Matches
ImpedanceBC.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 * @file ImpedanceBC.h
5 * @brief model::ImpedanceBC source file
6 */
7#ifndef SVZERODSOLVER_MODEL_IMPEDANCEBC_HPP_
8#define SVZERODSOLVER_MODEL_IMPEDANCEBC_HPP_
9
10#include <string>
11#include <vector>
12
13#include "Block.h"
14#include "SparseSystem.h"
15
16/**
17 * @brief Structured-tree impedance boundary condition with periodic memory.
18 *
19 * Imposes the outlet pressure as a discrete convolution of the impedance
20 * kernel with one cardiac cycle of flow history, following the structured-tree
21 * outflow condition of \cite olufsen2000.
22 *
23 * ### Governing equations
24 *
25 * \f[
26 * P_{n+1}=P_d+z_0Q_{n+1}+\sum_{m=1}^{N_k-1}z_mQ_{n+1-m}
27 * \f]
28 *
29 * The \f$z\f$ entries are discrete-time convolution weights. The \f$m=0\f$
30 * term depends on the current flow, so it enters the system matrix directly;
31 * the history terms (\f$m\geq 1\f$) are evaluated from accepted timesteps only,
32 * held in a fixed-size ring buffer spanning one cardiac cycle.
33 *
34 * The ring buffer is zero-initialized, so the history terms are omitted
35 * entirely until one full cycle has been accepted, after which they switch on
36 * at full magnitude. This reproduces the reference implementation, which gates
37 * the same sum on `t > Period`, and makes the pressure discontinuous at the
38 * start of the second cycle. The first cycle is therefore not a meaningful
39 * solution and should be discarded.
40 *
41 * ### Local contributions
42 *
43 * \f[
44 * \mathbf{y}^{e}=\left[\begin{array}{ll}P^{e} & Q^{e}\end{array}\right]^{T}
45 * \f]
46 *
47 * \f[
48 * \mathbf{F}^{e}=\left[\begin{array}{cc}
49 * 1 & -z_{0}
50 * \end{array}\right]
51 * \f]
52 *
53 * \f[
54 * \mathbf{c}^{e}=\left[\begin{array}{c}
55 * -\left(P_{d}+\sum_{m=1}^{N_k-1}z_mQ_{n+1-m}\right)
56 * \end{array}\right]
57 * \f]
58 *
59 * ### Parameters
60 *
61 * * `z`: Time-domain impedance kernel. Its length must equal one cardiac cycle
62 * in time steps (see the timing requirements below).
63 * * `Pd`: Distal/reference pressure. Optional, defaults to `0.0`.
64 * * `convolution_mode`: `exact` (default) or `truncated`. In `truncated` mode
65 * only the first `num_kernel_terms` kernel entries are used, which costs
66 * \f$O(N_k)\f$ per accepted timestep instead of one full cycle.
67 * * `num_kernel_terms`: Number of kernel terms. Required when
68 * `convolution_mode` is `truncated`, ignored otherwise.
69 *
70 * ### Usage in json configuration file
71 *
72 * "boundary_conditions": [
73 * {
74 * "bc_name": "OUT",
75 * "bc_type": "IMPEDANCE",
76 * "bc_values": {
77 * "Pd": 0.0,
78 * "z": [ ... ],
79 * "convolution_mode": "exact",
80 * "num_kernel_terms": 128
81 * }
82 * }
83 * ]
84 *
85 * ### Simulation timing requirements
86 *
87 * The kernel is discretized at the solver time step, which constrains
88 * `simulation_parameters`:
89 *
90 * * `cardiac_period / dt` must equal `z.size()` in all cases.
91 * * Non-coupled configurations must set
92 * `number_of_time_pts_per_cardiac_cycle = z.size() + 1`.
93 * * 3D-coupled configurations must keep `number_of_time_pts = 2` for the
94 * external interface step. If `number_of_time_pts_per_cardiac_cycle` is
95 * omitted, the cycle discretization is inferred from `cardiac_period / dt`.
96 * * `steady_initial` must be set to `false`. The steady initial condition is
97 * solved with a time step of `cardiac_period / 10`, which does not match the
98 * kernel discretization and has no meaningful interpretation for this
99 * boundary condition. Since `steady_initial` defaults to `true`, it must be
100 * set explicitly; otherwise the solver reports an error and exits.
101 */
102class ImpedanceBC : public Block {
103 public:
104 /**
105 * @brief Construct a new ImpedanceBC object
106 *
107 * @param id Global ID of the block
108 * @param model The model to which the block belongs
109 */
111 : Block(id, model, BlockType::impedance_bc,
112 BlockClass::boundary_condition,
113 {{"z", InputParameter(false, false, false)},
114 {"Pd", InputParameter(true, false, false)},
115 {"convolution_mode", InputParameter(true, false, false)},
116 {"num_kernel_terms", InputParameter(true, false, false)}}) {}
117
118 /**
119 * @brief Set up the algebraic degree of freedom for the impedance BC.
120 *
121 * @param dofhandler Global degree-of-freedom handler
122 */
123 void setup_dofs(DOFHandler& dofhandler) override;
124
125 /**
126 * @brief Assemble constant Jacobian contributions.
127 *
128 * @param system Global sparse system
129 * @param parameters Current parameter values
130 */
131 void update_constant(SparseSystem& system,
132 std::vector<double>& parameters) override;
133
134 /**
135 * @brief Assemble time-dependent residual contributions.
136 *
137 * @param system Global sparse system
138 * @param parameters Current parameter values
139 */
140 void update_time(SparseSystem& system,
141 std::vector<double>& parameters) override;
142
143 /**
144 * @brief Commit the accepted outlet flow for the next convolution update.
145 *
146 * @param y Accepted global state vector
147 */
148 void accept_timestep(
149 const Eigen::Matrix<double, Eigen::Dynamic, 1>& y) override;
150
151 /**
152 * @brief Serialize the convolution history needed for restart/coupling.
153 *
154 * @return Persistent block state
155 */
156 std::vector<double> get_persistent_state() const override;
157
158 /**
159 * @brief Restore a previously serialized convolution history state.
160 *
161 * @param state Persistent block state
162 */
163 void set_persistent_state(const std::vector<double>& state) override;
164
165 /**
166 * @brief Configure the impedance kernel and convolution controls.
167 *
168 * @param z Time-domain impedance kernel
169 * @param pd Distal/reference pressure
170 * @param convolution_mode Convolution mode (`exact` or `truncated`)
171 * @param num_kernel_terms Number of kernel terms for truncated mode
172 * @param points_per_cycle Sample points spanning one cardiac cycle
173 * (`simulation_parameters.number_of_time_pts_per_cardiac_cycle`). Pass 0 to
174 * infer the cycle discretization from `cardiac_period / dt` instead.
175 */
176 void configure(const std::vector<double>& z, double pd,
177 const std::string& convolution_mode, int num_kernel_terms,
178 int points_per_cycle);
179
180 /**
181 * @brief Nonzero triplet counts contributed by this block.
182 */
184
185 private:
186 enum class ConvolutionMode { exact = 0, truncated = 1 };
187
188 /// @brief Scalar fields preceding the flow history in the persistent state.
189 /// Layout: [dt, num_period_steps, num_kernel_terms, head,
190 /// num_accepted_steps, flow_history...]
191 static constexpr size_t kNumStateHeaderFields = 5;
192
193 void ensure_initialized();
194 double lagged_flow(int lag) const;
195
196 std::vector<double> kernel;
197 double pd = 0.0;
198 ConvolutionMode mode = ConvolutionMode::exact;
199 int num_kernel_terms_input = -1;
200 /// @brief One-cycle sample count from the configuration; 0 means infer the
201 /// cycle discretization from `cardiac_period / dt`.
202 int points_per_cycle_input = 0;
203 bool configured = false;
204
205 // Runtime-discretized state.
206 bool initialized = false;
207 double dt = -1.0;
208 int num_period_steps = -1;
209 int num_kernel_terms = -1;
210 int head = -1;
211 /// @brief Number of timesteps accepted since (re)initialization. Also tells
212 /// how much of the ring buffer holds real data: min(this, num_period_steps).
213 long long num_accepted_steps = 0;
214 std::vector<double> flow_history;
215};
216
217#endif // SVZERODSOLVER_MODEL_IMPEDANCEBC_HPP_
model::Block source file
BlockType
The types of blocks supported by the solver.
Definition BlockType.h:15
BlockClass
The classes/categories of blocks supported. Some classes require special handling (e....
Definition BlockType.h:45
SparseSystem source file.
Block(int id, Model *model, BlockType block_type, BlockClass block_class, std::vector< std::pair< std::string, InputParameter > > input_params)
Construct a new Block object.
Definition Block.h:102
const int id
Global ID of the block.
Definition Block.h:79
const Model * model
The model to which the block belongs.
Definition Block.h:80
void accept_timestep(const Eigen::Matrix< double, Eigen::Dynamic, 1 > &y) override
Commit the accepted outlet flow for the next convolution update.
Definition ImpedanceBC.cpp:41
TripletsContributions num_triplets
Nonzero triplet counts contributed by this block.
Definition ImpedanceBC.h:183
void configure(const std::vector< double > &z, double pd, const std::string &convolution_mode, int num_kernel_terms, int points_per_cycle)
Configure the impedance kernel and convolution controls.
Definition ImpedanceBC.cpp:126
void setup_dofs(DOFHandler &dofhandler) override
Set up the algebraic degree of freedom for the impedance BC.
Definition ImpedanceBC.cpp:13
void update_time(SparseSystem &system, std::vector< double > &parameters) override
Assemble time-dependent residual contributions.
Definition ImpedanceBC.cpp:22
std::vector< double > get_persistent_state() const override
Serialize the convolution history needed for restart/coupling.
Definition ImpedanceBC.cpp:51
void update_constant(SparseSystem &system, std::vector< double > &parameters) override
Assemble constant Jacobian contributions.
Definition ImpedanceBC.cpp:17
ImpedanceBC(int id, Model *model)
Construct a new ImpedanceBC object.
Definition ImpedanceBC.h:110
void set_persistent_state(const std::vector< double > &state) override
Restore a previously serialized convolution history state.
Definition ImpedanceBC.cpp:69
Model of 0D elements.
Definition Model.h:56
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
The number of triplets that the element contributes to the global system.
Definition Block.h:28