Skip to content

Commit

Permalink
BRAYNS 579 - Add morphology samples (#1203)
Browse files Browse the repository at this point in the history
  • Loading branch information
NadirRoGue committed Oct 3, 2023
1 parent 51db355 commit 4657e8a
Show file tree
Hide file tree
Showing 15 changed files with 688 additions and 312 deletions.
169 changes: 110 additions & 59 deletions plugins/CircuitExplorer/api/circuit/MorphologyCircuitBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@
#include <api/coloring/methods/BrainDatasetColorMethod.h>
#include <api/coloring/methods/IdColorMethod.h>
#include <api/coloring/methods/MorphologySectionTypeColorMethod.h>
#include <api/neuron/NeuronGeometryBuilder.h>
#include <api/neuron/NeuronMorphologyPipeline.h>
#include <api/neuron/NeuronMorphologyReader.h>
#include <api/neuron/builders/NeuronCapsuleBuilder.h>
#include <api/neuron/builders/NeuronSphereBuilder.h>
#include <components/BrainColorData.h>
#include <components/CircuitIds.h>
#include <components/ColorHandler.h>
Expand All @@ -47,45 +48,40 @@

namespace
{
struct MorphologyMap
{
size_t cellCount{};
std::unordered_map<std::string, std::vector<size_t>> pathToCellIndices;
};

class MorphologyMapBuilder
/**
* @brief builds a map of morphology file paths to all the morphology indices that uses such file.
*/
class MorphologyPathMap
{
public:
static MorphologyMap build(const std::vector<std::string> &paths)
static std::unordered_map<std::string, std::vector<size_t>> build(const std::vector<std::string> &paths)
{
MorphologyMap result;
result.cellCount = paths.size();

auto &mapping = result.pathToCellIndices;

auto result = std::unordered_map<std::string, std::vector<size_t>>();
for (size_t i = 0; i < paths.size(); ++i)
{
mapping[paths[i]].push_back(i);
result[paths[i]].push_back(i);
}

return result;
}
};

/**
* @brief Reads the morphology files and transform them into geometry.
*/
class ParallelMorphologyLoader
{
public:
static inline constexpr size_t maxThreads = 800;

static std::vector<NeuronGeometry> load(
const MorphologyMap &morphologyMap,
template<typename PrimitiveType>
static std::vector<NeuronGeometry<PrimitiveType>> load(
const std::unordered_map<std::string, std::vector<size_t>> &morphologyMap,
const NeuronMorphologyLoaderParameters &morphologyParameters,
const std::vector<brayns::Vector3f> &positions,
const std::vector<brayns::Quaternion> &rotations,
ProgressUpdater &progressUpdater)
{
auto cellCount = morphologyMap.cellCount;
auto morphologies = std::vector<NeuronGeometry>(cellCount);
auto morphologies = std::vector<NeuronGeometry<PrimitiveType>>(positions.size());

auto soma = morphologyParameters.load_soma;
auto axon = morphologyParameters.load_axon;
Expand All @@ -97,20 +93,19 @@ class ParallelMorphologyLoader
auto morphology = NeuronMorphologyReader::read(path, soma, axon, dendrites);
pipeline.process(morphology);

auto builder = NeuronGeometryBuilder(morphology);
auto baseGeometry = NeuronGeometryBuilder<PrimitiveType>::build(morphology);

for (auto idx : indices)
{
morphologies[idx] = builder.instantiate(positions[idx], rotations[idx]);
morphologies[idx] =
NeuronGeometryInstantiator<PrimitiveType>::instantiate(baseGeometry, positions[idx], rotations[idx]);
}
};

auto updateMessage = std::string("Loading neurons");
auto &pathToCellIndexMap = morphologyMap.pathToCellIndices;

auto loadTasks = std::deque<std::future<void>>();

for (auto &[path, cellIndices] : pathToCellIndexMap)
for (auto &[path, cellIndices] : morphologyMap)
{
if (loadTasks.size() == maxThreads)
{
Expand All @@ -133,6 +128,9 @@ class ParallelMorphologyLoader
}
};

/**
* Builder pattern with all needed steps to create a model with a neuron/astrocyte circuit on it.
*/
class ModelBuilder
{
public:
Expand All @@ -148,7 +146,8 @@ class ModelBuilder
_components.add<CircuitIds>(std::move(ids));
}

void addGeometry(std::vector<std::vector<brayns::Capsule>> primitivesList)
template<typename PrimitiveType>
void addGeometry(std::vector<std::vector<PrimitiveType>> primitivesList)
{
_components.add<brayns::Geometries>(std::move(primitivesList));
_systems.setBoundsSystem<brayns::GenericBoundsSystem<brayns::Geometries>>();
Expand Down Expand Up @@ -183,12 +182,14 @@ class ModelBuilder

void addDefaultColor()
{
// Neurons are yellow
if (_modelType == ModelType::neurons)
{
_components.add<brayns::ColorSolid>(brayns::Vector4f(1.f, 1.f, 0.f, 1.f));
return;
}

// Astrocytes are blue
_components.add<brayns::ColorSolid>(brayns::Vector4f(0.55f, 0.7f, 1.f, 1.f));
}

Expand All @@ -197,47 +198,97 @@ class ModelBuilder
brayns::Systems &_systems;
const std::string &_modelType;
};
}

std::vector<CellCompartments> MorphologyCircuitBuilder::build(
brayns::Model &model,
Context context,
ProgressUpdater &updater)
/**
* @brief Flattens the morphology data into separate containers.
*/
template<typename PrimitiveType>
class DataFlattener
{
auto &morphPaths = context.morphologyPaths;
auto &morphParams = context.morphologyParams;
auto &positions = context.positions;
auto &rotations = context.rotations;

auto morphologyPathMap = MorphologyMapBuilder::build(morphPaths);
auto morphologies = ParallelMorphologyLoader::load(morphologyPathMap, morphParams, positions, rotations, updater);

auto sectionTypeMappings = std::vector<std::vector<SectionTypeMapping>>();
sectionTypeMappings.reserve(morphologies.size());
public:
struct FlatData
{
std::vector<std::vector<SectionTypeMapping>> sectionTypeMappings;
std::vector<CellCompartments> compartments;
std::vector<std::vector<PrimitiveType>> geometries;

auto compartments = std::vector<CellCompartments>();
compartments.reserve(morphologies.size());
FlatData(std::size_t size)
{
sectionTypeMappings.reserve(size);
compartments.reserve(size);
geometries.reserve(size);
}

auto geometries = std::vector<std::vector<brayns::Capsule>>();
geometries.reserve(morphologies.size());
void flattenElement(NeuronGeometry<PrimitiveType> &element)
{
sectionTypeMappings.push_back(std::move(element.sectionTypeMapping));
compartments.push_back({element.primitives.size(), std::move(element.sectionSegmentMapping)});
geometries.push_back(std::move(element.primitives));
}
};

for (auto &morphology : morphologies)
static FlatData flatten(std::vector<NeuronGeometry<PrimitiveType>> &input)
{
auto &sectionTypeMapping = morphology.sectionTypeMapping;
auto &sectionSegmentMapping = morphology.sectionSegmentMapping;
auto &geometry = morphology.geometry;
auto flatData = FlatData(input.size());
for (auto &element : input)
{
flatData.flattenElement(element);
}
return flatData;
}
};

sectionTypeMappings.push_back(std::move(sectionTypeMapping));
compartments.push_back({geometry.size(), std::move(sectionSegmentMapping)});
geometries.push_back(std::move(geometry));
template<typename PrimitiveType>
class Builder
{
public:
static std::vector<CellCompartments> build(
brayns::Model &model,
MorphologyCircuitBuilder::Context context,
ProgressUpdater &updater)
{
auto morphologies = ParallelMorphologyLoader::load<PrimitiveType>(
MorphologyPathMap::build(context.morphologyPaths),
context.morphologyParams,
context.positions,
context.rotations,
updater);

auto data = DataFlattener<PrimitiveType>::flatten(morphologies);

auto builder = ModelBuilder(model);
builder.addIds(std::move(context.ids));
builder.addGeometry(std::move(data.geometries));
builder.addNeuronSections(std::move(data.sectionTypeMappings));
builder.addColoring(std::move(context.colorData));
builder.addDefaultColor();

return data.compartments;
}
};

auto builder = ModelBuilder(model);
builder.addIds(std::move(context.ids));
builder.addGeometry(std::move(geometries));
builder.addNeuronSections(std::move(sectionTypeMappings));
builder.addColoring(std::move(context.colorData));
builder.addDefaultColor();
class BuildDispatcher
{
public:
static std::vector<CellCompartments> dispatch(
brayns::Model &model,
MorphologyCircuitBuilder::Context context,
ProgressUpdater &updater)
{
auto geometryType = context.morphologyParams.geometry_type;
if (geometryType == NeuronGeometryType::Spheres)
{
return Builder<brayns::Sphere>::build(model, std::move(context), updater);
}
return Builder<brayns::Capsule>::build(model, std::move(context), updater);
}
};
}

return compartments;
std::vector<CellCompartments> MorphologyCircuitBuilder::build(
brayns::Model &model,
Context context,
ProgressUpdater &updater)
{
return BuildDispatcher::dispatch(model, std::move(context), updater);
}
75 changes: 75 additions & 0 deletions plugins/CircuitExplorer/api/neuron/NeuronBuilder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* Copyright (c) 2015-2023, EPFL/Blue Brain Project
* All rights reserved. Do not distribute without permission.
* Responsible Author: Nadir Roman <nadir.romanguerrero@epfl.ch>
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 3.0 as published
* by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#pragma once

#include "NeuronGeometry.h"
#include "NeuronMorphology.h"

template<typename T>
constexpr bool NeuronBuilderNotSpecialized = false;

/**
* @brief The NeuronGeometryBuilder class transform a Morphology object into primitive geometry.
* @tparam PrimitiveType The type of primitive that forms the geometry to build.
*/
template<typename PrimitiveType>
class NeuronGeometryBuilder
{
public:
static_assert(NeuronBuilderNotSpecialized<PrimitiveType>, "NeuronGeometryBuilder not specialized");

/**
* @brief Builds the geometry from the given morphology
* @param morphology The morphology to transform into geometry
* @returns The built neuron geometry object
*/
static NeuronGeometry<PrimitiveType> build(const NeuronMorphology &morphology)
{
(void)morphology;
return {};
}
};

/**
* @brief Instantiates a neuron geometry object based on a given transformation.
* @tparam PrimitiveType Type of primitive that forms the geometry to be instantiated
*/
template<typename PrimitiveType>
class NeuronGeometryInstantiator
{
public:
static_assert(NeuronBuilderNotSpecialized<PrimitiveType>, "NeuronGeometryInstantiator::instantiate not specialized");

/**
* @brief Instantiates the neuron geometry with the given transform.
* @param position Translation A translation in a vector form.
* @param rotation Rotation A rotation in quaternion form.
* @return NeuronGeometry<PrimitiveType> the geometry instantiated with the given transformation.
*/
static NeuronGeometry<PrimitiveType> instantiate(
const NeuronGeometry<PrimitiveType> &source,
const brayns::Vector3f &translation,
const brayns::Quaternion &rotation)
{
(void)source;
(void)translation;
(void)rotation;
return {};
}
};
5 changes: 2 additions & 3 deletions plugins/CircuitExplorer/api/neuron/NeuronGeometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@

#pragma once

#include <brayns/engine/geometry/types/Capsule.h>

#include "NeuronSection.h"

#include <vector>
Expand All @@ -40,9 +38,10 @@ struct SectionSegmentMapping
size_t end;
};

template<typename PrimitiveType>
struct NeuronGeometry
{
std::vector<brayns::Capsule> geometry;
std::vector<PrimitiveType> primitives;
std::vector<SectionTypeMapping> sectionTypeMapping;
std::vector<SectionSegmentMapping> sectionSegmentMapping;
};

0 comments on commit 4657e8a

Please sign in to comment.