diff --git a/.gitmodules b/.gitmodules index d6197ece4..63dc10f89 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "platform/deps/LASTools"] path = platform/deps/LASTools url = https://github.com/LAStools/LAStools.git +[submodule "platform/deps/MorphIO"] + path = platform/deps/MorphIO + url = https://github.com/BlueBrain/MorphIO.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 1495780c0..4363c672f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,9 @@ find_package(assimp) find_package(LibJpegTurbo) find_package(Rockets) find_package(CGAL) -find_package(LASlib) +find_package(LASlib OPTIONAL_COMPONENTS) +find_package(MorphIO OPTIONAL_COMPONENTS) +find_package(HighFive OPTIONAL_COMPONENTS) set(BIOEXPLORER_SOURCE_DIRS ${PROJECT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/generated) @@ -51,6 +53,10 @@ set(BIOEXPLORER_SOURCE_DIRS ${PROJECT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/generated) # Options # ============================================================================== +if(MorphIO_FOUND) + message(STATUS "[Core] MorphIO module enabled") +endif() + # Find the Intel C++ compiler find_program(CMAKE_CXX_COMPILER NAMES icpc) if(CMAKE_CXX_COMPILER) diff --git a/bioexplorer/backend/CMakeLists.txt b/bioexplorer/backend/CMakeLists.txt index 201377447..941ec5812 100644 --- a/bioexplorer/backend/CMakeLists.txt +++ b/bioexplorer/backend/CMakeLists.txt @@ -24,7 +24,6 @@ option(${NAME}_USE_CGAL "Use CGAL meshing features" ON) # Packages find_package(OpenMP) find_package(PQXX REQUIRED) -find_package(LASlib) # Compiler flags add_compile_options("-fopenmp") @@ -62,6 +61,7 @@ set(${NAME}_SOURCES science/io/OOCManager.cpp science/io/db/DBConnector.cpp science/io/cache/MemoryCache.cpp + science/io/filesystem/MorphologyLoader.cpp science/molecularsystems/EnzymeReaction.cpp science/molecularsystems/Molecule.cpp science/molecularsystems/Membrane.cpp @@ -97,6 +97,7 @@ set(${NAME}_PUBLIC_HEADERS science/io/OOCManager.h science/io/CacheLoader.h science/io/cache/MemoryCache.h + science/io/filesystem/MorphologyLoader.h science/io/db/DBConnector.h science/common/Assembly.h science/common/Node.h @@ -150,6 +151,9 @@ set(${NAME}_PUBLIC_HEADERS science/BioExplorerPlugin.h ) +set(${NAME}_PUBLIC_MODULE_LIBRARIES) +set(${NAME}_PRIVATE_MODULE_LIBRARIES) + if(${CGAL_FOUND} AND NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-frounding-math) endif() @@ -160,8 +164,11 @@ if(ASSIMP_FOUND) list(APPEND ${NAME}_PRIVATE_MODULE_LIBRARIES ${ASSIMP_LIBRARIES}) endif() -set(${NAME}_PUBLIC_MODULE_LIBRARIES) -set(${NAME}_PRIVATE_MODULE_LIBRARIES) +if(MorphIO_FOUND) + list(APPEND ${NAME}_PRIVATE_MODULE_LIBRARIES morphio HighFive) + include_directories(${HDF5_INCLUDE_DIRS}) +endif() + # ============================================================================== # OSPRay module # ============================================================================== diff --git a/bioexplorer/backend/science/BioExplorerPlugin.cpp b/bioexplorer/backend/science/BioExplorerPlugin.cpp index 2a60ae380..42d8c5bf9 100644 --- a/bioexplorer/backend/science/BioExplorerPlugin.cpp +++ b/bioexplorer/backend/science/BioExplorerPlugin.cpp @@ -849,6 +849,7 @@ Response BioExplorerPlugin::_setGeneralSettings(const GeneralSettingsDetails &pa instance->setLoggingLevel(payload.loggingLevel); instance->setDBLoggingLevel(payload.databaseLoggingLevel); instance->setV1Compatibility(payload.v1Compatibility); + instance->setLoadMorphologiesFromFileSystem(payload.loadMorphologiesFromFileSystem); MemoryCache::getInstance()->setEnabled(payload.cacheEnabled); PLUGIN_INFO(3, "Setting general options for the plugin"); diff --git a/bioexplorer/backend/science/api/Params.cpp b/bioexplorer/backend/science/api/Params.cpp index 71e64a91d..56b9152bb 100644 --- a/bioexplorer/backend/science/api/Params.cpp +++ b/bioexplorer/backend/science/api/Params.cpp @@ -88,6 +88,7 @@ bool from_json(GeneralSettingsDetails ¶m, const std::string &payload) FROM_JSON(param, js, databaseLoggingLevel); FROM_JSON(param, js, v1Compatibility); FROM_JSON(param, js, cacheEnabled); + FROM_JSON(param, js, loadMorphologiesFromFileSystem); } catch (...) { diff --git a/bioexplorer/backend/science/common/GeneralSettings.h b/bioexplorer/backend/science/common/GeneralSettings.h index 460e382f7..d2ce40084 100644 --- a/bioexplorer/backend/science/common/GeneralSettings.h +++ b/bioexplorer/backend/science/common/GeneralSettings.h @@ -125,6 +125,21 @@ class GeneralSettings */ void setV1Compatibility(const bool value) { _v1Compatibility = value; } + /** + * @brief Get value of the loading morphologies from file system option + * + * @return true Loading morphologies from file system is enabled + * @return false Loading morphologies from file system is disabled + */ + bool getLoadMorphologiesFromFileSystem() const { return _loadMorphologiesFromFilesystem; } + + /** + * @brief Set the loading morphologies from file system option + * + * @param value Enabled is true, disabled otherwise + */ + void setLoadMorphologiesFromFileSystem(const bool value) { _loadMorphologiesFromFilesystem = value; } + static std::mutex _mutex; static GeneralSettings* _instance; @@ -136,6 +151,7 @@ class GeneralSettings size_t _loggingLevel{1}; size_t _dbLoggingLevel{0}; bool _v1Compatibility{false}; + bool _loadMorphologiesFromFilesystem{false}; }; } // namespace common } // namespace bioexplorer diff --git a/bioexplorer/backend/science/common/Types.h b/bioexplorer/backend/science/common/Types.h index a3c5c14e4..843d0cc44 100644 --- a/bioexplorer/backend/science/common/Types.h +++ b/bioexplorer/backend/science/common/Types.h @@ -328,6 +328,9 @@ using VasculaturePtr = std::shared_ptr; namespace morphology { +const std::string NEURON_CONFIG_MORPHOLOGY_FOLDER = "alternate_morphologies"; +const std::string NEURON_CONFIG_MORPHOLOGY_FILE_EXTENSION = "morphology_file_extension"; + class Morphologies; using MorphologiesPtr = std::shared_ptr; @@ -494,6 +497,12 @@ namespace io class OOCManager; using OOCManagerPtr = std::shared_ptr; +namespace filesystem +{ +class MorphologyLoader; +using MorphologyLoaderPtr = std::shared_ptr; +} // namespace filesystem + namespace db { class DBConnector; @@ -549,6 +558,7 @@ typedef struct uint32_t databaseLoggingLevel; bool v1Compatibility{false}; bool cacheEnabled{false}; + bool loadMorphologiesFromFileSystem{false}; } GeneralSettingsDetails; typedef struct @@ -1537,6 +1547,8 @@ enum class NeuronSectionType basal_dendrite = 3, apical_dendrite = 4 }; +using NeuronSectionTypes = std::vector; +const int64_t SOMA_AS_PARENT = -1; typedef struct { diff --git a/bioexplorer/backend/science/io/cache/MemoryCache.cpp b/bioexplorer/backend/science/io/cache/MemoryCache.cpp index 69837b9c0..987f50c1f 100644 --- a/bioexplorer/backend/science/io/cache/MemoryCache.cpp +++ b/bioexplorer/backend/science/io/cache/MemoryCache.cpp @@ -16,21 +16,25 @@ #include "MemoryCache.h" +#include #include #include +#include namespace bioexplorer { namespace io { +using namespace common; +using namespace morphology; using namespace details; using namespace db; +using namespace filesystem; MemoryCache* MemoryCache::_instance = nullptr; std::mutex MemoryCache::_mutex; -const morphology::SectionMap& MemoryCache::getNeuronSections(const DBConnector& connector, const uint64_t neuronId, - const NeuronsDetails& details) +const morphology::SectionMap& MemoryCache::getNeuronSections(const uint64_t neuronId, const NeuronsDetails& details) { if (_enabled) { @@ -39,7 +43,25 @@ const morphology::SectionMap& MemoryCache::getNeuronSections(const DBConnector& return (*it).second; } - _sections[neuronId] = connector.getNeuronSections(details.populationName, neuronId, details.sqlSectionFilter); + if (GeneralSettings::getInstance()->getLoadMorphologiesFromFileSystem()) + { + NeuronSectionTypes sectionTypes; + if (details.loadAxon) + sectionTypes.push_back(NeuronSectionType::axon); + if (details.loadApicalDendrites) + sectionTypes.push_back(NeuronSectionType::apical_dendrite); + if (details.loadBasalDendrites) + sectionTypes.push_back(NeuronSectionType::basal_dendrite); + + const auto morphologyLoader = _getMorphologyLoader(details.populationName); + const auto path = DBConnector::getInstance().getNeuronMorphologyRelativePath(details.populationName, neuronId); + _sections[neuronId] = morphologyLoader->getNeuronSections(path, sectionTypes); + } + else + { + _sections[neuronId] = + DBConnector::getInstance().getNeuronSections(details.populationName, neuronId, details.sqlSectionFilter); + } return _sections[neuronId]; } @@ -50,5 +72,27 @@ void MemoryCache::setEnabled(const bool enabled) if (!enabled) _sections.clear(); } + +MorphologyLoaderPtr MemoryCache::_getMorphologyLoader(const std::string& populationName) +{ + const auto itm = _morphologyLoaders.find(populationName); + if (itm != _morphologyLoaders.end()) + return (*itm).second; + + const auto& connector = DBConnector::getInstance(); + const auto configurationValues = connector.getNeuronConfiguration(populationName); + + const auto itc = configurationValues.find(NEURON_CONFIG_MORPHOLOGY_FOLDER); + if (itc == configurationValues.end()) + PLUGIN_THROW(NEURON_CONFIG_MORPHOLOGY_FOLDER + " is not defined in the configuration table"); + + const auto itf = configurationValues.find(NEURON_CONFIG_MORPHOLOGY_FILE_EXTENSION); + if (itf == configurationValues.end()) + PLUGIN_THROW(NEURON_CONFIG_MORPHOLOGY_FILE_EXTENSION + " is not defined in the configuration table"); + + _morphologyLoaders[populationName] = + std::shared_ptr(new MorphologyLoader((*itc).second, (*itf).second)); + return _morphologyLoaders[populationName]; +} } // namespace io } // namespace bioexplorer diff --git a/bioexplorer/backend/science/io/cache/MemoryCache.h b/bioexplorer/backend/science/io/cache/MemoryCache.h index f3320cfdd..4fd4e16d4 100644 --- a/bioexplorer/backend/science/io/cache/MemoryCache.h +++ b/bioexplorer/backend/science/io/cache/MemoryCache.h @@ -56,17 +56,18 @@ class MemoryCache * * @return Sections */ - const morphology::SectionMap& getNeuronSections(const db::DBConnector& connector, const uint64_t neuronId, - const details::NeuronsDetails& details); + const morphology::SectionMap& getNeuronSections(const uint64_t neuronId, const details::NeuronsDetails& details); static std::mutex _mutex; static MemoryCache* _instance; private: ~MemoryCache() {} + io::filesystem::MorphologyLoaderPtr _getMorphologyLoader(const std::string& populationName); bool _enabled{false}; std::map _sections; + std::map _morphologyLoaders; }; } // namespace io } // namespace bioexplorer diff --git a/bioexplorer/backend/science/io/db/DBConnector.cpp b/bioexplorer/backend/science/io/db/DBConnector.cpp index 5cf087da7..bfedfb1fb 100644 --- a/bioexplorer/backend/science/io/db/DBConnector.cpp +++ b/bioexplorer/backend/science/io/db/DBConnector.cpp @@ -640,6 +640,60 @@ TriangleMesh DBConnector::getAstrocyteMicroDomain(const std::string& populationN return mesh; } +StringMap DBConnector::getNeuronConfiguration(const std::string& populationName) const +{ + CHECK_DB_INITIALIZATION + + StringMap values; + pqxx::nontransaction transaction(*_connections[omp_get_thread_num() % _dbNbConnections]); + try + { + Timer chrono; + std::string sql = "SELECT guid, value FROM " + populationName + ".configuration"; + + PLUGIN_DB_INFO(1, sql); + auto res = transaction.exec(sql); + for (auto c = res.begin(); c != res.end(); ++c) + values[c[0].as()] = c[1].as(); + PLUGIN_DB_TIMER(chrono.elapsed(), "getNeuronConfiguration(populationName=" << populationName << ")"); + } + catch (const pqxx::sql_error& e) + { + PLUGIN_THROW(e.what()); + } + + return values; +} + +std::string DBConnector::getNeuronMorphologyRelativePath(const std::string& populationName, + const uint64_t neuronId) const +{ + CHECK_DB_INITIALIZATION + + std::string relativePath; + pqxx::nontransaction transaction(*_connections[omp_get_thread_num() % _dbNbConnections]); + try + { + Timer chrono; + const std::string sql = "SELECT code FROM " + populationName + + ".morphology WHERE guid=(SELECT morphology_guid FROM " + populationName + + ".node WHERE guid=" + std::to_string(neuronId) + ")"; + + PLUGIN_DB_INFO(1, sql); + auto res = transaction.exec(sql); + if (res.empty()) + PLUGIN_THROW("No relative path defined for the neuron " + std::to_string(neuronId)); + for (auto c = res.begin(); c != res.end(); ++c) + relativePath = c[0].as(); + PLUGIN_DB_TIMER(chrono.elapsed(), "getNeuronConfiguration(populationName=" << populationName << ")"); + } + catch (const pqxx::sql_error& e) + { + PLUGIN_THROW(e.what()); + } + return relativePath; +} + uint64_t DBConnector::getNumberOfNeurons(const std::string& populationName, const std::string& sqlCondition) const { CHECK_DB_INITIALIZATION @@ -704,8 +758,8 @@ NeuronSomaMap DBConnector::getNeurons(const std::string& populationName, const s soma.morphologyId = c[10].as(); somas[c[0].as()] = soma; } - PLUGIN_DB_TIMER(chrono.elapsed(), "getNeuronSections(populationName=" << populationName << ", sqlCondition=" - << sqlCondition << ")"); + PLUGIN_DB_TIMER(chrono.elapsed(), + "getNeurons(populationName=" << populationName << ", sqlCondition=" << sqlCondition << ")"); } catch (const pqxx::sql_error& e) { diff --git a/bioexplorer/backend/science/io/db/DBConnector.h b/bioexplorer/backend/science/io/db/DBConnector.h index f506f0616..dfe96d129 100644 --- a/bioexplorer/backend/science/io/db/DBConnector.h +++ b/bioexplorer/backend/science/io/db/DBConnector.h @@ -230,6 +230,14 @@ class DBConnector */ core::TriangleMesh getAstrocyteMicroDomain(const std::string& populationName, const uint64_t astrocyteId) const; + /** + * @brief Get the Neuron Circuit Configuration details + * + * @param populationName Name of the population + * @return StringMap Map of key value strings + */ + StringMap getNeuronConfiguration(const std::string& populationName) const; + /** * @brief Get the number of neurons for a given population and a specific filter * @@ -250,6 +258,15 @@ class DBConnector */ morphology::NeuronSomaMap getNeurons(const std::string& populationName, const std::string& sqlCondition = "") const; + /** + * @brief Get the relative path to the morphology file for a given neuron Id + * + * @param populationName Name of the population + * @param neuronId Identifier of the neuron + * @return std::string Relative path to the morphology file + */ + std::string getNeuronMorphologyRelativePath(const std::string& populationName, const uint64_t neuronId) const; + /** * @brief Get the sections of a given neuron * diff --git a/bioexplorer/backend/science/io/filesystem/MorphologyLoader.cpp b/bioexplorer/backend/science/io/filesystem/MorphologyLoader.cpp new file mode 100644 index 000000000..fc1413514 --- /dev/null +++ b/bioexplorer/backend/science/io/filesystem/MorphologyLoader.cpp @@ -0,0 +1,91 @@ +/* + Copyright 2020 - 2024 Blue Brain Project / EPFL + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "MorphologyLoader.h" + +#include + +#ifdef USE_MORPHIO +#include +#include +#endif // USE_MORPHIO + +namespace bioexplorer +{ +namespace io +{ +namespace filesystem +{ +using namespace core; +using namespace morphology; +using namespace details; + +#ifdef USE_MORPHIO +using namespace morphio; +#endif + +MorphologyLoader::MorphologyLoader(const std::string& morphologyFolder, const std::string& morphologyFileExtension) + : _morphologyFolder(morphologyFolder) + , _morphologyFileExtension(morphologyFileExtension) +{ +} + +#ifdef USE_MORPHIO +void loadSection(const morphio::Section& section, morphology::SectionMap& sections, + const NeuronSectionTypes& sectionTypes) +{ + if (std::find(sectionTypes.begin(), sectionTypes.end(), static_cast(section.type())) == + sectionTypes.end()) + return; + + morphology::Section s; + s.type = section.type(); + s.parentId = section.isRoot() ? SOMA_AS_PARENT : section.parent().id(); + + const auto& p = section.points(); + const auto& d = section.diameters(); + s.points.resize(p.size()); + for (uint64_t i = 0; i < p.size(); ++i) + s.points[i] = Vector4f(p[i][0], p[i][1], p[i][2], d[i]); + + s.length = 0.0; + for (uint64_t i = 0; i < s.points.size() - 1; ++i) + s.length += length(Vector3f(s.points[i + 1]) - Vector3f(s.points[i])); + + sections[section.id()] = s; + + for (const auto& child : section.children()) + loadSection(child, sections, sectionTypes); +} +#endif + +SectionMap MorphologyLoader::getNeuronSections(const std::string& filename, + const NeuronSectionTypes& sectionTypes) const +{ +#ifdef USE_MORPHIO + morphology::SectionMap sections; + const auto path = _morphologyFolder + "/" + filename + "." + _morphologyFileExtension; + Morphology morph = Morphology(path, Option::NRN_ORDER); + for (const auto& section : morph.rootSections()) + loadSection(section, sections, sectionTypes); + return sections; +#else + PLUGIN_THROW("BioExplorer was not compiler with MorphIO"); +#endif +} +} // namespace filesystem +} // namespace io +} // namespace bioexplorer diff --git a/bioexplorer/backend/science/io/filesystem/MorphologyLoader.h b/bioexplorer/backend/science/io/filesystem/MorphologyLoader.h new file mode 100644 index 000000000..4b5b2c8f7 --- /dev/null +++ b/bioexplorer/backend/science/io/filesystem/MorphologyLoader.h @@ -0,0 +1,57 @@ +/* + Copyright 2020 - 2024 Blue Brain Project / EPFL + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#pragma once + +#include + +namespace bioexplorer +{ +namespace io +{ +namespace filesystem +{ +/** + * Load molecular systems from an optimized binary representation of the 3D + * scene + */ +class MorphologyLoader +{ +public: + /** + * @brief Construct a new object + * + * @param morphologyFolder Morphology folder + */ + MorphologyLoader(const std::string& morphologyFolder, const std::string& morphologyFileExtension); + + /** + * @brief Import sections from morphology file + * + * @param filename Full path of the morphology file + * @param sectionTypes Section types to import + * @return SectionMap Sections from the morphology file + */ + morphology::SectionMap getNeuronSections(const std::string& filename, + const details::NeuronSectionTypes& sectionTypes) const; + +private: + std::string _morphologyFolder; + std::string _morphologyFileExtension; +}; +} // namespace filesystem +} // namespace io +} // namespace bioexplorer diff --git a/bioexplorer/backend/science/morphologies/Morphologies.cpp b/bioexplorer/backend/science/morphologies/Morphologies.cpp index 0f3631767..fe47cd0ff 100644 --- a/bioexplorer/backend/science/morphologies/Morphologies.cpp +++ b/bioexplorer/backend/science/morphologies/Morphologies.cpp @@ -27,6 +27,7 @@ using namespace core; namespace bioexplorer { using namespace common; +using namespace details; namespace morphology { @@ -89,7 +90,6 @@ double Morphologies::_addSomaAsSpheres(const uint64_t neuronId, const size_t som // Smooth soma according to section root for (auto& sphere : spheres) { -#if 1 const double smoothingFactor = 1.0; double r = minRadius * smoothingFactor; for (const auto& sr : sectionRoots) @@ -99,7 +99,6 @@ double Morphologies::_addSomaAsSpheres(const uint64_t neuronId, const size_t som if (angle >= 0.0) r += length(dir) * -angle * smoothingFactor; } -#endif const auto src = _animatedPosition(Vector4d(somaPosition + somaRotation * (baryCenter + normalize(sphere) * r * 0.5), diff --git a/bioexplorer/backend/science/morphologies/Morphologies.h b/bioexplorer/backend/science/morphologies/Morphologies.h index f2e29bfd7..c3bf61392 100644 --- a/bioexplorer/backend/science/morphologies/Morphologies.h +++ b/bioexplorer/backend/science/morphologies/Morphologies.h @@ -38,8 +38,6 @@ const size_t MATERIAL_OFFSET_MYELIN_SHEATH = 9; const size_t MATERIAL_OFFSET_END_FOOT = 4; const size_t MATERIAL_OFFSET_MICRO_DOMAIN = 5; -const int64_t SOMA_AS_PARENT = -1; - /** * @brief The Morphologies class */ diff --git a/bioexplorer/backend/science/morphologies/Neurons.cpp b/bioexplorer/backend/science/morphologies/Neurons.cpp index 8716779eb..0aecce167 100644 --- a/bioexplorer/backend/science/morphologies/Neurons.cpp +++ b/bioexplorer/backend/science/morphologies/Neurons.cpp @@ -570,11 +570,7 @@ void Neurons::_buildMorphology(ThreadSafeContainer& container, const uint64_t ne double somaRadius = _getCorrectedRadius(1.f, _details.radiusMultiplier); if (_details.loadAxon || _details.loadApicalDendrites || _details.loadBasalDendrites) { -#if 0 - sections = connector.getNeuronSections(_details.populationName, neuronId, _details.sqlSectionFilter); -#else - sections = MemoryCache::getInstance()->getNeuronSections(connector, neuronId, _details); -#endif + sections = MemoryCache::getInstance()->getNeuronSections(neuronId, _details); double count = 0.0; for (const auto& section : sections) if (section.second.parentId == SOMA_AS_PARENT) @@ -1284,7 +1280,7 @@ Vector4ds Neurons::getNeuronSectionPoints(const uint64_t neuronId, const uint64_ if (neurons.empty()) PLUGIN_THROW("Neuron " + std::to_string(neuronId) + " does not exist"); const auto& neuron = neurons.begin()->second; - const auto sections = connector.getNeuronSections(_details.populationName, neuronId); + const auto sections = MemoryCache::getInstance()->getNeuronSections(neuronId, _details); if (sections.empty()) PLUGIN_THROW("Section " + std::to_string(sectionId) + " does not exist for neuron " + std::to_string(neuronId)); diff --git a/bioexplorer/pythonsdk/bioexplorer/bio_explorer.py b/bioexplorer/pythonsdk/bioexplorer/bio_explorer.py index 40e4bd9b4..e9b9cad2b 100644 --- a/bioexplorer/pythonsdk/bioexplorer/bio_explorer.py +++ b/bioexplorer/pythonsdk/bioexplorer/bio_explorer.py @@ -2690,7 +2690,8 @@ def set_general_settings( logging_level=1, database_logging_level=1, v1_compatibility=False, - cache_enabled=False + cache_enabled=False, + load_morphologies_from_file_system=False ): """ Set general settings for the plugin @@ -2700,6 +2701,7 @@ def set_general_settings( :database_logging_level: Back-end logging level for database (0=no information logs, 3=full logging) :cache_enabled: Enabled memory cache + :load_morphologies_from_file_system: Load morphologies from file system instead of DB :return: Result of the request submission """ self._v1_compatibility = v1_compatibility @@ -2709,6 +2711,7 @@ def set_general_settings( params["databaseLoggingLevel"] = database_logging_level params["v1Compatibility"] = v1_compatibility params["cacheEnabled"] = cache_enabled + params["loadMorphologiesFromFileSystem"] = load_morphologies_from_file_system response = self._invoke_and_check("set-general-settings", params) return response diff --git a/common/Defines.h.in b/common/Defines.h.in index e731bd7d2..4b1e3a92a 100644 --- a/common/Defines.h.in +++ b/common/Defines.h.in @@ -52,6 +52,11 @@ #define USE_LASLIB #endif +// MORPHIO_FOUND +#if @MorphIO_FOUND@==1 +#define USE_MORPHIO +#endif + // Memory alignment (optional). Can be necessary for compatibility between multiple engines #define PLATFORM_MEMORY_ALIGNMENT @PLATFORM_MEMORY_ALIGNMENT@ #ifdef PLATFORM_MEMORY_ALIGNMENT diff --git a/platform/deps/MorphIO b/platform/deps/MorphIO new file mode 160000 index 000000000..d4d43238f --- /dev/null +++ b/platform/deps/MorphIO @@ -0,0 +1 @@ +Subproject commit d4d43238fbc1431081750a4c05c2be0f64a12770