Blue Brain BioExplorer
Neurons.cpp
Go to the documentation of this file.
1 /*
2  *
3  * The Blue Brain BioExplorer is a tool for scientists to extract and analyse
4  * scientific data from visualization
5  *
6  * This file is part of Blue Brain BioExplorer <https://github.com/BlueBrain/BioExplorer>
7  *
8  * Copyright 2020-2024 Blue BrainProject / EPFL
9  *
10  * This program is free software: you can redistribute it and/or modify it under
11  * the terms of the GNU General Public License as published by the Free Software
12  * Foundation, either version 3 of the License, or (at your option) any later
13  * version.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License along with
21  * this program. If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include "Neurons.h"
26 #include "SomaSimulationHandler.h"
27 #include "SpikeSimulationHandler.h"
28 
29 #include <science/common/Logs.h>
30 #include <science/common/Utils.h>
32 
35 
40 
41 #ifdef USE_CGAL
44 #endif
45 
46 #include <omp.h>
47 
48 using namespace core;
49 
50 namespace bioexplorer
51 {
52 using namespace details;
53 using namespace common;
54 using namespace io;
55 using namespace db;
56 #ifdef USE_CGAL
57 using namespace meshing;
58 #endif
59 
60 namespace morphology
61 {
62 const uint64_t NB_MYELIN_FREE_SEGMENTS = 4;
63 const double DEFAULT_ARROW_RADIUS_RATIO = 10.0;
65 const double MAX_SOMA_RADIUS = 10.0;
66 
67 std::map<ReportType, std::string> reportTypeAsString = {{ReportType::undefined, "undefined"},
68  {ReportType::spike, "spike"},
69  {ReportType::soma, "soma"},
70  {ReportType::compartment, "compartment"},
71  {ReportType::synapse_efficacy, "synapse efficacy"}};
72 
73 // Mitochondria density per layer
74 // Source: A simplified morphological classification scheme for pyramidal cells in six layers of primary somatosensory
75 // cortex of juvenile rats https://www.sciencedirect.com/science/article/pii/S2451830118300293)
76 const doubles MITOCHONDRIA_DENSITY = {0.0459, 0.0522, 0.064, 0.0774, 0.0575, 0.0403};
77 
78 Neurons::Neurons(Scene& scene, const NeuronsDetails& details, const Vector3d& assemblyPosition,
79  const Quaterniond& assemblyRotation, const LoaderProgress& callback)
80  : Morphologies(details.alignToGrid, assemblyPosition, assemblyRotation, doublesToVector3d(details.scale))
81  , _details(details)
82  , _scene(scene)
83 {
85  srand(_animationDetails.seed);
86 
87  Timer chrono;
88  _buildModel(callback);
89  PLUGIN_TIMER(chrono.elapsed(), "Neurons loaded");
90 }
91 
92 double Neurons::_getDisplacementValue(const DisplacementElement& element)
93 {
94  const auto params = _details.displacementParams;
95  switch (element)
96  {
121  default:
122  PLUGIN_THROW("Invalid displacement element");
123  }
124 }
125 
126 void Neurons::_logRealismParams()
127 {
128  PLUGIN_INFO(1, "----------------------------------------------------");
129  PLUGIN_INFO(1, "Realism level (" << static_cast<uint32_t>(_details.realismLevel) << ")");
130  PLUGIN_INFO(1, "- Soma : " << boolAsString(andCheck(static_cast<uint32_t>(_details.realismLevel),
131  static_cast<uint32_t>(MorphologyRealismLevel::soma))));
132  PLUGIN_INFO(1, "- Axon : " << boolAsString(andCheck(static_cast<uint32_t>(_details.realismLevel),
133  static_cast<uint32_t>(MorphologyRealismLevel::axon))));
134  PLUGIN_INFO(1, "- Dendrite : " << boolAsString(andCheck(static_cast<uint32_t>(_details.realismLevel),
135  static_cast<uint32_t>(MorphologyRealismLevel::dendrite))));
136  PLUGIN_INFO(1, "- Internals: " << boolAsString(andCheck(static_cast<uint32_t>(_details.realismLevel),
137  static_cast<uint32_t>(MorphologyRealismLevel::internals))));
138  PLUGIN_INFO(1, "- Externals: " << boolAsString(andCheck(static_cast<uint32_t>(_details.realismLevel),
139  static_cast<uint32_t>(MorphologyRealismLevel::externals))));
140  PLUGIN_INFO(1, "- Spine : " << boolAsString(andCheck(static_cast<uint32_t>(_details.realismLevel),
141  static_cast<uint32_t>(MorphologyRealismLevel::spine))));
142  PLUGIN_INFO(1, "----------------------------------------------------");
143 }
144 
145 void Neurons::_buildContours(ThreadSafeContainer& container, const NeuronSomaMap& somas)
146 {
147 #ifdef USE_CGAL
148  PointCloud pointCloud;
149  uint64_t progress = 0;
150  size_t materialId = 0;
151 
152  for (const auto soma : somas)
153  {
154  PLUGIN_PROGRESS("Loading somas", progress, somas.size());
155  ++progress;
156 
157  const Vector3d position = soma.second.position;
158  pointCloud[materialId].push_back({position.x, position.y, position.z, _details.radiusMultiplier});
159  }
160 
161  PointCloudMesher pcm;
162  pcm.toConvexHull(container, pointCloud);
163 #else
164  PLUGIN_THROW("The BioExplorer was not compiled with the CGAL library")
165 #endif
166 }
167 
168 void Neurons::_buildSurface(const NeuronSomaMap& somas)
169 {
170 #ifdef USE_CGAL
171  PointCloud pointCloud;
172  uint64_t progress = 0;
173  size_t materialId = 0;
174 
175  for (const auto soma : somas)
176  {
177  PLUGIN_PROGRESS("Loading somas", progress, somas.size());
178  ++progress;
179 
180  const Vector3d position = soma.second.position;
181  pointCloud[materialId].push_back({position.x, position.y, position.z, _details.radiusMultiplier});
182  }
183 
184  SurfaceMesher sm(_uuid);
185  _modelDescriptor = sm.generateSurface(_scene, _details.assemblyName, pointCloud[materialId]);
186  if (!_modelDescriptor)
187  PLUGIN_THROW("Failed to generate surface")
188 
189 #else
190  PLUGIN_THROW("The BioExplorer was not compiled with the CGAL library")
191 #endif
192 }
193 
194 void Neurons::_buildModel(const LoaderProgress& callback)
195 {
196  const auto& connector = DBConnector::getInstance();
197 
198  auto model = _scene.createModel();
199 
200  // Report parameters
201  float* voltages = nullptr;
202  std::string sqlNodeFilter = _details.sqlNodeFilter;
203  _neuronsReportParameters = doublesToNeuronsReportParametersDetails(_details.reportParams);
204  if (_neuronsReportParameters.reportId != -1)
205  {
206  _simulationReport = connector.getSimulationReport(_details.populationName, _neuronsReportParameters.reportId);
207  _attachSimulationReport(*model);
208  voltages = static_cast<float*>(
209  model->getSimulationHandler()->getFrameData(_neuronsReportParameters.initialSimulationFrame));
210  }
211 
212  // Neurons
213  const auto somas = connector.getNeurons(_details.populationName, sqlNodeFilter);
214 
215  if (somas.empty())
216  PLUGIN_THROW("Selection returned no nodes");
217 
218  PLUGIN_INFO(1, "Building " << somas.size() << " neurons");
219  _logRealismParams();
220 
221  size_t previousMaterialId = std::numeric_limits<size_t>::max();
222  Vector3ui indexOffset;
223 
224  const bool somasOnly =
225  _details.loadSomas && !_details.loadAxon && !_details.loadApicalDendrites && !_details.loadBasalDendrites;
226 
227  ThreadSafeContainers containers;
230  {
231  ThreadSafeContainer container(*model, _alignToGrid, _position, _rotation, _scale);
232  switch (_details.morphologyRepresentation)
233  {
235  {
236  _buildOrientations(container, somas);
237  break;
238  }
240  {
241  _buildContours(container, somas);
242  break;
243  }
245  {
246  _buildSurface(somas);
247  return;
248  break;
249  }
250  default:
251  {
252  _buildSomasOnly(*model, container, somas);
253  break;
254  }
255  }
256  containers.push_back(container);
257  }
258  else
259  {
260  const auto nbDBConnections = DBConnector::getInstance().getNbConnections();
261 
262  uint64_t neuronIndex;
263  volatile bool flag = false;
264  std::string flagMessage;
265 #pragma omp parallel for shared(flag, flagMessage) num_threads(nbDBConnections)
266  for (neuronIndex = 0; neuronIndex < somas.size(); ++neuronIndex)
267  {
268  try
269  {
270  if (flag)
271  continue;
272 
273  if (omp_get_thread_num() == 0)
274  {
275  PLUGIN_PROGRESS("Loading neurons...", neuronIndex, somas.size() / nbDBConnections);
276  try
277  {
278  callback.updateProgress("Loading neurons...",
279  0.5f *
280  ((float)neuronIndex / ((float)(somas.size() / nbDBConnections))));
281  }
282  catch (...)
283  {
284 #pragma omp critical
285  flag = true;
286  }
287  }
288 
289  auto it = somas.begin();
290  std::advance(it, neuronIndex);
291  const auto& soma = it->second;
292  ThreadSafeContainer container(*model, _alignToGrid, _position, _rotation, _scale);
293  _buildMorphology(container, it->first, soma, neuronIndex, voltages);
294 
295 #pragma omp critical
296  containers.push_back(container);
297  }
298  catch (const std::runtime_error& e)
299  {
300 #pragma omp critical
301  flagMessage = e.what();
302 #pragma omp critical
303  flag = true;
304  }
305  catch (...)
306  {
307 #pragma omp critical
308  flagMessage = "Loading was canceled";
309 #pragma omp critical
310  flag = true;
311  }
312  }
313  }
314 
315  for (uint64_t i = 0; i < containers.size(); ++i)
316  {
317  PLUGIN_PROGRESS("- Compiling 3D geometry...", i, containers.size());
318  callback.updateProgress("Compiling 3D geometry...", 0.5f + 0.5f * (float)(1 + i) / (float)containers.size());
319  auto& container = containers[i];
320  container.commitToModel();
321  }
322  model->applyDefaultColormap();
323  if (_neuronsReportParameters.reportId != -1)
324  setDefaultTransferFunction(*model, _neuronsReportParameters.valueRange);
325 
326  ModelMetadata metadata = {{"Number of Neurons", std::to_string(somas.size())},
327  {"Number of Spines", std::to_string(_nbSpines)},
328  {"SQL node filter", _details.sqlNodeFilter},
329  {"SQL section filter", _details.sqlSectionFilter},
330  {"Max distance to soma", std::to_string(_maxDistanceToSoma)},
331  {"Min soma radius", std::to_string(_minMaxSomaRadius.x)},
332  {"Max soma radius", std::to_string(_minMaxSomaRadius.y)}};
333 
334  if (!_simulationReport.description.empty())
335  metadata["Simulation " + reportTypeAsString[_simulationReport.type] + " report"] =
336  _simulationReport.description;
337 
338  _modelDescriptor.reset(new core::ModelDescriptor(std::move(model), _details.assemblyName, metadata));
339  if (!_modelDescriptor)
340  PLUGIN_THROW("Neurons model could not be created");
341 }
342 
343 void Neurons::_buildSomasOnly(Model& model, ThreadSafeContainer& container, const NeuronSomaMap& somas)
344 {
345  uint64_t progress = 0;
346  uint64_t i = 0;
347  _minMaxSomaRadius = Vector2d(_details.radiusMultiplier, _details.radiusMultiplier);
348 
349  float* voltages = nullptr;
350  if (_neuronsReportParameters.reportId != -1)
351  voltages = static_cast<float*>(
352  model.getSimulationHandler()->getFrameData(_neuronsReportParameters.initialSimulationFrame));
353 
354  for (const auto soma : somas)
355  {
356  PLUGIN_PROGRESS("Loading somas", progress, somas.size());
357  ++progress;
358 
359  const auto useSdf =
360  andCheck(static_cast<uint32_t>(_details.realismLevel), static_cast<uint32_t>(MorphologyRealismLevel::soma));
361  const auto baseMaterialId =
363  if (_details.showMembrane)
364  {
365  uint64_t somaUserData = NO_USER_DATA;
366  const auto neuronId = soma.first;
367  switch (_simulationReport.type)
368  {
369  case ReportType::spike:
370  case ReportType::soma:
371  {
372  if (_simulationReport.guids.empty())
373  somaUserData = neuronId;
374  else
375  {
376  const auto it = _simulationReport.guids.find(neuronId);
377  if (it == _simulationReport.guids.end() && !_neuronsReportParameters.loadNonSimulatedNodes)
378  continue; // Ignore non-simulated nodes
379  somaUserData = (*it).second;
380  }
381  break;
382  }
383  }
384 
385  auto radius = _details.radiusMultiplier;
386  if (voltages)
387  radius = _details.radiusMultiplier * _neuronsReportParameters.scalingRange.x *
388  std::max(0.0, voltages[somaUserData] - _neuronsReportParameters.valueRange.x);
389 
390  const Vector3d position = soma.second.position;
391  container.addSphere(position, radius, baseMaterialId, useSdf, somaUserData, {},
393  _getDisplacementValue(DisplacementElement::morphology_soma_frequency), 0.f));
394  }
395  if (_details.generateInternals)
396  {
397  const double mitochondriaDensity =
398  (soma.second.layer < MITOCHONDRIA_DENSITY.size() ? MITOCHONDRIA_DENSITY[soma.second.layer] : 0.0);
399 
400  const auto useSdf = andCheck(static_cast<uint32_t>(_details.realismLevel),
401  static_cast<uint32_t>(MorphologyRealismLevel::internals));
402  _addSomaInternals(container, baseMaterialId, soma.second.position, _details.radiusMultiplier,
403  mitochondriaDensity, useSdf, _details.radiusMultiplier);
404  }
405  ++i;
406  }
407 }
408 
409 void Neurons::_buildOrientations(ThreadSafeContainer& container, const NeuronSomaMap& somas)
410 {
411  const auto radius = _details.radiusMultiplier;
412  uint64_t i = 0;
413  for (const auto soma : somas)
414  {
415  PLUGIN_PROGRESS("Loading soma orientations", i, somas.size());
416  const auto baseMaterialId =
418  _addArrow(container, soma.first, soma.second.position, soma.second.rotation, Vector4d(0, 0, 0, radius * 0.2),
419  Vector4d(radius, 0, 0, radius * 0.2), NeuronSectionType::soma, baseMaterialId, 0.0);
420  ++i;
421  }
422 }
423 
424 SectionSynapseMap Neurons::_buildDebugSynapses(const uint64_t neuronId, const SectionMap& sections)
425 {
426  SectionSynapseMap synapses;
427  for (const auto& section : sections)
428  {
429  if (static_cast<NeuronSectionType>(section.second.type) == NeuronSectionType::axon)
430  continue;
431 
432  // Process points according to representation
433  const auto points = _getProcessedSectionPoints(_details.morphologyRepresentation, section.second.points);
434  double sectionLength = 0.0;
435  doubles segmentEnds;
436  for (size_t i = 0; i < points.size() - 1; ++i)
437  {
438  const double segmentLength = length(points[i + 1] - points[i]);
439  sectionLength += segmentLength;
440  segmentEnds.push_back(sectionLength);
441  }
442  const size_t potentialNumberOfSynapses =
444  size_t effectiveNumberOfSynapses = potentialNumberOfSynapses * (0.5 + 0.5 * rnd0());
445 
446  for (size_t i = 0; i < effectiveNumberOfSynapses; ++i)
447  {
448  const double distance = rnd0() * sectionLength;
449  size_t segmentId = 0;
450  while (distance > segmentEnds[segmentId] && segmentId < segmentEnds.size())
451  ++segmentId;
452 
453  const auto preSynapticSectionId = section.first;
454  const auto preSynapticSegmentId = segmentId;
455  Synapse synapse;
456  synapse.type = (rand() % 2 == 0 ? MorphologySynapseType::afferent : MorphologySynapseType::efferent);
457  synapse.preSynapticSegmentDistance = distance - (segmentId > 0 ? segmentEnds[segmentId - 1] : 0.f);
458  synapse.postSynapticNeuronId = neuronId;
459  synapse.postSynapticSectionId = 0;
460  synapse.postSynapticSegmentId = 0;
461  synapse.postSynapticSegmentDistance = 0.0;
462  synapses[preSynapticSectionId][preSynapticSegmentId].push_back(synapse);
463  }
464  }
465  return synapses;
466 }
467 
468 void Neurons::_buildMorphology(ThreadSafeContainer& container, const uint64_t neuronId, const NeuronSoma& soma,
469  const uint64_t neuronIndex, const float* voltages)
470 {
471  const auto& connector = DBConnector::getInstance();
472 
473  const auto& somaRotation = soma.rotation;
474  const auto layer = soma.layer;
475  const double mitochondriaDensity = (layer < MITOCHONDRIA_DENSITY.size() ? MITOCHONDRIA_DENSITY[layer] : 0.0);
476 
477  SectionMap sections;
478 
479  // Soma radius
480  double somaRadius = _getCorrectedRadius(1.f, _details.radiusMultiplier);
481  if (_details.loadAxon || _details.loadApicalDendrites || _details.loadBasalDendrites)
482  {
483 #if 0
484  sections = connector.getNeuronSections(_details.populationName, neuronId, _details.sqlSectionFilter);
485 #else
486  sections = MemoryCache::getInstance()->getNeuronSections(connector, neuronId, _details);
487 #endif
488  double count = 0.0;
489  for (const auto& section : sections)
490  if (section.second.parentId == SOMA_AS_PARENT)
491  {
492  const Vector3d point = section.second.points[0];
493  somaRadius += 0.5 * length(point);
494  count += 1.0;
495  }
496  if (count > 0.0)
497  somaRadius /= count;
498  _minMaxSomaRadius.x = std::min(_minMaxSomaRadius.x, somaRadius);
499  _minMaxSomaRadius.y = std::max(_minMaxSomaRadius.y, somaRadius);
500  somaRadius = _getCorrectedRadius(std::min(somaRadius, MAX_SOMA_RADIUS), _details.radiusMultiplier);
501  }
502  const auto somaPosition = _animatedPosition(Vector4d(soma.position, somaRadius), neuronId);
503 
504  size_t baseMaterialId;
505  switch (_details.populationColorScheme)
506  {
508  baseMaterialId = 0;
509  break;
511  baseMaterialId = neuronIndex * NB_MATERIALS_PER_MORPHOLOGY;
512  break;
513  }
514 
515  size_t somaMaterialId;
516  switch (_details.morphologyColorScheme)
517  {
519  somaMaterialId = baseMaterialId;
520  break;
522  somaMaterialId = baseMaterialId + MATERIAL_OFFSET_SOMA;
523  break;
525  somaMaterialId = getMaterialIdFromOrientation({1.0, 1.0, 1.0});
526  break;
528  somaMaterialId = 0;
529  break;
530  }
531 
532  // Soma
533  uint64_t somaUserData = NO_USER_DATA;
534  switch (_simulationReport.type)
535  {
536  case ReportType::compartment:
537  {
538  const auto compartments =
539  connector.getNeuronSectionCompartments(_details.populationName, _neuronsReportParameters.reportId, neuronId,
540  0);
541  if (!compartments.empty())
542  somaUserData = compartments[0];
543  break;
544  }
545  case ReportType::spike:
546  case ReportType::soma:
547  {
548  if (_simulationReport.guids.empty())
549  somaUserData = neuronId + 1;
550  else
551  {
552  const auto it = _simulationReport.guids.find(neuronId);
553  if (it == _simulationReport.guids.end() && !_neuronsReportParameters.loadNonSimulatedNodes)
554  return; // Ignore non-simulated nodes
555  somaUserData = (*it).second + 1;
556  }
557  break;
558  }
559  }
560  float voltageScaling = 1.f;
561  if (voltages)
562  voltageScaling = _neuronsReportParameters.scalingRange.x *
563  std::max(0.0, voltages[somaUserData] - _neuronsReportParameters.valueRange.x);
564 
565  // Load synapses for all sections
566  SectionSynapseMap synapses;
567  switch (_details.synapsesType)
568  {
570  {
571  synapses = connector.getNeuronAfferentSynapses(_details.populationName, neuronId);
572  break;
573  }
575  {
576  synapses = _buildDebugSynapses(neuronId, sections);
577  break;
578  }
579  }
580 
581  // Sections (dendrites and axon)
582  Neighbours somaNeighbours;
583  double correctedSomaRadius = 0.f;
584  for (const auto& section : sections)
585  {
586  Neighbours sectionNeighbours;
587  const auto sectionType = static_cast<NeuronSectionType>(section.second.type);
588  const auto& points = section.second.points;
589  bool useSdf = andCheck(static_cast<uint32_t>(_details.realismLevel), static_cast<uint32_t>(sectionType));
590 
591  double distanceToSoma = 0.0;
592  if (_details.maxDistanceToSoma > 0.0)
593  // If maxDistanceToSoma != 0, then compute actual distance from soma
594  distanceToSoma = _getDistanceToSoma(sections, section.second);
595 
596  if (sectionType == NeuronSectionType::axon && !_details.loadAxon)
597  continue;
598  if (sectionType == NeuronSectionType::basal_dendrite && !_details.loadBasalDendrites)
599  continue;
600  if (sectionType == NeuronSectionType::apical_dendrite && !_details.loadApicalDendrites)
601  continue;
603  {
604  if (distanceToSoma <= _details.maxDistanceToSoma)
605  _addArrow(container, neuronIndex, somaPosition, somaRotation, section.second.points[0],
606  section.second.points[section.second.points.size() - 1], sectionType, baseMaterialId,
607  distanceToSoma);
608  continue;
609  }
610 
611  uint64_t count = 0.0;
612  // Sections connected to the soma
613  if (_details.showMembrane && _details.loadSomas && section.second.parentId == SOMA_AS_PARENT)
614  {
615  auto points = section.second.points;
616  for (uint64_t i = 0; i < points.size(); ++i)
617  {
618  auto& point = points[i];
619  point.x *= voltageScaling;
620  point.y *= voltageScaling;
621  point.z *= voltageScaling;
622  }
623 
624  const auto& firstPoint = points[0];
625  const auto& lastPoint = points[points.size() - 1];
626  auto point = firstPoint;
627  if (length(lastPoint) < length(firstPoint))
628  point = lastPoint;
629 
630  useSdf = andCheck(static_cast<uint32_t>(_details.realismLevel),
631  static_cast<uint32_t>(MorphologyRealismLevel::soma));
632 
633  const double srcRadius = _getCorrectedRadius(somaRadius * 0.75, _details.radiusMultiplier);
634  const double dstRadius = _getCorrectedRadius(point.w * 0.5, _details.radiusMultiplier);
635 
636  const auto sectionType = static_cast<NeuronSectionType>(section.second.type);
637  const bool loadSection =
638  (sectionType == NeuronSectionType::axon && _details.loadAxon) ||
639  (sectionType == NeuronSectionType::basal_dendrite && _details.loadBasalDendrites) ||
640  (sectionType == NeuronSectionType::apical_dendrite && _details.loadApicalDendrites);
641 
642  if (!loadSection)
643  continue;
644 
645  const Vector3d dst =
646  _animatedPosition(Vector4d(somaPosition + somaRotation * Vector3d(point), dstRadius), neuronId);
647  const Vector3f displacement = {
649  _getDisplacementValue(DisplacementElement::morphology_soma_frequency), 0.f)};
650 
651  Vector3d p2 = Vector3d();
652  if (voltageScaling == 1.f)
653  {
654  const Vector3d segmentDirection = normalize(lastPoint - firstPoint);
655  const double halfDistanceToSoma = length(Vector3d(point)) * 0.5;
656  const Vector3d p1 = Vector3d(point) - halfDistanceToSoma * segmentDirection;
657  p2 = p1 * dstRadius / somaRadius * 0.95;
658  }
659  const auto src = _animatedPosition(Vector4d(somaPosition + somaRotation * p2, srcRadius), neuronId);
660 
661  correctedSomaRadius = std::max(correctedSomaRadius, length(p2)) * 2.0;
662  const uint64_t geometryIndex = container.addCone(src, srcRadius, dst, dstRadius, somaMaterialId, useSdf,
663  somaUserData, {}, displacement);
664  somaNeighbours.insert(geometryIndex);
665  sectionNeighbours.insert(geometryIndex);
666  if (!useSdf)
667  container.addSphere(dst, dstRadius, somaMaterialId, useSdf, somaUserData);
668  ++count;
669  }
670  correctedSomaRadius = count == 0 ? somaRadius : correctedSomaRadius / count;
671 
672  float parentRadius = section.second.points[0].w;
673  if (sections.find(section.second.parentId) != sections.end())
674  {
675  const auto& parentSection = sections[section.second.parentId];
676  const auto& parentSectionPoints = parentSection.points;
677  parentRadius = parentSection.points[parentSection.points.size() - 1].w;
678  }
679 
680  if (distanceToSoma <= _details.maxDistanceToSoma)
681  _addSection(container, neuronId, soma.morphologyId, section.first, section.second, somaPosition,
682  somaRotation, parentRadius, baseMaterialId, mitochondriaDensity, somaUserData, synapses,
683  distanceToSoma, sectionNeighbours, voltageScaling);
684  }
685 
686  if (_details.loadSomas)
687  {
688  if (_details.showMembrane)
689  {
690  const bool useSdf = andCheck(static_cast<uint32_t>(_details.realismLevel),
691  static_cast<uint32_t>(MorphologyRealismLevel::soma));
692  somaNeighbours.insert(
693  container.addSphere(somaPosition, correctedSomaRadius, somaMaterialId, useSdf, somaUserData, {},
695  _getDisplacementValue(DisplacementElement::morphology_soma_frequency),
696  0.f)));
697  if (useSdf)
698  for (const auto index : somaNeighbours)
699  container.setSDFGeometryNeighbours(index, somaNeighbours);
700  }
701  if (_details.generateInternals)
702  {
703  const bool useSdf = andCheck(static_cast<uint32_t>(_details.realismLevel),
704  static_cast<uint32_t>(MorphologyRealismLevel::internals));
705  _addSomaInternals(container, baseMaterialId, somaPosition, correctedSomaRadius, mitochondriaDensity, useSdf,
706  _details.radiusMultiplier);
707  }
708  }
709 
710 } // namespace morphology
711 
712 void Neurons::_addVaricosity(Vector4fs& points)
713 {
714  // Reference: The cholinergic innervation develops early and rapidly in the
715  // rat cerebral cortex: a quantitative immunocytochemical study
716  // https://www.sciencedirect.com/science/article/abs/pii/S030645220100389X
717  const uint64_t middlePointIndex = points.size() / 2;
718  const auto& startPoint = points[middlePointIndex];
719  const auto& endPoint = points[middlePointIndex + 1];
720  const double radius = std::min(startPoint.w, startPoint.w);
721 
722  const auto sp = Vector3d(startPoint);
723  const auto ep = Vector3d(endPoint);
724 
725  const Vector3d dir = ep - sp;
726  const Vector3d p0 = sp + dir * 0.2;
727  const Vector3d p1 =
728  sp + dir * 0.5 +
729  radius * Vector3d((rand() % 100 - 50) / 100.0, (rand() % 100 - 50) / 100.0, (rand() % 100 - 50) / 100.0);
730  const Vector3d p2 = sp + dir * 0.8;
731 
732  auto idx = points.begin() + middlePointIndex + 1;
733  idx = points.insert(idx, {p2.x, p2.y, p2.z, startPoint.w});
734  idx = points.insert(idx, {p1.x, p1.y, p1.z, radius * 2.0});
735  points.insert(idx, {p0.x, p0.y, p0.z, endPoint.w});
736 }
737 
738 void Neurons::_addArrow(ThreadSafeContainer& container, const uint64_t neuronId, const Vector3d& somaPosition,
739  const Quaterniond& somaRotation, const Vector4d& srcNode, const Vector4d& dstNode,
740  const NeuronSectionType sectionType, const size_t baseMaterialId, const double distanceToSoma)
741 {
742  size_t sectionMaterialId;
743  switch (_details.morphologyColorScheme)
744  {
746  sectionMaterialId = sectionMaterialId = baseMaterialId;
747  break;
749  sectionMaterialId = baseMaterialId + static_cast<size_t>(sectionType);
750  break;
752  sectionMaterialId = getMaterialIdFromOrientation(somaRotation * Vector3d(0, 0, 1));
753  break;
755  sectionMaterialId = _getMaterialFromDistanceToSoma(_details.maxDistanceToSoma, distanceToSoma);
756  break;
757  }
758 
759  auto src = _animatedPosition(Vector4d(somaPosition + somaRotation * Vector3d(srcNode), srcNode.w), neuronId);
760  auto dst = _animatedPosition(Vector4d(somaPosition + somaRotation * Vector3d(dstNode), dstNode.w), neuronId);
761 
762  if (sectionType != NeuronSectionType::axon)
763  {
764  auto tmp = src;
765  src = dst;
766  dst = tmp;
767  }
768 
769  const auto userData = neuronId;
770  auto direction = dst - src;
771  const auto maxRadius = _details.radiusMultiplier < 0 ? -_details.radiusMultiplier : std::max(srcNode.w, dstNode.w);
772  const float radius = _details.radiusMultiplier < 0
773  ? -_details.radiusMultiplier
774  : std::min(length(direction) / 5.0, maxRadius * _details.radiusMultiplier);
775 
776  const auto d1 = normalize(direction) * (length(direction) / 2.0 - radius);
777  const auto d2 = normalize(direction) * (length(direction) / 2.0 + radius);
778 
779  const bool useSdf = false;
780 
781  container.addSphere(src, radius * 0.2, sectionMaterialId, useSdf, userData);
782  container.addCone(src, radius * 0.2, Vector3f(src + d1 * 0.99), radius * 0.2, sectionMaterialId, useSdf, userData);
783  container.addCone(Vector3f(src + d1 * 0.99), radius * 0.2, Vector3f(src + d1), radius, sectionMaterialId, useSdf,
784  userData);
785  container.addCone(Vector3f(src + d1), radius, Vector3f(src + d2), radius * 0.2, sectionMaterialId, useSdf,
786  userData);
787  container.addCone(Vector3f(src + d2), radius * 0.2, dst, radius * 0.2, sectionMaterialId, useSdf, userData);
788  _bounds.merge(src);
789  _bounds.merge(dst);
790 }
791 
792 void Neurons::_addSection(ThreadSafeContainer& container, const uint64_t neuronId, const uint64_t morphologyId,
793  const uint64_t sectionId, const Section& section, const Vector3d& somaPosition,
794  const Quaterniond& somaRotation, const double parentRadius, const size_t baseMaterialId,
795  const double mitochondriaDensity, const uint64_t somaUserData,
796  const SectionSynapseMap& synapses, const double distanceToSoma, const Neighbours& neighbours,
797  const float voltageScaling)
798 {
799  const auto& connector = DBConnector::getInstance();
800  const auto sectionType = static_cast<NeuronSectionType>(section.type);
801  bool useSdf = false;
802  switch (sectionType)
803  {
804  case NeuronSectionType::axon:
805  useSdf =
806  andCheck(static_cast<uint32_t>(_details.realismLevel), static_cast<uint32_t>(MorphologyRealismLevel::axon));
807  break;
808  case NeuronSectionType::apical_dendrite:
809  case NeuronSectionType::basal_dendrite:
810  useSdf = andCheck(static_cast<uint32_t>(_details.realismLevel),
811  static_cast<uint32_t>(MorphologyRealismLevel::dendrite));
812  break;
813  }
814  auto userData = NO_USER_DATA;
815 
816  auto points = section.points;
817 
818  for (auto& point : points)
819  {
820  point.x *= voltageScaling;
821  point.y *= voltageScaling;
822  point.z *= voltageScaling;
823  }
824 
825  points[0].w = parentRadius;
826 
827  size_t sectionMaterialId;
828  switch (_details.morphologyColorScheme)
829  {
831  sectionMaterialId = baseMaterialId;
832  break;
834  sectionMaterialId = baseMaterialId + section.type;
835  break;
837  sectionMaterialId = getMaterialIdFromOrientation(points[points.size() - 1] - points[0]);
838  break;
839  }
840 
841  // Process points according to representation
842  auto localPoints = _getProcessedSectionPoints(_details.morphologyRepresentation, points);
843 
844  // Generate varicosities
845  const auto middlePointIndex = localPoints.size() / 2;
846  const bool addVaricosity = _details.generateVaricosities && sectionType == NeuronSectionType::axon &&
847  localPoints.size() > nbMinSegmentsForVaricosity;
848  if (addVaricosity)
849  _addVaricosity(localPoints);
850 
851  // Section surface and volume
852  double sectionLength = 0.0;
853  double sectionVolume = 0.0;
854  uint64_ts compartments;
855  switch (_simulationReport.type)
856  {
857  case ReportType::undefined:
858  userData = NO_USER_DATA;
859  break;
860  case ReportType::spike:
861  case ReportType::soma:
862  {
863  userData = somaUserData;
864  break;
865  }
866  case ReportType::compartment:
867  {
868  compartments = connector.getNeuronSectionCompartments(_details.populationName,
869  _neuronsReportParameters.reportId, neuronId, sectionId);
870  break;
871  }
872  }
873 
874  // Section synapses
875  SegmentSynapseMap segmentSynapses;
876  const auto it = synapses.find(sectionId);
877  if (it != synapses.end())
878  segmentSynapses = (*it).second;
879 
880  // Section points
881  Neighbours sectionNeighbours = neighbours;
882  uint64_t previousGeometryIndex = 0;
883  for (uint64_t i = 0; i < localPoints.size() - 1; ++i)
884  {
885  if (!compartments.empty())
886  {
887  const uint64_t compartmentIndex = i * compartments.size() / localPoints.size();
888  userData = compartments[compartmentIndex];
889  }
890 
891  const auto& srcPoint = localPoints[i];
892  const auto& dstPoint = localPoints[i + 1];
893 
894  if (srcPoint == dstPoint)
895  // It sometimes occurs that points are duplicated, resulting in a zero-length segment that can ignored
896  continue;
897 
898  const double srcRadius = _getCorrectedRadius(srcPoint.w * 0.5, _details.radiusMultiplier);
899  const auto src =
900  _animatedPosition(Vector4d(somaPosition + somaRotation * Vector3d(srcPoint), srcRadius), neuronId);
901 
902  const double dstRadius = _getCorrectedRadius(dstPoint.w * 0.5, _details.radiusMultiplier);
903  const auto dst =
904  _animatedPosition(Vector4d(somaPosition + somaRotation * Vector3d(dstPoint), dstRadius), neuronId);
905  const double sampleLength = length(dstPoint - srcPoint);
906  sectionLength += sampleLength;
907 
908  if (_details.showMembrane)
909  {
910  Vector3f displacement{std::min(std::min(srcRadius, dstRadius) * 0.5f,
911  _getDisplacementValue(DisplacementElement::morphology_section_strength)),
912  _getDisplacementValue(DisplacementElement::morphology_section_frequency), 0.f};
913 
914  size_t materialId = _details.morphologyColorScheme == MorphologyColorScheme::distance_to_soma
915  ? _getMaterialFromDistanceToSoma(_details.maxDistanceToSoma, distanceToSoma)
916 
917  : sectionMaterialId;
918 
919  // Varicosity (axon only)
920  if (addVaricosity && _details.morphologyColorScheme == MorphologyColorScheme::section_type)
921  {
922  if (i > middlePointIndex && i < middlePointIndex + 3)
923  {
924  materialId = baseMaterialId + MATERIAL_OFFSET_VARICOSITY;
925  displacement =
926  Vector3f(2.f * srcRadius *
928  _getDisplacementValue(DisplacementElement::morphology_section_frequency) / srcRadius,
929  0.f);
930  }
931  if (i == middlePointIndex + 1 || i == middlePointIndex + 3)
932  sectionNeighbours = {};
933  if (i == middlePointIndex + 1)
934  _varicosities[neuronId].push_back(dst);
935  }
936 
937  // Synapses
938  const auto it = segmentSynapses.find(i);
939  if (it != segmentSynapses.end())
940  {
941  const auto synapses = (*it).second;
942  PLUGIN_INFO(3,
943  "Adding " << synapses.size() << " spines to segment " << i << " of section " << sectionId);
944  for (const auto& synapse : synapses)
945  {
946  const size_t spineMaterialId =
948  ? baseMaterialId + (synapse.type == MorphologySynapseType::afferent
951  : materialId;
952  const Vector3d segmentDirection = dst - src;
953  const double radiusInSegment =
954  srcRadius + ((1.0 / length(segmentDirection)) * synapse.preSynapticSegmentDistance) *
955  (dstRadius - srcRadius);
956  const Vector3d positionInSegment =
957  src + normalize(segmentDirection) * synapse.preSynapticSegmentDistance;
958  _addSpine(container, neuronId, morphologyId, sectionId, synapse, spineMaterialId, positionInSegment,
959  radiusInSegment);
960  }
961  }
962 
963  if (!useSdf)
964  container.addSphere(dst, dstRadius, materialId, useSdf, userData);
965 
966  const uint64_t geometryIndex =
967  container.addCone(src, srcRadius, dst, dstRadius, materialId, useSdf, userData, {}, displacement);
968 
969  previousGeometryIndex = geometryIndex;
970  sectionNeighbours = {geometryIndex};
971 
972  // Stop if distance to soma in greater than the specified max value
973  _maxDistanceToSoma = std::max(_maxDistanceToSoma, distanceToSoma + sectionLength);
974  if (_details.maxDistanceToSoma > 0.0 && distanceToSoma + sectionLength >= _details.maxDistanceToSoma)
975  break;
976  }
977  sectionVolume += coneVolume(sampleLength, srcRadius, dstRadius);
978 
979  _bounds.merge(srcPoint);
980  _bounds.merge(dstPoint);
981  }
982 
983  if (sectionType == NeuronSectionType::axon)
984  {
985  if (_details.generateInternals)
986  _addSectionInternals(container, neuronId, somaPosition, somaRotation, sectionLength, sectionVolume,
987  localPoints, mitochondriaDensity, baseMaterialId);
988 
989  if (_details.generateExternals)
990  _addAxonMyelinSheath(container, neuronId, somaPosition, somaRotation, sectionLength, localPoints,
991  mitochondriaDensity, baseMaterialId);
992  }
993 }
994 
995 void Neurons::_addSectionInternals(ThreadSafeContainer& container, const uint64_t neuronId,
996  const Vector3d& somaPosition, const Quaterniond& somaRotation,
997  const double sectionLength, const double sectionVolume, const Vector4fs& points,
998  const double mitochondriaDensity, const size_t baseMaterialId)
999 {
1000  if (mitochondriaDensity == 0.0)
1001  return;
1002 
1003  const auto useSdf = andCheck(static_cast<uint32_t>(_details.realismLevel),
1004  static_cast<uint32_t>(MorphologyRealismLevel::internals));
1005 
1006  // Add mitochondria (density is per section, not for the full axon)
1007  const size_t nbMaxMitochondrionSegments = sectionLength / mitochondrionSegmentSize;
1008  const double indexRatio = double(points.size()) / double(nbMaxMitochondrionSegments);
1009 
1010  double mitochondriaVolume = 0.0;
1011  const size_t mitochondrionMaterialId = baseMaterialId + MATERIAL_OFFSET_MITOCHONDRION;
1012 
1013  uint64_t nbSegments = _getNbMitochondrionSegments();
1014  int64_t mitochondrionSegment = -(rand() % (1 + nbMaxMitochondrionSegments / 10));
1015  double previousRadius;
1016  Vector3d previousPosition;
1017 
1018  uint64_t geometryIndex = 0;
1019  Vector3d randomPosition{points[0].w * (rand() % 100 - 50) / 200.0, points[0].w * (rand() % 100 - 50) / 200.0,
1020  points[0].w * (rand() % 100 - 50) / 200.0};
1021  for (size_t step = 0; step < nbMaxMitochondrionSegments; ++step)
1022  {
1023  if (mitochondriaVolume < sectionVolume * mitochondriaDensity && mitochondrionSegment >= 0 &&
1024  mitochondrionSegment < nbSegments)
1025  {
1026  const uint64_t srcIndex = uint64_t(step * indexRatio);
1027  const uint64_t dstIndex = uint64_t(step * indexRatio) + 1;
1028  if (dstIndex < points.size())
1029  {
1030  const auto srcSample = _animatedPosition(points[srcIndex], neuronId);
1031  const auto dstSample = _animatedPosition(points[dstIndex], neuronId);
1032  const double srcRadius = _getCorrectedRadius(points[srcIndex].w * 0.5, _details.radiusMultiplier);
1033  const Vector3d srcPosition{srcSample.x + srcRadius * (rand() % 100 - 50) / 500.0,
1034  srcSample.y + srcRadius * (rand() % 100 - 50) / 500.0,
1035  srcSample.z + srcRadius * (rand() % 100 - 50) / 500.0};
1036  const double dstRadius = _getCorrectedRadius(points[dstIndex].w * 0.5, _details.radiusMultiplier);
1037  const Vector3d dstPosition{dstSample.x + dstRadius * (rand() % 100 - 50) / 500.0,
1038  dstSample.y + dstRadius * (rand() % 100 - 50) / 500.0,
1039  dstSample.z + dstRadius * (rand() % 100 - 50) / 500.0};
1040 
1041  const Vector3d direction = dstPosition - srcPosition;
1042  const Vector3d position = srcPosition + randomPosition + direction * (step * indexRatio - srcIndex);
1043  const double radius = (1.0 + (rand() % 1000 - 500) / 5000.0) * mitochondrionRadius *
1044  0.5; // Make twice smaller than in the soma
1045 
1046  Neighbours neighbours;
1047  if (mitochondrionSegment != 0)
1048  neighbours = {geometryIndex};
1049 
1050  if (!useSdf)
1051  container.addSphere(somaPosition + somaRotation * position, radius, mitochondrionMaterialId,
1052  NO_USER_DATA);
1053 
1054  if (mitochondrionSegment > 0)
1055  {
1056  Neighbours neighbours = {};
1057  if (mitochondrionSegment > 1)
1058  neighbours = {geometryIndex};
1059  const auto srcPosition =
1060  _animatedPosition(Vector4d(somaPosition + somaRotation * position, radius), neuronId);
1061  const auto dstPosition =
1062  _animatedPosition(Vector4d(somaPosition + somaRotation * previousPosition, previousRadius),
1063  neuronId);
1064  geometryIndex = container.addCone(
1065  srcPosition, radius, dstPosition, previousRadius, mitochondrionMaterialId, useSdf, NO_USER_DATA,
1066  neighbours,
1067  Vector3f(radius *
1069  2.0,
1070  radius *
1072  0.f));
1073 
1074  mitochondriaVolume += coneVolume(length(position - previousPosition), radius, previousRadius);
1075  }
1076 
1077  previousPosition = position;
1078  previousRadius = radius;
1079  }
1080  }
1081  ++mitochondrionSegment;
1082 
1083  if (mitochondrionSegment == nbSegments)
1084  {
1085  mitochondrionSegment = -(rand() % (1 + nbMaxMitochondrionSegments / 10));
1086  nbSegments = _getNbMitochondrionSegments();
1087  const auto index = uint64_t(step * indexRatio);
1088  randomPosition =
1089  Vector3d(points[index].w * (rand() % 100 - 50) / 200.0, points[index].w * (rand() % 100 - 50) / 200.0,
1090  points[index].w * (rand() % 100 - 50) / 200.0);
1091  }
1092  }
1093 }
1094 
1095 void Neurons::_addAxonMyelinSheath(ThreadSafeContainer& container, const uint64_t neuronId,
1096  const Vector3d& somaPosition, const Quaterniond& somaRotation,
1097  const double sectionLength, const Vector4fs& points,
1098  const double mitochondriaDensity, const size_t baseMaterialId)
1099 {
1100  if (sectionLength == 0 || points.empty())
1101  return;
1102 
1103  const bool useSdf = andCheck(static_cast<uint32_t>(_details.realismLevel),
1104  static_cast<uint32_t>(MorphologyRealismLevel::externals));
1105 
1106  const size_t myelinSteathMaterialId = baseMaterialId + MATERIAL_OFFSET_MYELIN_SHEATH;
1107 
1108  if (sectionLength < 2 * myelinSteathLength)
1109  return;
1110 
1111  const uint64_t nbPoints = points.size();
1112  if (nbPoints < NB_MYELIN_FREE_SEGMENTS)
1113  return;
1114 
1115  // Average radius for myelin steath
1116  const auto myelinScale = myelinSteathRadiusRatio;
1117  double srcRadius = 0.0;
1118  for (const auto& point : points)
1119  srcRadius += _getCorrectedRadius(point.w * 0.5 * myelinScale, _details.radiusMultiplier);
1120  srcRadius /= points.size();
1121 
1122  uint64_t i = NB_MYELIN_FREE_SEGMENTS; // Ignore first 3 segments
1123  while (i < nbPoints - NB_MYELIN_FREE_SEGMENTS)
1124  {
1125  // Start surrounding segments with myelin steaths
1126  const auto& srcPoint = points[i];
1127  const auto srcPosition =
1128  _animatedPosition(Vector4d(somaPosition + somaRotation * Vector3d(srcPoint), srcRadius), neuronId);
1129 
1130  if (!useSdf)
1131  container.addSphere(srcPosition, srcRadius, myelinSteathMaterialId, NO_USER_DATA);
1132 
1133  double currentLength = 0;
1134  auto previousPosition = srcPosition;
1135  auto previousRadius = srcRadius;
1136  const Vector3f displacement{srcRadius *
1139  0.f};
1140  Neighbours neighbours;
1141 
1142  while (currentLength < myelinSteathLength && i < nbPoints - NB_MYELIN_FREE_SEGMENTS)
1143  {
1144  ++i;
1145  const auto& dstPoint = points[i];
1146  const auto dstRadius = srcRadius;
1147  const auto dstPosition =
1148  _animatedPosition(Vector4d(somaPosition + somaRotation * Vector3d(dstPoint), dstRadius), neuronId);
1149 
1150  currentLength += length(dstPosition - previousPosition);
1151  if (!useSdf)
1152  container.addSphere(dstPosition, srcRadius, myelinSteathMaterialId, NO_USER_DATA);
1153 
1154  const auto geometryIndex =
1155  container.addCone(dstPosition, dstRadius, previousPosition, previousRadius, myelinSteathMaterialId,
1156  useSdf, NO_USER_DATA, neighbours, displacement);
1157  neighbours.insert(geometryIndex);
1158  previousPosition = dstPosition;
1159  previousRadius = dstRadius;
1160  }
1161  i += NB_MYELIN_FREE_SEGMENTS; // Leave free segments between
1162  // myelin steaths
1163  }
1164 }
1165 
1166 void Neurons::_addSpine(ThreadSafeContainer& container, const uint64_t neuronId, const uint64_t morphologyId,
1167  const uint64_t sectionId, const Synapse& synapse, const size_t SpineMaterialId,
1168  const Vector3d& preSynapticSurfacePosition, const double radiusAtSurfacePosition)
1169 {
1170  const double radius = DEFAULT_SPINE_RADIUS;
1171  const double spineScale = 0.25;
1172  const double spineLength = 0.4 + 0.2 * rnd1();
1173 
1174  const auto spineDisplacement =
1176  _getDisplacementValue(DisplacementElement::morphology_spine_frequency), 0.0);
1177 
1178  const auto spineSmallRadius = std::max(spineDisplacement.x, radius * spineRadiusRatio * 0.5 * spineScale);
1179  const auto spineBaseRadius = std::max(spineDisplacement.x, radius * spineRadiusRatio * 0.75 * spineScale);
1180  const auto spineLargeRadius = std::max(spineDisplacement.x, radius * spineRadiusRatio * 2.5 * spineScale);
1181  const auto sectionDisplacementAmplitude = _getDisplacementValue(DisplacementElement::morphology_section_strength);
1182 
1183  const auto direction = normalize(Vector3d(rnd1(), rnd1(), rnd1()));
1184  const auto origin = preSynapticSurfacePosition +
1185  normalize(direction) * std::max(0.0, radiusAtSurfacePosition - sectionDisplacementAmplitude);
1186  const auto target = preSynapticSurfacePosition + normalize(direction) * (radiusAtSurfacePosition + spineLength);
1187 
1188  // Create random shape between origin and target
1189  auto middle = (target + origin) / 2.0;
1190  const double d = length(target - origin) / 2.0;
1191  middle += Vector3f(d * rnd1(), d * rnd1(), d * rnd1());
1192  const float spineMiddleRadius = spineSmallRadius + d * 0.1 * rnd1();
1193 
1194  Neighbours neighbours;
1195 
1196  const bool useSdf =
1197  andCheck(static_cast<uint32_t>(_details.realismLevel), static_cast<uint32_t>(MorphologyRealismLevel::spine));
1198 
1199  if (!useSdf)
1200  container.addSphere(target, spineLargeRadius, SpineMaterialId, neuronId);
1201  neighbours.insert(container.addSphere(middle, spineMiddleRadius, SpineMaterialId, useSdf, neuronId, neighbours,
1202  spineDisplacement));
1203  if (middle != origin)
1204  container.addCone(origin, spineSmallRadius, middle, spineMiddleRadius, SpineMaterialId, useSdf, neuronId,
1205  neighbours, spineDisplacement);
1206  if (middle != target)
1207  container.addCone(middle, spineMiddleRadius, target, spineLargeRadius, SpineMaterialId, useSdf, neuronId,
1208  neighbours, spineDisplacement);
1209 
1210  ++_nbSpines;
1211 }
1212 
1213 Vector4ds Neurons::getNeuronSectionPoints(const uint64_t neuronId, const uint64_t sectionId)
1214 {
1215  const auto& connector = DBConnector::getInstance();
1216  const auto neurons = connector.getNeurons(_details.populationName, "guid=" + std::to_string(neuronId));
1217 
1218  if (neurons.empty())
1219  PLUGIN_THROW("Neuron " + std::to_string(neuronId) + " does not exist");
1220  const auto& neuron = neurons.begin()->second;
1221  const auto sections = connector.getNeuronSections(_details.populationName, neuronId);
1222 
1223  if (sections.empty())
1224  PLUGIN_THROW("Section " + std::to_string(sectionId) + " does not exist for neuron " + std::to_string(neuronId));
1225  const auto section = sections.begin()->second;
1226  Vector4ds points;
1227  for (const auto& point : section.points)
1228  {
1229  const Vector3d position = _scale * (neuron.position + neuron.rotation * Vector3d(point));
1230  const double radius = point.w;
1231  points.push_back({position.x, position.y, position.z, radius});
1232  }
1233  return points;
1234 }
1235 
1237 {
1238  if (_varicosities.find(neuronId) == _varicosities.end())
1239  PLUGIN_THROW("Neuron " + std::to_string(neuronId) + " does not exist");
1240  return _varicosities[neuronId];
1241 }
1242 
1243 void Neurons::_attachSimulationReport(Model& model)
1244 {
1245  // Simulation report
1246  std::string sqlNodeFilter = _details.sqlNodeFilter;
1247  const auto& connector = DBConnector::getInstance();
1248  switch (_simulationReport.type)
1249  {
1250  case ReportType::undefined:
1251  PLUGIN_DEBUG("No report attached to the geometry");
1252  break;
1253  case ReportType::spike:
1254  {
1255  PLUGIN_INFO(1,
1256  "Initialize spike simulation handler and restrain "
1257  "guids to the simulated ones");
1258  auto handler =
1259  std::make_shared<SpikeSimulationHandler>(_details.populationName, _neuronsReportParameters.reportId);
1260  model.setSimulationHandler(handler);
1261  break;
1262  }
1263  case ReportType::soma:
1264  {
1265  PLUGIN_INFO(1,
1266  "Initialize soma simulation handler and restrain guids "
1267  "to the simulated ones");
1268  auto handler =
1269  std::make_shared<SomaSimulationHandler>(_details.populationName, _neuronsReportParameters.reportId);
1270  model.setSimulationHandler(handler);
1271  break;
1272  }
1273  case ReportType::compartment:
1274  {
1275  PLUGIN_INFO(1,
1276  "Initialize compartment simulation handler and restrain "
1277  "guids to the simulated ones");
1278  auto handler =
1279  std::make_shared<CompartmentSimulationHandler>(_details.populationName, _neuronsReportParameters.reportId);
1280  model.setSimulationHandler(handler);
1281  break;
1282  }
1283  }
1284 }
1285 
1286 } // namespace morphology
1287 } // namespace bioexplorer
@ neuron
Definition: CommonTypes.h:27
core::Boxd _bounds
Definition: Node.h:68
core::Vector3d _scale
Definition: Node.h:70
core::ModelDescriptorPtr _modelDescriptor
Definition: Node.h:67
double _getCorrectedRadius(const double radius, const double radiusMultiplier) const
details::CellAnimationDetails _animationDetails
Definition: SDFGeometries.h:73
core::Vector3d _animatedPosition(const core::Vector4d &position, const uint64_t index=0) const
core::Vector4fs _getProcessedSectionPoints(const morphology::MorphologyRepresentation &representation, const core::Vector4fs &points)
static MemoryCache * getInstance()
Get the Instance object.
Definition: MemoryCache.h:50
const morphology::SectionMap & getNeuronSections(const db::DBConnector &connector, const uint64_t neuronId, const details::NeuronsDetails &details)
Get morphology sections from cache.
Definition: MemoryCache.cpp:39
static DBConnector & getInstance()
Get the Instance object.
Definition: DBConnector.h:54
size_t getNbConnections() const
Get the number of connections to the database.
Definition: DBConnector.h:74
void _addSomaInternals(common::ThreadSafeContainer &container, const size_t materialId, const core::Vector3d &somaPosition, const double somaRadius, const double mitochondriaDensity, const bool useSdf, const double radiusMultiplier)
size_t _getMaterialFromDistanceToSoma(const double maxDistanceToSoma, const double distanceToSoma) const
double _getDistanceToSoma(const SectionMap &sections, const Section &section)
Vector3ds getNeuronVaricosities(const uint64_t neuronId)
Get the neuron varicosities location in space.
Definition: Neurons.cpp:1236
Vector4ds getNeuronSectionPoints(const uint64_t neuronId, const uint64_t sectionId)
Get the neuron section 3D points for a given section Id.
Definition: Neurons.cpp:1213
void merge(const Box< T > &aabb)
Definition: MathTypes.h:64
void updateProgress(const std::string &message, const float fraction) const
Definition: Loader.h:58
The ModelDescriptor struct defines the metadata attached to a model.Model descriptor are exposed via ...
Definition: Model.h:285
The abstract Model class holds the geometry attached to an asset of the scene (mesh,...
Definition: Model.h:469
PLATFORM_API AbstractSimulationHandlerPtr getSimulationHandler() const
Returns the simulation handler.
Definition: Model.cpp:650
PLATFORM_API void setSimulationHandler(AbstractSimulationHandlerPtr handler)
Sets the simulation handler.
Definition: Model.cpp:634
Scene object This object contains collections of geometries, materials and light sources that are use...
Definition: Scene.h:43
virtual PLATFORM_API ModelPtr createModel() const =0
Factory method to create an engine-specific model.
double elapsed() const
Definition: Timer.cpp:39
double rnd1()
Return a random double between -0.5 and 0.5.
Definition: Utils.cpp:558
double valueFromDoubles(const doubles &array, const size_t index, const double defaultValue)
Returns the value of an array of double at a given index. Default value if index is out of bounds.
Definition: Utils.cpp:610
double coneVolume(const double height, const double r1, const double r2)
Definition: Utils.cpp:446
std::set< size_t > Neighbours
Definition: Types.h:81
double rnd0()
Return a random double between 0 and 1.
Definition: Utils.cpp:553
void setDefaultTransferFunction(Model &model, const Vector2d range, const double alpha)
Set the default transfer function (Unipolar) to a given model.
Definition: Utils.cpp:99
size_t getMaterialIdFromOrientation(const Vector3d &orientation)
Definition: Utils.cpp:546
std::vector< ThreadSafeContainer > ThreadSafeContainers
Definition: Types.h:116
std::string boolAsString(const bool value)
Return a Yes/No string representation of a boolean.
Definition: Utils.cpp:605
Vector3d doublesToVector3d(const doubles &value)
Converts a vector of doubles into a 3D vector.
Definition: Utils.cpp:258
NeuronsReportParameters doublesToNeuronsReportParametersDetails(const doubles &values)
Converts a vector of doubles into neurons report parameters details.
Definition: Utils.cpp:325
bool andCheck(const uint32_t value, const uint32_t test)
Check is test is part of value using the AND operator.
Definition: Utils.cpp:600
CellAnimationDetails doublesToCellAnimationDetails(const doubles &values)
Converts a vector of doubles into cell animation details.
Definition: Utils.cpp:315
std::map< size_t, Vector4ds > PointCloud
const double myelinSteathRadiusRatio
Definition: Types.h:414
const double MAX_SOMA_RADIUS
Definition: Neurons.cpp:65
const uint64_t DEFAULT_DEBUG_SYNAPSE_DENSITY_RATIO
Definition: Neurons.cpp:64
const double mitochondrionSegmentSize
Definition: Types.h:408
const double DEFAULT_ARROW_RADIUS_RATIO
Definition: Neurons.cpp:63
std::map< uint64_t, std::vector< Synapse > > SegmentSynapseMap
Definition: Types.h:357
const doubles MITOCHONDRIA_DENSITY
Definition: Neurons.cpp:76
const size_t MATERIAL_OFFSET_MITOCHONDRION
Definition: Morphologies.h:42
const double spineRadiusRatio
Definition: Types.h:411
const size_t NB_MATERIALS_PER_MORPHOLOGY
Definition: Morphologies.h:34
const double DEFAULT_SPINE_RADIUS
Definition: Morphologies.h:32
std::map< uint64_t, NeuronSoma > NeuronSomaMap
Definition: Types.h:377
const size_t MATERIAL_OFFSET_EFFERENT_SYNAPSE
Definition: Morphologies.h:41
const size_t MATERIAL_OFFSET_VARICOSITY
Definition: Morphologies.h:35
const uint64_t NB_MYELIN_FREE_SEGMENTS
Definition: Neurons.cpp:62
const size_t MATERIAL_OFFSET_SOMA
Definition: Morphologies.h:36
const size_t MATERIAL_OFFSET_AFFERENT_SYNAPSE
Definition: Morphologies.h:40
const double myelinSteathLength
Definition: Types.h:413
const uint64_t nbMinSegmentsForVaricosity
Definition: Types.h:416
const size_t MATERIAL_OFFSET_MYELIN_SHEATH
Definition: Morphologies.h:44
const int64_t SOMA_AS_PARENT
Definition: Morphologies.h:48
std::map< uint64_t, SegmentSynapseMap > SectionSynapseMap
Definition: Types.h:358
std::map< uint64_t, Section > SectionMap
Definition: Types.h:386
std::map< ReportType, std::string > reportTypeAsString
Definition: Neurons.cpp:67
const double mitochondrionRadius
Definition: Types.h:409
const double DEFAULT_MORPHOLOGY_SECTION_STRENGTH
Definition: Displacement.h:51
const double DEFAULT_MORPHOLOGY_MITOCHONDRION_STRENGTH
Definition: Displacement.h:55
const double DEFAULT_MORPHOLOGY_SOMA_STRENGTH
Definition: Displacement.h:49
const double DEFAULT_MORPHOLOGY_SECTION_FREQUENCY
Definition: Displacement.h:52
const double DEFAULT_MORPHOLOGY_MITOCHONDRION_FREQUENCY
Definition: Displacement.h:56
const double DEFAULT_MORPHOLOGY_MYELIN_STEATH_STRENGTH
Definition: Displacement.h:57
const double DEFAULT_MORPHOLOGY_SPINE_FREQUENCY
Definition: Displacement.h:60
const double DEFAULT_MORPHOLOGY_MYELIN_STEATH_FREQUENCY
Definition: Displacement.h:58
const double DEFAULT_MORPHOLOGY_SOMA_FREQUENCY
Definition: Displacement.h:50
const double DEFAULT_MORPHOLOGY_NUCLEUS_STRENGTH
Definition: Displacement.h:53
const double DEFAULT_MORPHOLOGY_NUCLEUS_FREQUENCY
Definition: Displacement.h:54
const double DEFAULT_MORPHOLOGY_SPINE_STRENGTH
Definition: Displacement.h:59
std::map< std::string, std::string > ModelMetadata
Definition: Types.h:104
glm::vec3 Vector3f
Definition: MathTypes.h:137
glm::vec< 3, uint32_t > Vector3ui
Definition: MathTypes.h:134
glm::vec< 3, double > Vector3d
Definition: MathTypes.h:143
glm::vec< 2, double > Vector2d
Definition: MathTypes.h:142
std::vector< Vector4f > Vector4fs
Definition: MathTypes.h:140
glm::tquat< double, glm::highp > Quaterniond
Double quaternion.
Definition: MathTypes.h:153
glm::vec< 4, double > Vector4d
Definition: MathTypes.h:144
@ point
Definition: CommonTypes.h:67
#define NO_USER_DATA
Definition: CommonTypes.h:26
std::vector< core::Vector3d > Vector3ds
Definition: Types.h:64
std::vector< double > doubles
Definition: Types.h:62
std::vector< core::Vector4d > Vector4ds
Definition: Types.h:66
std::vector< uint64_t > uint64_ts
Definition: Types.h:55
#define PLUGIN_INFO(message)
Definition: Logs.h:34
#define PLUGIN_PROGRESS(__msg, __progress, __maxValue)
Definition: Logs.h:50
#define PLUGIN_THROW(message)
Definition: Logs.h:45
#define PLUGIN_DEBUG(message)
Definition: Logs.h:39
#define PLUGIN_TIMER(__time, __msg)
Definition: Logs.h:42
morphology::MorphologyColorScheme morphologyColorScheme
Definition: Types.h:1558
morphology::MorphologyRepresentation morphologyRepresentation
Definition: Types.h:1556
morphology::MorphologySynapseType synapsesType
Definition: Types.h:1543
morphology::PopulationColorScheme populationColorScheme
Definition: Types.h:1560