NEURON
mechanism_range.hpp
Go to the documentation of this file.
1 #pragma once
2 #include "membfunc.h"
3 #include "nrn_ansi.h"
4 #include "nrnoc_ml.h"
5 
6 #include <array>
7 
8 namespace neuron::cache {
9 /**
10  * @brief Call the given method with each dparam index that should be cached for a mechanism.
11  *
12  * The callable will be invoked with the largest index first. This is useful if you want to resize a
13  * container and use the indices as offsets into it.
14  */
15 template <typename Callable>
16 void indices_to_cache(short type, Callable callable) {
17  auto const pdata_size = nrn_prop_dparam_size_[type];
18  auto* const dparam_semantics = memb_func[type].dparam_semantics.get();
19  for (int field = pdata_size - 1; field >= 0; --field) {
20  // Check if the field-th dparam of this mechanism type is an ion variable. See
21  // hoc_register_dparam_semantics.
22  auto const sem = dparam_semantics[field];
23  // See https://github.com/neuronsimulator/nrn/issues/2312 for discussion of possible
24  // extensions to caching.
25  if (nrn_semantics_is_ion(sem) || sem == -1 /* area */ || sem == -9 /* diam */) {
26  std::invoke(callable, field);
27  }
28  }
29 }
30 
31 /**
32  * @brief Version of Memb_list for use in performance-critical code.
33  *
34  * Unlike Memb_list, this requires that the number of fields is known at compile time.
35  * This is typically only true in translated MOD file code. The idea is that an instance of this
36  * class will be created outside a loop over the data vectors and then used inside the loop.
37  *
38  * @warning It is the responsibility of the caller to ensure that the model remains sorted beyond
39  * the lifetime of the MechanismRange instance.
40  */
41 template <std::size_t NumFloatingPointFields, std::size_t NumDatumFields>
43  /**
44  * @brief Construct a MechanismRange from sorted model data.
45  * @param cache_token Token showing the model data are sorted.
46  * @param ml Range of mechanisms this MechanismRange refers to.
47  *
48  * This mirrors the signature of the functions (nrn_state, nrn_cur, nrn_init...) that are
49  * generated in C++ from MOD files. Typically those generated functions immediately create an
50  * instance of MechanismRange using this constructor.
51  */
53  : MechanismRange{ml.type(), ml.get_storage_offset()} {
54  auto const& ptr_cache = mechanism::_get::_pdata_ptr_cache_data(cache_token, ml.type());
55  m_pdata_ptrs = ptr_cache.data();
56  assert(ptr_cache.size() <= NumDatumFields);
57  }
58 
59  /** Deprecated. */
61  NrnThread&,
62  Memb_list& ml,
63  int type)
64  : MechanismRange(cache_token, ml) {
65  assert(type == ml.type());
66  }
67 
68  protected:
69  /**
70  * @brief Calls `MechanismRange(mech_type, offset, offset)`.
71  */
72  MechanismRange(int mech_type, std::size_t offset)
73  : MechanismRange(mech_type, offset, offset) {}
74 
75  /**
76  * @brief Sets up the pointers for data (not pdata) and the offsets.
77  *
78  * @param data_offset the offset for data (not pdata).
79  * @param dptr_offset the offset for pdata.
80  */
81  MechanismRange(int mech_type, std::size_t data_offset, std::size_t dptr_offset)
84  , m_data_offset{data_offset}
85  , m_dptr_offset{dptr_offset} {
86  assert((mech_type < 0) ||
87  (mechanism::get_field_count<double>(mech_type) == NumFloatingPointFields));
88  }
89 
90  public:
91  /**
92  * @brief Get the range of values for an array RANGE variable.
93  * @tparam variable The index of the RANGE variable in the mechanism.
94  * @tparam array_size The array dimension of the RANGE variable.
95  * @param instance Which mechanism instance to access inside this mechanism range.
96  */
97  template <int variable, int array_size>
98  [[nodiscard]] double* data_array(std::size_t instance) {
99  static_assert(variable < NumFloatingPointFields);
100  // assert(array_size == m_data_array_dims[variable]);
101  return std::next(m_data_ptrs[variable], array_size * (m_data_offset + instance));
102  }
103 
104  template <int variable, int array_size>
105  [[nodiscard]] double* data_array_ptr() {
106  return data_array<variable, array_size>(0);
107  }
108 
109  /**
110  * @brief Get a RANGE variable value.
111  * @tparam variable The index of the RANGE variable in the mechanism.
112  * @param instance Which mechanism instance to access inside this MechanismRange.
113  *
114  * This is only intended for use with non-array RANGE variables, otherwise use @ref data_array.
115  */
116  template <int variable>
117  [[nodiscard]] double& fpfield(std::size_t instance) {
118  return *data_array<variable, 1>(instance);
119  }
120 
121  template <int variable>
122  [[nodiscard]] double* fpfield_ptr() {
123  return data_array<variable, 1>(0);
124  }
125 
126  /**
127  * @brief Get a RANGE variable value.
128  * @param instance Which mechanism instance to access inside this MechanismRange.
129  * @param ind The index of the RANGE variable in the mechanism. This includes both the
130  * index of the variable and the index into an array RANGE variable.
131  */
132  [[nodiscard]] double& data(std::size_t instance, container::field_index ind) {
133  // assert(ind.field < NumFloatingPointFields);
134  auto const array_dim = m_data_array_dims[ind.field];
135  // assert(ind.array_index < array_dim);
136  return m_data_ptrs[ind.field][array_dim * (m_data_offset + instance) + ind.array_index];
137  }
138 
139  /**
140  * @brief Get a POINTER variable.
141  * @tparam variable The index of the POINTER variable in the pdata/dparam entries of the
142  * mechanism.
143  * @param instance Which mechanism instance to access inside this mechanism range.
144  */
145  template <int variable>
146  [[nodiscard]] double* dptr_field(std::size_t instance) {
147  static_assert(variable < NumDatumFields);
148  return m_pdata_ptrs[variable][m_dptr_offset + instance];
149  }
150 
151  template <int variable>
152  [[nodiscard]] double* const* dptr_field_ptr() {
153  static_assert(variable < NumDatumFields);
154  return m_pdata_ptrs[variable] + m_dptr_offset;
155  }
156 
157  protected:
158  /**
159  * @brief Pointer to a range of pointers to the start of RANGE variable storage.
160  *
161  * @c m_data_ptrs[i] is a pointer to the start of the contiguous storage for the
162  * @f$\texttt{i}^{th}@f$ RANGE variable.
163  * @see container::detail::field_data<Tag, true>::data_ptrs()
164  */
165  double* const* m_data_ptrs{};
166 
167  /**
168  * @brief Pointer to a range of array dimensions for the RANGE variables in this mechanism.
169  *
170  * @c m_data_array_dims[i] is the array dimension of the @f$\texttt{i}^{th}@f$ RANGE variable.
171  */
172  int const* m_data_array_dims{};
173 
174  /**
175  * @brief Pointer to a range of pointers to the start of POINTER variable caches.
176  *
177  * @c m_pdata_ptrs[i][j] is the @c double* corresponding to the @f$\texttt{i}^{th}@f$ @c pdata /
178  * @c dparam field and the @f$\texttt{j}^{th}@f$ instance of the mechanism in the program.
179  * @see MechanismInstance::MechanismInstance(Prop*) and @ref nrn_sort_mech_data.
180  */
181  double* const* const* m_pdata_ptrs{};
182 
183  /**
184  * @brief Offset of this contiguous range of mechanism instances into the global range.
185  *
186  * Typically if there is more than one thread in the process then the instances of a particular
187  * mechanism type will be distributed across multiple NrnThread objects and processed by
188  * different threads, and the mechanism data will be permuted so that the instances owned by a
189  * given thread are contiguous. In that case the MechanismRange for the 0th thread would have an
190  * @c m_data_offset of zero, and the MechanismRange for the next thread would have an @c
191  * m_data_offset of the number of instances in the 0th thread.
192  *
193  * @see @ref nrn_sort_mech_data.
194  */
195  std::size_t m_data_offset{}; // Offset into the SoA mechanism data.
196  std::size_t m_dptr_offset{}; // Offset into the dptr data.
197 };
198 /**
199  * @brief Specialised version of MechanismRange for a single instance.
200  *
201  * This is used inside generated code that takes a single mechanism instance (Prop) instead of a
202  * range of instances (Memb_list). A key feature of methods that take Prop is that they should
203  * *not* require a call to nrn_ensure_model_data_are_sorted(). This is conceptually fine, as if
204  * we are only concerned with a single mechanism instance then it doesn't matter where it lives
205  * in the global storage vectors. In this case, @ref m_dptr_cache contains an array of pointers
206  * that @ref m_dptr_datums can refer to.
207  */
208 template <std::size_t NumFloatingPointFields, std::size_t NumDatumFields>
209 struct MechanismInstance: MechanismRange<NumFloatingPointFields, NumDatumFields> {
210  /**
211  * @brief Shorthand for the MechanismRange base class.
212  */
214 
215  /**
216  * @brief Construct from a single mechanism instance.
217  * @param prop Handle to a single mechanism instance.
218  */
221  if (!prop) {
222  // see cagkftab test where setdata is not called(?) and extcall_prop is null(?)
223  return;
224  }
226  assert(field < NumDatumFields);
227  auto& datum = _nrn_mechanism_access_dparam(prop)[field];
228  m_dptr_cache[field] = datum.template get<double*>();
229  this->m_dptr_datums[field] = &(m_dptr_cache[field]);
230  });
231 
232  this->m_pdata_ptrs = m_dptr_datums.data();
233  }
234 
235  /**
236  * @brief Copy constructor.
237  */
239  *this = other; // Implement using copy assignment.
240  }
241 
242  /**
243  * @brief Copy assignment
244  *
245  * This has to be implemented manually because the base class (MechanismInstance) member @ref
246  * m_pdata_ptrs has to be updated to point at the derived class (MechanismInstance) member @ref
247  * m_dptr_datums.
248  */
250  if (this != &other) {
251  this->m_data_ptrs = other.m_data_ptrs;
252  this->m_data_array_dims = other.m_data_array_dims;
253  this->m_data_offset = other.m_data_offset;
254  this->m_dptr_offset = other.m_dptr_offset;
255  m_dptr_cache = other.m_dptr_cache;
256  for (auto i = 0; i < NumDatumFields; ++i) {
258  }
259  this->m_pdata_ptrs = m_dptr_datums.data();
260  }
261  return *this;
262  }
263 
264  private:
265  /**
266  * @brief Cached @c double* values for this instance, calculated from @ref Datum.
267  */
268  std::array<double*, NumDatumFields> m_dptr_cache{};
269 
270  /**
271  * @brief Pointers to m_dptr_cache needed to satisfy MechanismRange's requirements.
272  * @invariant @ref m_dptr_datums[i] is equal to &@ref m_dptr_cache[i] for all @c i.
273  * @invariant @c MechanismInstance::m_pdata_ptrs is equal to @ref m_dptr_datums.%data().
274  */
275  std::array<double* const*, NumDatumFields> m_dptr_datums{};
276 };
277 } // namespace neuron::cache
278 
279 namespace neuron::legacy {
280 /**
281  * @brief Helper for legacy MOD files that mess with _p in VERBATIM blocks.
282  */
283 template <typename MechInstance, typename MechRange>
284 void set_globals_from_prop(Prop* p, MechInstance& ml, MechRange*& ml_ptr, std::size_t& iml) {
285  ml = {p};
286  ml_ptr = &ml;
287  iml = 0;
288 }
289 } // namespace neuron::legacy
#define i
Definition: md1redef.h:19
#define prop
Definition: md1redef.h:38
#define assert(ex)
Definition: hocassrt.h:24
bool nrn_semantics_is_ion(int i)
Definition: ion_semantics.h:6
int _nrn_mechanism_get_type(Prop *prop)
Definition: membfunc.cpp:82
neuron::container::generic_data_handle *& _nrn_mechanism_access_dparam(Prop *prop)
Definition: membfunc.cpp:37
Item * next(Item *item)
Definition: list.cpp:89
static const char * mechanism[]
Definition: capac.cpp:19
void indices_to_cache(short type, Callable callable)
Call the given method with each dparam index that should be cached for a mechanism.
void set_globals_from_prop(Prop *p, MechInstance &ml, MechRange *&ml_ptr, std::size_t &iml)
Helper for legacy MOD files that mess with _p in VERBATIM blocks.
std::vector< double *const * > const & _pdata_ptr_cache_data(neuron::model_sorted_token const &cache_token, int mech_type)
Definition: membfunc.cpp:96
std::size_t _current_row(Prop *prop)
Definition: membfunc.cpp:93
int get_field_count< double >(int mech_type)
Definition: init.cpp:939
int const * get_array_dims(int mech_type)
Get the array dimensions for fields of the given type.
T *const * get_data_ptrs(int mech_type)
Pointer to a range of pointers to the start of contiguous storage ranges.
mech_type
int * nrn_prop_dparam_size_
Definition: init.cpp:163
size_t p
std::vector< Memb_func > memb_func
Definition: init.cpp:145
short type
Definition: cabvars.h:10
A view into a set of mechanism instances.
Definition: nrnoc_ml.h:34
int type() const
Get the mechanism type.
Definition: memblist.cpp:177
Represent main neuron object computed by single thread.
Definition: multicore.h:58
Definition: section.h:231
Specialised version of MechanismRange for a single instance.
MechanismInstance & operator=(MechanismInstance const &other)
Copy assignment.
MechanismInstance(MechanismInstance const &other)
Copy constructor.
std::array< double *, NumDatumFields > m_dptr_cache
Cached double* values for this instance, calculated from Datum.
MechanismInstance(Prop *prop)
Construct from a single mechanism instance.
std::array< double *const *, NumDatumFields > m_dptr_datums
Pointers to m_dptr_cache needed to satisfy MechanismRange's requirements.
Version of Memb_list for use in performance-critical code.
MechanismRange(neuron::model_sorted_token const &cache_token, NrnThread &, Memb_list &ml, int type)
Deprecated.
double & fpfield(std::size_t instance)
Get a RANGE variable value.
std::size_t m_data_offset
Offset of this contiguous range of mechanism instances into the global range.
double *const * m_data_ptrs
Pointer to a range of pointers to the start of RANGE variable storage.
MechanismRange(neuron::model_sorted_token const &cache_token, Memb_list &ml)
Construct a MechanismRange from sorted model data.
double * data_array(std::size_t instance)
Get the range of values for an array RANGE variable.
int const * m_data_array_dims
Pointer to a range of array dimensions for the RANGE variables in this mechanism.
double *const *const * m_pdata_ptrs
Pointer to a range of pointers to the start of POINTER variable caches.
double * dptr_field(std::size_t instance)
Get a POINTER variable.
MechanismRange(int mech_type, std::size_t data_offset, std::size_t dptr_offset)
Sets up the pointers for data (not pdata) and the offsets.
double & data(std::size_t instance, container::field_index ind)
Get a RANGE variable value.
MechanismRange(int mech_type, std::size_t offset)
Calls MechanismRange(mech_type, offset, offset).
Struct used to index SoAoS data, such as array range variables.