LCOV - code coverage report
Current view: top level - ugbase/lib_grid/refinement/projectors - neurite_projector.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 566 0
Test Date: 2025-09-21 23:31:46 Functions: 0.0 % 44 0

            Line data    Source code
       1              : /*
       2              :  * Copyright (c) 2016:  G-CSC, Goethe University Frankfurt
       3              :  * Author: Markus Breit
       4              :  *
       5              :  * This file is part of UG4.
       6              :  *
       7              :  * UG4 is free software: you can redistribute it and/or modify it under the
       8              :  * terms of the GNU Lesser General Public License version 3 (as published by the
       9              :  * Free Software Foundation) with the following additional attribution
      10              :  * requirements (according to LGPL/GPL v3 §7):
      11              :  *
      12              :  * (1) The following notice must be displayed in the Appropriate Legal Notices
      13              :  * of covered and combined works: "Based on UG4 (www.ug4.org/license)".
      14              :  *
      15              :  * (2) The following notice must be displayed at a prominent place in the
      16              :  * terminal output of covered works: "Based on UG4 (www.ug4.org/license)".
      17              :  *
      18              :  * (3) The following bibliography is recommended for citation and must be
      19              :  * preserved in all covered files:
      20              :  * "Reiter, S., Vogel, A., Heppner, I., Rupp, M., and Wittum, G. A massively
      21              :  *   parallel geometric multigrid solver on hierarchically distributed grids.
      22              :  *   Computing and visualization in science 16, 4 (2013), 151-164"
      23              :  * "Vogel, A., Reiter, S., Rupp, M., Nägel, A., and Wittum, G. UG4 -- a novel
      24              :  *   flexible software system for simulating pde based models on high performance
      25              :  *   computers. Computing and visualization in science 16, 4 (2013), 165-179"
      26              :  *
      27              :  * This program is distributed in the hope that it will be useful,
      28              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      29              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      30              :  * GNU Lesser General Public License for more details.
      31              :  *
      32              :  *  Created on: 2016-12-19
      33              :  */
      34              : 
      35              : #include "neurite_projector.h"
      36              : #include "common/error.h"
      37              : #include "lib_grid/global_attachments.h" // GlobalAttachments
      38              : #include "lib_grid/algorithms/debug_util.h"  // ElementDebugInfo
      39              : #include <cmath>
      40              : #include <iterator> // std::distance
      41              : #include <boost/lexical_cast.hpp>
      42              : 
      43              : namespace ug {
      44              : 
      45              : static number VecProd(const vector3& a, const vector3& b)
      46              : {
      47            0 :         return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
      48              : }
      49              : 
      50              : static number VecNormSquared(const vector3& a)
      51              : {
      52            0 :         return a[0]*a[0] + a[1]*a[1] + a[2]*a[2];
      53              : }
      54              : 
      55              : static number VecNorm(const vector3& a)
      56              : {
      57            0 :         return sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]);
      58              : }
      59              : 
      60              : 
      61            0 : NeuriteProjector::NeuriteProjector() // : m_quadOrder(80)
      62              : {
      63              :         typedef Attachment<NeuriteProjector::SurfaceParams> NPSurfParam;
      64            0 :         if (!GlobalAttachments::is_declared("npSurfParams"))
      65            0 :                 GlobalAttachments::declare_attachment<NPSurfParam>("npSurfParams", true);
      66              : 
      67              :         // If branching points extend 5r in each direction and we integrate over twice that size
      68              :         // we add around quadOrder/2 Gaussians with sigma=r over a total length of 20r.
      69              :         // So we should use about quadOrder=80 to ensure a smooth surface
      70              :         // that does not look like a pearl necklace.
      71            0 :         prepare_quadrature();
      72            0 : }
      73              : 
      74              : 
      75            0 : NeuriteProjector::NeuriteProjector(SPIGeometry3d geometry)
      76            0 : : RefinementProjector(geometry) // ,m_quadOrder(80)
      77              : {
      78              :         typedef Attachment<NeuriteProjector::SurfaceParams> NPSurfParam;
      79            0 :         if (!GlobalAttachments::is_declared("npSurfParams"))
      80            0 :                 GlobalAttachments::declare_attachment<NPSurfParam>("npSurfParams", true);
      81              : 
      82            0 :         attach_surf_params();
      83            0 :         prepare_quadrature();
      84            0 : }
      85              : 
      86              : 
      87            0 : NeuriteProjector::~NeuriteProjector() {}
      88              : 
      89              : 
      90            0 : void NeuriteProjector::set_geometry(SPIGeometry3d geometry)
      91              : {
      92              :         // call base class method
      93            0 :         RefinementProjector::set_geometry(geometry);
      94            0 :         attach_surf_params();
      95            0 : }
      96              : 
      97              : 
      98            0 : number NeuriteProjector::new_vertex(Vertex* vrt, Vertex* parent)
      99              : {
     100            0 :         m_aaSurfParams[vrt].neuriteID = m_aaSurfParams[parent].neuriteID;
     101            0 :         m_aaSurfParams[vrt].axial = m_aaSurfParams[parent].axial;
     102            0 :         m_aaSurfParams[vrt].angular = m_aaSurfParams[parent].angular;
     103            0 :         m_aaSurfParams[vrt].radial = m_aaSurfParams[parent].radial;
     104              : 
     105            0 :         set_pos(vrt, pos(parent));
     106            0 :         return 1.0;
     107              : }
     108              : 
     109              : 
     110            0 : number NeuriteProjector::new_vertex(Vertex* vrt, Edge* parent)
     111              : {
     112            0 :         return push_into_place(vrt, parent);
     113              : }
     114              : 
     115              : 
     116            0 : number NeuriteProjector::new_vertex(Vertex* vrt, Face* parent)
     117              : {
     118            0 :         return push_into_place(vrt, parent);
     119              : }
     120              : 
     121              : 
     122            0 : number NeuriteProjector::new_vertex(Vertex* vrt, Volume* parent)
     123              : {
     124            0 :         return push_into_place(vrt, parent);
     125              : }
     126              : 
     127              : 
     128            0 : void NeuriteProjector::project(Vertex* vrt)
     129              : {
     130            0 :         push_into_place(vrt, (IVertexGroup*) NULL);
     131            0 : }
     132              : 
     133              : 
     134            0 : void NeuriteProjector::direction_at_grid_object(vector3& dirOut, GridObject* o) const
     135              : {
     136              :         // treat vertex separately as it is no vertex group
     137            0 :         if (o->base_object_id() == VERTEX)
     138              :         {
     139            0 :                 Vertex* v = dynamic_cast<Vertex*>(o);
     140            0 :                 UG_COND_THROW(!v, "Non-vertex with VERTEX base object id.")
     141              : 
     142              :                 const SurfaceParams& sp = m_aaSurfParams[v];
     143            0 :                 uint32_t nid = sp.neuriteID & ((1 << 20) - 1);
     144            0 :                 float t = sp.axial;
     145              : 
     146            0 :                 const Section& sec = *get_section_iterator(nid, t);
     147              : 
     148            0 :                 number te = sec.endParam;
     149              :                 const number* s = &sec.splineParamsX[0];
     150              :                 number& v0 = dirOut[0];
     151            0 :                 v0 = -3.0*s[0]*(te-t) - 2.0*s[1];
     152            0 :                 v0 = v0*(te-t) - s[2];
     153              : 
     154              :                 s = &sec.splineParamsY[0];
     155              :                 number& v1 = dirOut[1];
     156            0 :                 v1 = -3.0*s[0]*(te-t) - 2.0*s[1];
     157            0 :                 v1 = v1*(te-t) - s[2];
     158              : 
     159              :                 s = &sec.splineParamsZ[0];
     160              :                 number& v2 = dirOut[2];
     161            0 :                 v2 = -3.0*s[0]*(te-t) - 2.0*s[1];
     162            0 :                 v2 = v2*(te-t) - s[2];
     163              : 
     164            0 :                 return;
     165              :         }
     166              : 
     167            0 :         IVertexGroup* vrtGrp = dynamic_cast<IVertexGroup*>(o);
     168            0 :         UG_COND_THROW(!vrtGrp, "Non-vertex element which is not a vertex group.");
     169              : 
     170              :         uint32_t nid;
     171              :         float t, dummy1, dummy2;
     172            0 :         average_params(nid, t, dummy1, dummy2, vrtGrp);
     173              : 
     174            0 :         const Section& sec = *get_section_iterator(nid, t);
     175              : 
     176            0 :         number te = sec.endParam;
     177              :         const number* s = &sec.splineParamsX[0];
     178              :         number& v0 = dirOut[0];
     179            0 :         v0 = -3.0*s[0]*(te-t) - 2.0*s[1];
     180            0 :         v0 = v0*(te-t) - s[2];
     181              : 
     182              :         s = &sec.splineParamsY[0];
     183              :         number& v1 = dirOut[1];
     184            0 :         v1 = -3.0*s[0]*(te-t) - 2.0*s[1];
     185            0 :         v1 = v1*(te-t) - s[2];
     186              : 
     187              :         s = &sec.splineParamsZ[0];
     188              :         number& v2 = dirOut[2];
     189            0 :         v2 = -3.0*s[0]*(te-t) - 2.0*s[1];
     190            0 :         v2 = v2*(te-t) - s[2];
     191              : }
     192              : 
     193              : 
     194              : 
     195            0 : void NeuriteProjector::attach_surf_params()
     196              : {
     197            0 :         Grid& grid = this->geometry()->grid();
     198            0 :         UG_COND_THROW(!GlobalAttachments::is_declared("npSurfParams"),
     199              :                 "GlobalAttachment 'npSurfParams' not declared.");
     200            0 :         m_aSurfParams = GlobalAttachments::attachment<Attachment<SurfaceParams> >("npSurfParams");
     201              : 
     202              :         // make sure surfaceParams attachment is attached
     203            0 :         UG_COND_THROW(!grid.has_vertex_attachment(m_aSurfParams),
     204              :                 "No surface parameter attachment for neurite projector attached to grid.");
     205            0 :         m_aaSurfParams.access(grid, m_aSurfParams);
     206            0 : }
     207              : 
     208              : 
     209            0 : void NeuriteProjector::debug_neurites() const
     210              : {
     211              :         UG_LOGN("BPs of neurites in projector:")
     212              :         // print out branching region data
     213            0 :         for (size_t n = 0; n < m_vNeurites.size(); ++n)
     214              :         {
     215              :                 UG_LOGN("Neurite " << n);
     216              :                 const NeuriteProjector::Neurite& neurite = m_vNeurites[n];
     217            0 :                 const std::vector<NeuriteProjector::BranchingRegion> vBR = neurite.vBR;
     218              : 
     219            0 :                 for (size_t b = 0; b < vBR.size(); ++b)
     220              :                 {
     221            0 :                         UG_LOGN("  BR " << b << ": " << vBR[b].t);
     222              :                         SmartPtr<NeuriteProjector::BranchingPoint> bp = vBR[b].bp;
     223              : 
     224              :                         UG_LOGN("    associated BP data:")
     225              :                         size_t bpSz = bp->vNid.size();
     226            0 :                         if (bpSz != bp->vRegions.size())
     227              :                         {
     228              :                                 UG_LOGN("Size mismatch: vNid " << bpSz << ", vRegions " << bp->vRegions.size());
     229              :                         }
     230              :                         else
     231              :                         {
     232            0 :                                 for (size_t i = 0; i < bpSz; ++i)
     233              :                                 {
     234            0 :                                         UG_LOGN("      " << bp->vNid[i] << " (" << bp->vRegions[i]->t << ")");
     235              :                                 }
     236              :                         }
     237              :                 }
     238            0 :         }
     239            0 : }
     240              : 
     241              : 
     242            0 : std::vector<NeuriteProjector::Neurite>& NeuriteProjector::neurites()
     243              : {
     244            0 :         return m_vNeurites;
     245              : }
     246              : 
     247              : 
     248            0 : const NeuriteProjector::Neurite& NeuriteProjector::neurite(uint32_t nid) const
     249              : {
     250            0 :         UG_COND_THROW(nid >= m_vNeurites.size(),
     251              :                 "Requested neurite index " << nid << " that is not present in the neurite.");
     252            0 :         return m_vNeurites[nid];
     253              : }
     254              : 
     255              : 
     256              : Grid::VertexAttachmentAccessor<Attachment<NeuriteProjector::SurfaceParams> >&
     257            0 : NeuriteProjector::surface_params_accessor()
     258              : {
     259            0 :         return m_aaSurfParams;
     260              : }
     261              : 
     262              : 
     263              : const Grid::VertexAttachmentAccessor<Attachment<NeuriteProjector::SurfaceParams> >&
     264            0 : NeuriteProjector::surface_params_accessor() const
     265              : {
     266            0 :         return m_aaSurfParams;
     267              : }
     268              : 
     269              : 
     270            0 : const std::vector<std::pair<number, number> >& NeuriteProjector::quadrature_points() const
     271              : {
     272            0 :         return m_qPoints;
     273              : };
     274              : 
     275              : 
     276              : std::vector<NeuriteProjector::Section>::const_iterator
     277            0 : NeuriteProjector::get_section_iterator(uint32_t nid, float t) const
     278              : {
     279            0 :         Section cmpSec(t);
     280            0 :         const std::vector<Section>& vSections = m_vNeurites[nid].vSec;
     281              :         std::vector<Section>::const_iterator itSec =
     282            0 :                 std::lower_bound(vSections.begin(), vSections.end(), cmpSec, CompareSections());
     283              : 
     284            0 :         UG_COND_THROW(itSec == vSections.end(),
     285              :                 "Could not find section for parameter t = " << t << " in neurite " << nid << ".");
     286              : 
     287            0 :         return itSec;
     288              : }
     289              : 
     290              : std::vector<NeuriteProjector::Section>::const_iterator
     291            0 : NeuriteProjector::get_soma_section_iterator(uint32_t nid, float t) const
     292              : {
     293            0 :         Section cmpSec(t);
     294            0 :         const std::vector<Section>& vSections = m_vNeurites[nid].vSomaSec;
     295              :         std::vector<Section>::const_iterator itSec =
     296            0 :                 std::lower_bound(vSections.begin(), vSections.end(), cmpSec, CompareSections());
     297              : 
     298            0 :         UG_COND_THROW(itSec == vSections.end(),
     299              :                 "Could not find section for parameter t = " << t << " in neurite " << nid << ".");
     300              : 
     301            0 :         return itSec;
     302              : }
     303              : 
     304              : 
     305            0 : static bool cmpQPairs(const std::pair<number, number>& a, const std::pair<number, number>& b)
     306            0 : {return a.first < b.first;}
     307              : 
     308            0 : void NeuriteProjector::prepare_quadrature()
     309              : {
     310              :         /*
     311              :     GaussLegendre gl(m_quadOrder);
     312              :     size_t nPts = gl.size();
     313              :     m_qPoints.resize(nPts);
     314              :     for (size_t i = 0; i < nPts; ++i)
     315              :     {
     316              :         m_qPoints[i].first = gl.point(i)[0];
     317              :         m_qPoints[i].second = gl.weight(i);
     318              :     }
     319              :         */
     320            0 :         m_qPoints.resize(40);
     321              :         /// points
     322            0 :         m_qPoints[0].first = 4.8061379124697458903340327798768835266e-1;
     323            0 :         m_qPoints[1].first = 5.1938620875302541096659672201231164734e-1;
     324            0 :         m_qPoints[2].first = 4.4195796466237239575827435779598794312e-1;
     325            0 :         m_qPoints[3].first = 5.5804203533762760424172564220401205688e-1;
     326            0 :         m_qPoints[4].first = 4.0365120964931445014224157396742505259e-1;
     327            0 :         m_qPoints[5].first = 5.9634879035068554985775842603257494741e-1;
     328            0 :         m_qPoints[6].first = 3.6592390749637315942940782759570190829e-1;
     329            0 :         m_qPoints[7].first = 6.3407609250362684057059217240429809171e-1;
     330            0 :         m_qPoints[8].first = 3.2900295458712076349625375941040284497e-1;
     331            0 :         m_qPoints[9].first = 6.7099704541287923650374624058959715503e-1;
     332            0 :         m_qPoints[10].first = 2.9311039781419749923756012709814315851e-1;
     333            0 :         m_qPoints[11].first = 7.0688960218580250076243987290185684149e-1;
     334            0 :         m_qPoints[12].first = 2.584620991569106435457167128775884977e-1;
     335            0 :         m_qPoints[13].first = 7.415379008430893564542832871224115023e-1;
     336            0 :         m_qPoints[14].first = 2.2526643745243589896203434723524101488e-1;
     337            0 :         m_qPoints[15].first = 7.7473356254756410103796565276475898512e-1;
     338            0 :         m_qPoints[16].first = 1.9372305516600988102369377488465256131e-1;
     339            0 :         m_qPoints[17].first = 8.0627694483399011897630622511534743869e-1;
     340            0 :         m_qPoints[18].first = 1.6402165769291022581032274251925294501e-1;
     341            0 :         m_qPoints[19].first = 8.3597834230708977418967725748074705499e-1;
     342            0 :         m_qPoints[20].first = 1.3634087240503644835950177412253472572e-1;
     343            0 :         m_qPoints[21].first = 8.6365912759496355164049822587746527428e-1;
     344            0 :         m_qPoints[22].first = 1.1084717428674030615251422724675257599e-1;
     345            0 :         m_qPoints[23].first = 8.8915282571325969384748577275324742401e-1;
     346            0 :         m_qPoints[24].first = 8.7693884583344168401839884666950613046e-2;
     347            0 :         m_qPoints[25].first = 9.1230611541665583159816011533304938695e-1;
     348            0 :         m_qPoints[26].first = 6.7020248393870248089609095822690018215e-2;
     349            0 :         m_qPoints[27].first = 9.3297975160612975191039090417730998179e-1;
     350            0 :         m_qPoints[28].first = 4.8950596515562851635873334565753448208e-2;
     351            0 :         m_qPoints[29].first = 9.5104940348443714836412666543424655179e-1;
     352            0 :         m_qPoints[30].first = 3.3593595860661733319573916577397141783e-2;
     353            0 :         m_qPoints[31].first = 9.6640640413933826668042608342260285822e-1;
     354            0 :         m_qPoints[32].first = 2.1041590393104172097729500273620357453e-2;
     355            0 :         m_qPoints[33].first = 9.7895840960689582790227049972637964255e-1;
     356            0 :         m_qPoints[34].first = 1.1370025008112868668314858143548096511e-2;
     357            0 :         m_qPoints[35].first = 9.8862997499188713133168514185645190349e-1;
     358            0 :         m_qPoints[36].first = 4.6368806502714967734728238893139225189e-3;
     359            0 :         m_qPoints[37].first = 9.9536311934972850322652717611068607748e-1;
     360            0 :         m_qPoints[38].first = 8.8114514472039982518864878970675383211e-4;
     361            0 :         m_qPoints[39].first = 9.9911885485527960017481135121029324617e-1;
     362              : 
     363              :         /// weights
     364            0 :         m_qPoints[0].second = 3.8752973989212405631861981479163163482e-2;
     365            0 :         m_qPoints[1].second = 3.8752973989212405631861981479163163482e-2;
     366            0 :         m_qPoints[2].second = 3.8519909082123982794153767141905124262e-2;
     367            0 :         m_qPoints[3].second = 3.8519909082123982794153767141905124262e-2;
     368            0 :         m_qPoints[4].second = 3.8055180950313121185779037961247411506e-2;
     369            0 :         m_qPoints[5].second = 3.8055180950313121185779037961247411506e-2;
     370            0 :         m_qPoints[6].second = 3.7361584528984132100094668130662336596e-2;
     371            0 :         m_qPoints[7].second = 3.7361584528984132100094668130662336596e-2;
     372            0 :         m_qPoints[8].second = 3.6443291197902029530255341721258917929e-2;
     373            0 :         m_qPoints[9].second = 3.6443291197902029530255341721258917929e-2;
     374            0 :         m_qPoints[10].second = 3.530582369564338984774181542764341618e-2;
     375            0 :         m_qPoints[11].second = 3.530582369564338984774181542764341618e-2;
     376            0 :         m_qPoints[12].second = 3.3956022907616951912845054115961992992e-2;
     377            0 :         m_qPoints[13].second = 3.3956022907616951912845054115961992992e-2;
     378            0 :         m_qPoints[14].second = 3.2402006728300519037277264783376365016e-2;
     379            0 :         m_qPoints[15].second = 3.2402006728300519037277264783376365016e-2;
     380            0 :         m_qPoints[16].second = 3.0653121246464469583268998204199297951e-2;
     381            0 :         m_qPoints[17].second = 3.0653121246464469583268998204199297951e-2;
     382            0 :         m_qPoints[18].second = 2.87198845496957756833088654552129928e-2;
     383            0 :         m_qPoints[19].second = 2.87198845496957756833088654552129928e-2;
     384            0 :         m_qPoints[20].second = 2.6613923491968412177498239886130252278e-2;
     385            0 :         m_qPoints[21].second = 2.6613923491968412177498239886130252278e-2;
     386            0 :         m_qPoints[22].second = 2.4347903817536116030717080224073194034e-2;
     387            0 :         m_qPoints[23].second = 2.4347903817536116030717080224073194034e-2;
     388            0 :         m_qPoints[24].second = 2.1935454092836635995837343020857747906e-2;
     389            0 :         m_qPoints[25].second = 2.1935454092836635995837343020857747906e-2;
     390            0 :         m_qPoints[26].second = 1.9391083987236008819986015645223081127e-2;
     391            0 :         m_qPoints[27].second = 1.9391083987236008819986015645223081127e-2;
     392            0 :         m_qPoints[28].second = 1.6730097641273923696339091543205424489e-2;
     393            0 :         m_qPoints[29].second = 1.6730097641273923696339091543205424489e-2;
     394            0 :         m_qPoints[30].second = 1.3968503490011700549244578753860538651e-2;
     395            0 :         m_qPoints[31].second = 1.3968503490011700549244578753860538651e-2;
     396            0 :         m_qPoints[32].second = 1.1122924597083478630752162092104286604e-2;
     397            0 :         m_qPoints[33].second = 1.1122924597083478630752162092104286604e-2;
     398            0 :         m_qPoints[34].second = 8.2105291909539443564317424411819636462e-3;
     399            0 :         m_qPoints[35].second = 8.2105291909539443564317424411819636462e-3;
     400            0 :         m_qPoints[36].second = 5.2491422655764068073710855336398261884e-3;
     401            0 :         m_qPoints[37].second = 5.2491422655764068073710855336398261884e-3;
     402            0 :         m_qPoints[38].second = 2.2606385492665956292358664390926663639e-3;
     403            0 :         m_qPoints[39].second = 2.2606385492665956292358664390926663639e-3;
     404              : 
     405            0 :         std::sort(m_qPoints.begin(), m_qPoints.end(), cmpQPairs);
     406            0 : }
     407              : 
     408            0 : static void compute_first_section_end_points
     409              : (
     410              :         vector3& pos0Out,
     411              :         vector3& pos1Out,
     412              :         std::vector<NeuriteProjector::Section>::const_iterator secIt
     413              : )
     414              : {
     415            0 :         number te = secIt->endParam;
     416              :         const number* s = &secIt->splineParamsX[0];
     417              :         number& p0 = pos0Out[0];
     418            0 :         p0 = s[0]*te + s[1];
     419            0 :         p0 = p0*te + s[2];
     420            0 :         p0 = p0*te + s[3];
     421            0 :         pos1Out[0] = s[3];
     422              : 
     423              :         s = &secIt->splineParamsY[0];
     424              :         number& p1 = pos0Out[1];
     425            0 :         p1 = s[0]*te + s[1];
     426            0 :         p1 = p1*te + s[2];
     427            0 :         p1 = p1*te + s[3];
     428            0 :         pos1Out[1] = s[3];
     429              : 
     430              :         s = &secIt->splineParamsZ[0];
     431              :         number& p2 = pos0Out[2];
     432            0 :         p2 = s[0]*te + s[1];
     433            0 :         p2 = p2*te + s[2];
     434            0 :         p2 = p2*te + s[3];
     435            0 :         pos1Out[2] = s[3];
     436            0 : }
     437              : 
     438              : 
     439            0 : static void compute_position_and_velocity_in_section
     440              : (
     441              :         vector3& posAxOut,
     442              :         vector3& velOut,
     443              :         number& radiusOut,
     444              :         std::vector<NeuriteProjector::Section>::const_iterator secIt,
     445              :         float t
     446              : )
     447              : {
     448            0 :         number te = secIt->endParam;
     449              :         const number* s = &secIt->splineParamsX[0];
     450              :         number& p0 = posAxOut[0];
     451              :         number& v0 = velOut[0];
     452            0 :         p0 = s[0]*(te-t) + s[1];  v0 = -3.0*s[0]*(te-t) - 2.0*s[1];
     453            0 :         p0 = p0*(te-t) + s[2];    v0 = v0*(te-t) - s[2];
     454            0 :         p0 = p0*(te-t) + s[3];
     455              : 
     456              :         s = &secIt->splineParamsY[0];
     457              :         number& p1 = posAxOut[1];
     458              :         number& v1 = velOut[1];
     459            0 :         p1 = s[0]*(te-t) + s[1];  v1 = -3.0*s[0]*(te-t) - 2.0*s[1];
     460            0 :         p1 = p1*(te-t) + s[2];    v1 = v1*(te-t) - s[2];
     461            0 :         p1 = p1*(te-t) + s[3];
     462              : 
     463              :         s = &secIt->splineParamsZ[0];
     464              :         number& p2 = posAxOut[2];
     465              :         number& v2 = velOut[2];
     466            0 :         p2 = s[0]*(te-t) + s[1];  v2 = -3.0*s[0]*(te-t) - 2.0*s[1];
     467            0 :         p2 = p2*(te-t) + s[2];    v2 = v2*(te-t) - s[2];
     468            0 :         p2 = p2*(te-t) + s[3];
     469              : 
     470              :         s = &secIt->splineParamsR[0];
     471            0 :         radiusOut = s[0]*(te-t) + s[1];
     472            0 :         radiusOut = radiusOut*(te-t) + s[2];
     473            0 :         radiusOut = radiusOut*(te-t) + s[3];
     474            0 : }
     475              : 
     476              : 
     477              : static void compute_radius_in_section
     478              : (
     479              :         number& radiusOut,
     480              :         std::vector<NeuriteProjector::Section>::const_iterator secIt,
     481              :         float t
     482              : )
     483              : {
     484            0 :         number te = secIt->endParam;
     485              :         const number* s = &secIt->splineParamsR[0];
     486            0 :         radiusOut = s[0]*(te-t) + s[1];
     487            0 :         radiusOut = radiusOut*(te-t) + s[2];
     488            0 :         radiusOut = radiusOut*(te-t) + s[3];
     489              : }
     490              : 
     491              : 
     492            0 : static inline void compute_ONB
     493              : (
     494              :         vector3& firstOut,
     495              :         vector3& secondOut,
     496              :         vector3& thirdOut,
     497              :         const vector3& firstIn,
     498              :         const vector3& refDir
     499              : )
     500              : {
     501            0 :         VecNormalize(firstOut, firstIn);
     502              :         number fac = VecProd(refDir, firstOut);
     503            0 :         VecScaleAdd(secondOut, 1.0, refDir, -fac, firstOut);
     504            0 :         VecNormalize(secondOut, secondOut);
     505              : 
     506            0 :         VecCross(thirdOut, firstOut, secondOut);
     507            0 : }
     508              : 
     509              : 
     510              : // computes the polar angle phi of a vector
     511              : // given a rotational axis direction and two additional
     512              : // directions, together being a orthoNORMAL basis
     513            0 : static inline number compute_angle
     514              : (
     515              :         const vector3& axis,
     516              :         const vector3& secondAxis,
     517              :         const vector3& thirdAxis,
     518              :         const vector3& posFromAxis
     519              : )
     520              : {
     521              :         // eliminate component in axis direction
     522              :         vector3 posFromAxisCopy;
     523            0 :         VecScaleAdd(posFromAxisCopy, 1.0, posFromAxis, -VecProd(posFromAxis, axis), axis);
     524              : 
     525              :         // compute coord vector (components 2 and 3) w.r.t. ONB
     526              :         vector2 relCoord;
     527            0 :         relCoord[0] = VecProd(posFromAxis, secondAxis);
     528            0 :         VecScaleAdd(posFromAxisCopy, 1.0, posFromAxisCopy, -relCoord[0], secondAxis);
     529            0 :         relCoord[1] = VecProd(posFromAxisCopy, thirdAxis);
     530            0 :         VecNormalize(relCoord, relCoord);
     531              : 
     532              :         // get angle from coord vector
     533              :         number angle;
     534            0 :         if (fabs(relCoord[0]) < 1e-8)
     535            0 :                 angle = relCoord[1] < 0 ? 1.5*PI : 0.5*PI;
     536              :         else
     537            0 :                 angle = relCoord[0] < 0 ? PI - atan(-relCoord[1]/relCoord[0]) : atan(relCoord[1]/relCoord[0]);
     538              : 
     539              :         // angle should be in [0,2*PI)
     540            0 :         if (angle < 0) angle += 2.0*PI;
     541              : 
     542            0 :         return angle;
     543              : }
     544              : 
     545              : 
     546            0 : void NeuriteProjector::average_params
     547              : (
     548              :         uint32_t& neuriteID,
     549              :         float& t,
     550              :         float& angle,
     551              :         float& radius,
     552              :         const IVertexGroup* parent
     553              : ) const
     554              : {
     555              :         // find neurite ID parent belongs to
     556            0 :         size_t nVrt = parent->num_vertices();
     557              :         //std::vector<std::vector<std::pair<uint32_t, uint32_t> > > candidates(nVrt);
     558              :         std::map<uint32_t, size_t> candidates;
     559              :         std::map<uint32_t, uint32_t> branchInfo;
     560            0 :         for (size_t i = 0; i < nVrt; ++i)
     561              :         {
     562            0 :                 const SurfaceParams& surfParams = m_aaSurfParams[parent->vertex(i)];
     563            0 :                 uint32_t thisNID = surfParams.neuriteID & ((1 << 20) - 1);
     564            0 :                 ++candidates[thisNID];
     565              : 
     566            0 :                 if (surfParams.neuriteID > (1 << 20))  // vrt also belongs to branch(es)
     567              :                 {
     568            0 :                         const uint32_t br = (surfParams.neuriteID & (255 << 20)) >> 20;
     569            0 :                         const std::vector<uint32_t>& vNID = m_vNeurites[thisNID].vBR[br].bp->vNid;
     570            0 :                         const uint32_t children = surfParams.neuriteID >> 28;
     571            0 :                         for (int j = 0; j < 4; ++j)
     572              :                         {
     573            0 :                                 if (children & (1 << j))
     574              :                                 {
     575            0 :                                         ++candidates[vNID[j+1]];
     576            0 :                                         branchInfo[vNID[j+1]] = (br << 20) + (1 << (28+j));
     577              :                                 }
     578              :                         }
     579              :                 }
     580              :         }
     581              : 
     582              :         // vertex gets all NIDs that have maximal counts
     583              :         std::vector<uint32_t> maxCands;
     584              :         size_t maxCnt = 0;
     585              : 
     586              :         std::map<uint32_t, size_t>::const_iterator it = candidates.begin();
     587              :         std::map<uint32_t, size_t>::const_iterator itEnd = candidates.end();
     588            0 :         for (; it != itEnd; ++it)
     589            0 :                 if (it->second > maxCnt)
     590              :                         maxCnt = it->second;
     591            0 :         for (it = candidates.begin(); it != itEnd; ++it)
     592            0 :                 if (it->second == maxCnt)
     593            0 :                         maxCands.push_back(it->first);
     594              : 
     595              :         const size_t nMaxCands = maxCands.size();
     596            0 :         if (nMaxCands == 1)
     597            0 :                 neuriteID = maxCands[0];
     598              :         else
     599              :         {
     600            0 :                 neuriteID = *std::min_element(maxCands.begin(), maxCands.end());
     601            0 :                 for (size_t i = 0; i < nMaxCands; ++i)
     602              :                 {
     603              :                         std::map<uint32_t, uint32_t>::const_iterator it2 = branchInfo.find(maxCands[i]);
     604            0 :                         if (it2 != branchInfo.end())
     605            0 :                                 neuriteID = neuriteID | it2->second;
     606              :                 }
     607              :         }
     608              : 
     609              : 
     610              :         // special case:
     611              :         // when refining the initial BP volume the center vertex needs to belong
     612              :         // to all branches and the parent
     613            0 :         if (branchInfo.size() && nVrt == 8)
     614              :         {
     615              :                 // all must have the same radius and parent
     616              :                 // and all children need to have count 4
     617            0 :                 const number firstRad = m_aaSurfParams[parent->vertex(0)].radial;
     618            0 :                 const uint32_t firstParent = m_aaSurfParams[parent->vertex(0)].neuriteID & ((1 << 20) - 1);
     619              :                 bool allSame = true;
     620            0 :                 for (size_t i = 1; i < 8; ++i)
     621              :                 {
     622            0 :                         if (m_aaSurfParams[parent->vertex(i)].radial != firstRad
     623            0 :                                 || (m_aaSurfParams[parent->vertex(i)].neuriteID & ((1 << 20) - 1)) != firstParent)
     624              :                         {
     625              :                                 allSame = false;
     626              :                                 break;
     627              :                         }
     628              :                 }
     629              : 
     630            0 :                 if (allSame)
     631              :                 {
     632              :                         bool allChildCounts4 = true;
     633              : 
     634              :                         std::map<uint32_t, uint32_t>::const_iterator it2 = branchInfo.begin();
     635              :                         std::map<uint32_t, uint32_t>::const_iterator it2End = branchInfo.end();
     636            0 :                         for (; it2 != it2End; ++it2)
     637              :                         {
     638            0 :                                 if (candidates[it2->first] != 4)
     639              :                                 {
     640              :                                         allChildCounts4 = false;
     641              :                                         break;
     642              :                                 }
     643              :                         }
     644              : 
     645            0 :                         if (allChildCounts4)
     646              :                         {
     647            0 :                                 neuriteID = candidates.begin()->first;
     648            0 :                                 for (it2 = branchInfo.begin(); it2 != it2End; ++it2)
     649            0 :                                         neuriteID = neuriteID | it2->second;
     650              :                         }
     651              :                 }
     652              :         }
     653              : 
     654              :         // special case:
     655              :         // when refining the initial connecting quadrilateral face of a branch
     656              :         // the center vertex needs to belong exclusively to the branch, not to the parent
     657            0 :         if (nMaxCands == 2 && nVrt == 4 && maxCnt == 4)
     658            0 :                 neuriteID = std::max(maxCands[0], maxCands[1]);
     659              : 
     660            0 :         const uint32_t plainNID = neuriteID & ((1 << 20) - 1);
     661              : 
     662              : 
     663              :         // average t, phi, r
     664            0 :         t = 0.0;
     665            0 :         float minRad = 1.0;
     666            0 :         float maxRad = 0.0;
     667              :         vector2 v(0.0);
     668              :         size_t nForeignParents = 0;
     669            0 :         std::vector<number> foreignParentRad(8, 0.0);
     670              :         vector3 foreignParentPosCenter = 0.0;
     671              :         number foreignParentsCenterRadial = 0.0;
     672              : 
     673              :         size_t nForeignChildren = 0;
     674              :         vector3 foreignChildrenPosCenter = 0.0;
     675              : 
     676            0 :         for (size_t i = 0; i < nVrt; ++i)
     677              :         {
     678            0 :                 const SurfaceParams& surfParams = m_aaSurfParams[parent->vertex(i)];
     679            0 :                 uint32_t nid = surfParams.neuriteID & ((1 << 20) - 1);
     680            0 :                 minRad = std::min(minRad, surfParams.radial);
     681            0 :                 maxRad = std::max(maxRad, surfParams.radial);
     682              : 
     683              :                 // parent vertex belongs to our branch
     684            0 :                 if (nid == plainNID)
     685              :                 {
     686            0 :                         t += surfParams.axial;
     687            0 :                         if (surfParams.radial > 0 && surfParams.axial < 2.0)
     688              :                         {
     689            0 :                                 const float phi = surfParams.angular;
     690            0 :                                 VecAdd(v, v, vector2(surfParams.radial*cos(phi), surfParams.radial*sin(phi)));
     691              :                         }
     692              :                 }
     693              : 
     694              :                 // parent vertex belongs to parent branch
     695            0 :                 else if (nid < plainNID)
     696              :                 {
     697            0 :                         foreignParentPosCenter += this->pos(parent->vertex(i));
     698            0 :                         foreignParentRad[nForeignParents] = surfParams.radial;
     699            0 :                         ++nForeignParents;
     700              :                 }
     701              : 
     702              :                 // parent vertex belongs to child branch
     703              :                 else
     704              :                 {
     705            0 :                         foreignChildrenPosCenter += this->pos(parent->vertex(i));
     706            0 :                         ++nForeignChildren;
     707              :                 }
     708              :         }
     709              : 
     710              :         // for parent vertices, calculate the t and phi params of their center
     711              :         // w.r.t. branching neurite
     712            0 :         if (nForeignParents)
     713              :         {
     714            0 :                 foreignParentPosCenter /= nForeignParents;
     715              : 
     716            0 :                 std::vector<Section>::const_iterator secIt = m_vNeurites[plainNID].vSec.begin();
     717              :                 vector3 pos0, pos1;
     718            0 :                 compute_first_section_end_points(pos0, pos1, secIt);
     719              :                 VecSubtract(pos1, pos1, pos0);
     720              :                 VecSubtract(pos0, foreignParentPosCenter, pos0);
     721            0 :                 number axPos = VecProd(pos0, pos1) / VecNormSquared(pos1) * secIt->endParam;
     722              : 
     723              :                 vector3 vel;
     724              :                 number surfRadius;
     725            0 :                 compute_position_and_velocity_in_section(pos1, vel, surfRadius, secIt, axPos);
     726            0 :                 const vector3& refDir = m_vNeurites[plainNID].refDir;
     727              :                 vector3 posRefDir, thirdDir;
     728            0 :                 compute_ONB(vel, posRefDir, thirdDir, vel, refDir);
     729              :                 vector3 posFromAxis;
     730              :                 VecSubtract(posFromAxis, foreignParentPosCenter, pos1);
     731            0 :                 number phi = compute_angle(vel, posRefDir, thirdDir, posFromAxis);
     732              : 
     733            0 :                 t += nForeignParents*axPos;
     734            0 :                 foreignParentsCenterRadial = VecNorm(posFromAxis) / surfRadius;
     735            0 :                 VecScaleAdd(v, 1.0, v, nForeignParents, vector2(foreignParentsCenterRadial*cos(phi), foreignParentsCenterRadial*sin(phi)));
     736              :         }
     737              : 
     738              :         // for child vertices, calculate the t and phi params of their center
     739              :         // w.r.t. parent neurite TODO: ATM this is not done as it is not overly important.
     740            0 :         if (nForeignChildren)
     741              :         {
     742            0 :                 t *= ((number) nVrt) / (nVrt - nForeignChildren);
     743              :                 VecScale(v, v, ((number) nVrt) / (nVrt - nForeignChildren));
     744              :         }
     745              : 
     746              : 
     747            0 :         t /= nVrt;
     748            0 :         radius = 0.5*(maxRad + minRad);
     749              : 
     750              :         // special case initial face of a branch:
     751              :         // move frontier between branch and parent into branch
     752            0 :         if (nForeignParents == 4 && nVrt == 4)
     753            0 :                 t *= sqrt(2.0);
     754              : 
     755              :         // special case tip of neurite
     756            0 :         if (fabs(t-2.0) < 1e-8)
     757              :                 return;
     758              : 
     759            0 :         if (VecTwoNorm(v) / nVrt < 0.4 * radius && (nVrt == 4 || nVrt == 8))
     760              :         {
     761            0 :                 if (VecTwoNorm(v) / nVrt > 0.2 * radius)
     762              :                 {
     763            0 :                         UG_LOGN("HERE: radius " << radius << " -> 0  (real: " << VecTwoNorm(v) / nVrt << ")");
     764              :                         //UG_LOGN("      plainNID " << plainNID);
     765              :                         UG_LOGN("      nVrt " << nVrt);
     766              :                         UG_LOGN("      parent vertex positions");
     767            0 :                         for (size_t i = 0; i < nVrt; ++i)
     768            0 :                                 UG_LOGN("        " << this->position(parent->vertex(i)));
     769              :                 }
     770              :                 angle = 0.0;
     771            0 :                 radius = 0.0;
     772              :         }
     773              : 
     774            0 :         if (fabs(v[0]) < 1e-4)
     775              :         {
     776            0 :                 if (fabs(v[1]) < 1e-4)  // do not alter radius in tip
     777              :                 {
     778            0 :                         angle = 0.0;  // center angle is 0.0 by default
     779            0 :                         radius = 0.0;
     780              :                 }
     781              :                 else
     782            0 :                         angle = v[1] < 0 ? 1.5*PI : 0.5*PI;
     783              :         }
     784              :         else
     785            0 :                 angle = v[0] < 0 ? PI - atan(-v[1]/v[0]) : atan(v[1]/v[0]);
     786              : 
     787            0 :         if (angle < 0)
     788            0 :                 angle += 2.0*PI;
     789              : 
     790            0 : }
     791              : 
     792              : 
     793            0 : void NeuriteProjector::average_pos_from_parent(vector3& posOut, const IVertexGroup* const parent) const
     794              : {
     795              :         posOut = 0.0;
     796            0 :         size_t nVrt = parent->num_vertices();
     797            0 :         for (size_t i = 0; i < nVrt; ++i)
     798            0 :                 posOut += this->pos(parent->vertex(i));
     799            0 :         posOut /= (number) nVrt;
     800            0 : }
     801              : 
     802              : 
     803              : /*
     804              : void NeuriteProjector::average_pos_from_parent_weighted(vector3& posOut, const IVertexGroup* parent) const
     805              : {
     806              :         MultiGrid* mg = dynamic_cast<MultiGrid*>(&this->geometry()->grid());
     807              :         UG_COND_THROW(!mg, "Underlying grid is not a multigrid.");
     808              :         int lv = mg->get_level<Vertex>(parent->vertex(0));
     809              :         if (lv == 0)
     810              :                 lv = 1;
     811              : 
     812              :         posOut = 0.0;
     813              :         size_t nVrt = parent->num_vertices();
     814              :         std::vector<int> vSheathID(nVrt);
     815              :         vSheathID[0] = 0;
     816              :         int sheathID[2] = {std::round(m_aaSurfParams[parent->vertex(0)].radial*(1 << (lv-1))), -1};
     817              :         int count[2] = {1, 0};
     818              :         for (size_t i = 1; i < nVrt; ++i)
     819              :         {
     820              :                 int thisSheathID = std::round(m_aaSurfParams[parent->vertex(i)].radial*(1 << (lv-1)));
     821              :                 if (thisSheathID != sheathID[0] && sheathID[1] == -1)
     822              :                         sheathID[1] = thisSheathID;
     823              :                 if (thisSheathID == sheathID[0])
     824              :                 {
     825              :                         ++count[0];
     826              :                         vSheathID[i] = 0;
     827              :                 }
     828              :                 else if (thisSheathID == sheathID[1])
     829              :                 {
     830              :                         ++count[1];
     831              :                         vSheathID[i] = 1;
     832              :                 }
     833              :                 else
     834              :                         UG_THROW("An element must only be part of at most 2 sheaths.");
     835              :         }
     836              : 
     837              :         if (!count[1])
     838              :                 count[1] = 1;
     839              : 
     840              :         for (size_t i = 0; i < nVrt; ++i)
     841              :                 VecScaleAppend(posOut, count[1-vSheathID[i]], this->pos(parent->vertex(i)));
     842              : 
     843              :         posOut /= 2*count[0]*count[1];
     844              : }
     845              : */
     846              : 
     847              : 
     848            0 : vector3 NeuriteProjector::position(Vertex* vrt) const
     849              : {
     850            0 :         return this->pos(vrt);
     851              : }
     852              : 
     853            0 : number NeuriteProjector::axial_range_around_branching_region
     854              : (
     855              :         uint32_t nid,
     856              :         size_t brInd,
     857              :         number numberOfRadii
     858              : ) const
     859              : {
     860            0 :         const Neurite& neurite = m_vNeurites[nid];
     861              :         const BranchingRegion& br = neurite.vBR[brInd];
     862              : 
     863              :         std::vector<Section>::const_iterator secIt;
     864            0 :         try {secIt = get_section_iterator(nid, br.t);}
     865            0 :         UG_CATCH_THROW("Could not get section iterator to branching region " << brInd << " at t = " << br.t
     866              :                 << " of neurite " << nid << " (which has " << neurite.vBR.size() << " branching regions).");
     867              : 
     868              :         vector3 secStartPos;
     869              :         vector3 secEndPos;
     870            0 :         const number te = secIt->endParam;
     871              :         number ts = 0.0;
     872            0 :         if (secIt == m_vNeurites[nid].vSec.begin())
     873            0 :                 compute_first_section_end_points(secStartPos, secEndPos, secIt);
     874              :         else
     875              :         {
     876            0 :                 secEndPos[0] = secIt->splineParamsX[3];
     877            0 :                 secEndPos[1] = secIt->splineParamsY[3];
     878            0 :                 secEndPos[2] = secIt->splineParamsZ[3];
     879              :                 --secIt;
     880            0 :                 ts = secIt->endParam;
     881            0 :                 secStartPos[0] = secIt->splineParamsX[3];
     882            0 :                 secStartPos[1] = secIt->splineParamsY[3];
     883            0 :                 secStartPos[2] = secIt->splineParamsZ[3];
     884              :                 ++secIt;
     885              :         }
     886            0 :         const number neuriteLengthApprox = VecDistance(secEndPos, secStartPos) / (te - ts);
     887              : 
     888              :         const number* s = &secIt->splineParamsR[0];
     889            0 :         number radius = s[0]*(te-br.t) + s[1];
     890            0 :         radius = radius*(te-br.t) + s[2];
     891            0 :         radius = radius*(te-br.t) + s[3];
     892              : 
     893            0 :         return numberOfRadii*radius / neuriteLengthApprox;
     894              : }
     895              : 
     896              : 
     897            0 : void NeuriteProjector::print_surface_params(const Vertex* const v) const
     898              : {
     899              :         const SurfaceParams& sp = m_aaSurfParams[v];
     900            0 :         UG_LOGN("neuriteID: " << sp.neuriteID);
     901            0 :         UG_LOGN("axial:     " << sp.axial);
     902            0 :         UG_LOGN("angular:   " << sp.angular);
     903            0 :         UG_LOGN("radial:    " << sp.radial);
     904            0 : }
     905              : 
     906              : 
     907            0 : static void bp_defect_and_gradient
     908              : (
     909              :         number& defectOut,
     910              :         vector3& gradientOut,
     911              :         const std::vector<NeuriteProjector::BPProjectionHelper>& bpList,
     912              :         const vector3& x,
     913              :         number rad,
     914              :         const vector3& constAngleSurfNormal,
     915              :         const NeuriteProjector* np
     916              : )
     917              : {
     918              :         // get integration rule points and weights
     919            0 :         const std::vector<std::pair<number, number> >& qPoints = np->quadrature_points();
     920              :         size_t nPts = qPoints.size();
     921              : 
     922              :         // evaluate integrand at points
     923            0 :         defectOut = 0.0;
     924              :         gradientOut = 0.0;
     925              :         size_t nParts = bpList.size();
     926            0 :         for (size_t i = 0; i < nParts; ++i)
     927              :         {
     928              :                 const NeuriteProjector::BPProjectionHelper& helper = bpList[i];
     929              :                 const number& start = helper.start;
     930              :                 const number& end = helper.end;
     931            0 :                 std::vector<NeuriteProjector::Section>::const_iterator secIt = helper.sec_start;
     932              : 
     933              :                 // iterate IPs
     934            0 :                 for (size_t j = 0; j < nPts; ++j)
     935              :                 {
     936              :                         // transform point coords to current context
     937            0 :                         float t = (float) (start + qPoints[j].first*(end-start));
     938            0 :                         while (secIt->endParam < t) ++secIt;    // this should be safe as last section must end with 1.0
     939              : 
     940              :                         vector3 posAx, vel, blub;
     941              :                         number radius;
     942            0 :                         compute_position_and_velocity_in_section(posAx, vel, radius, secIt, t);
     943              : 
     944            0 :                         number radInv = 1.0 / (rad*radius);
     945              :                         VecSubtract(posAx, posAx, x);
     946              :                         number posNormSq = VecNormSquared(posAx);
     947            0 :                         number velNorm = sqrt(VecNormSquared(vel));
     948            0 :                         number integrandVal = radInv*exp(-posNormSq*radInv*radInv) * velNorm;
     949            0 :                         number gradIntegrandVal = 2.0*radInv*radInv * integrandVal;
     950              :                         VecScale(blub, posAx, gradIntegrandVal);
     951              : 
     952            0 :                         number w = qPoints[j].second;
     953            0 :                         defectOut += integrandVal * w * (end-start);
     954            0 :                         VecScaleAdd(gradientOut, 1.0, gradientOut, w * (end-start), blub);
     955              : #if 0
     956              :                         UG_LOGN("  Ival: " << integrandVal << ",  iExp: " << -posNormSq*radInv*radInv
     957              :                                 << ",  velNorm: " << velNorm << ",  weight: " << w
     958              :                                 << ",  t: " << t << ",  intvl: " << end-start
     959              :                                 << ", posAx: " << posAx << "x: " << x);
     960              : 
     961              :                         UG_LOGN(" gradientOut: " << gradientOut);
     962              : #endif
     963              :                 }
     964              :         }
     965              : 
     966            0 :         defectOut -= sqrt(PI)*exp(-1.0);
     967              : 
     968              :         // project gradient to surface of constant angle
     969            0 :         VecScaleAppend(gradientOut, -VecDot(constAngleSurfNormal, gradientOut), constAngleSurfNormal);
     970            0 : }
     971              : 
     972              : #if 0
     973              : static void pos_on_surface_soma
     974              : (
     975              :     vector3& posOut,
     976              :     const NeuriteProjector::Neurite& neurite,
     977              :     const NeuriteProjector* np,
     978              :     const IVertexGroup* parent
     979              : )
     980              : {
     981              :         if (parent) np->average_pos_from_parent(posOut, parent);
     982              : }
     983              : 
     984              : static void bp_defect_and_gradient_soma
     985              : (
     986              :     number& defectOut,
     987              :     vector3& gradientOut,
     988              :     const std::vector<NeuriteProjector::BPProjectionHelper>& bpList,
     989              :     const vector3& x,
     990              :     const NeuriteProjector* np,
     991              :         const vector3& constAngleSurfNormal
     992              : )
     993              : {
     994              :     // get integration rule points and weights
     995              :     const std::vector<std::pair<number, number> >& qPoints = np->quadrature_points();
     996              :     size_t nPts = qPoints.size();
     997              : 
     998              :     // evaluate integrand at points
     999              :     defectOut = 0.0;
    1000              :     gradientOut = 0.0;
    1001              :     const NeuriteProjector::BPProjectionHelper& helper = bpList[0];
    1002              :     const number& start = helper.start;
    1003              :     const number& end = helper.end;
    1004              :     const number& somaRadius = helper.sr->somaPt.get()->radius;
    1005              :     const vector3& posSoma = helper.sr->somaPt.get()->soma;
    1006              :     std::vector<NeuriteProjector::Section>::const_iterator secIt = helper.sec_start;
    1007              : 
    1008              :     // iterate IPs
    1009              :     for (size_t j = 0; j < nPts; ++j)
    1010              :     {
    1011              :          /// NEURITE part
    1012              :          // transform point coords to current context
    1013              :          float t = (float) (start + qPoints[j].first*(end-start));
    1014              : 
    1015              :          vector3 posAx, vel, blub;
    1016              :          number radius;
    1017              :          /// First section is always BP to soma PM or ER surface of inner sphere
    1018              :          // /t/=10000;
    1019              :          /// if (t < 0) { t = -t; }
    1020              :          UG_LOGN("Section information... t: " << t << ", secIt->endParam(): " << secIt->endParam);
    1021              :          compute_position_and_velocity_in_section(posAx, vel, radius, secIt, t);
    1022              :          UG_LOGN("position in neurite: " << posAx);
    1023              : 
    1024              :          /// FIXME: add back rad in denominator (rad*radius) from current vertex's aaSurfParams
    1025              :          UG_LOGN("x: " << x);
    1026              :          number radInv = 1.0 / (radius*radius);
    1027              :          VecSubtract(posAx, posAx, x);
    1028              :          number posNormSq = VecNormSquared(posAx);
    1029              :          number velNorm = sqrt(VecNormSquared(vel));
    1030              :          number integrandVal = radInv*exp(-posNormSq*radInv*radInv) * velNorm;
    1031              :          number gradIntegrandVal = 2.0*radInv*radInv * integrandVal;
    1032              :          VecScale(blub, posAx, gradIntegrandVal);
    1033              :          number w = qPoints[j].second;
    1034              : 
    1035              :          defectOut += integrandVal * w * (end-start);
    1036              :          VecScaleAdd(gradientOut, 1.0, gradientOut, w * (end-start), blub);
    1037              : 
    1038              :          UG_LOGN("posNeurite: " << posAx);
    1039              :          UG_LOGN("Neurite part information... radius: " << radius << ", "
    1040              :                          << "x: " << x << ", vel: " << vel << ", posAx: " << posAx
    1041              :                          << ",radInv: "<< radInv << ", velNorm: " << velNorm
    1042              :                          << ",posNormSq: " << posNormSq << ", w:" << w << ", j: "
    1043              :                          << j << ", start: " << start << ", end: " << end
    1044              :                          << ",defectOut neurite: " << integrandVal * w * (end-start)
    1045              :                          << ",integrand neurite:" << integrandVal
    1046              :                          << ",gradintegrand neurite: " << gradIntegrandVal
    1047              :                          << ",blub: " << blub);
    1048              :     }
    1049              :     UG_LOGN("gradientOut (neurite): " << gradientOut);
    1050              : 
    1051              :     /// SOMA surface PM part or ER surface of inner sphere part
    1052              :     vector3 posAxSoma;
    1053              :     UG_LOGN("posSoma (initial): " << posSoma);
    1054              :     /// VecSubtract(posAxSoma, posSoma, x);
    1055              :     /// TODO: Verify: Soma position means: in direction from soma center towards
    1056              :     /// current point position (x) scaled with t (axial) equals position!
    1057              :     vector3 dir;
    1058              :     VecSubtract(dir, x, posSoma);
    1059              :     // FIXME verify this: soma outer sphere axial = 0 inner ER sphere -1:
    1060              :     /// set position of soma between end and start region of integration
    1061              :     const number t = std::fabs(helper.sr->t) + helper.sr->t+(start-end)/2;
    1062              :     VecScale(dir, dir, 1+t/helper.sr->radius);
    1063              :     VecAdd(posAxSoma, posSoma, dir);
    1064              :     VecSubtract(posAxSoma, posAxSoma, x);
    1065              :     vector3 velSoma = posAxSoma;
    1066              :     number velSomaNorm = sqrt(VecNormSquared(velSoma));
    1067              :     number posNormSomaSq = VecNormSquared(posAxSoma);
    1068              :     UG_LOGN("t (Soma): " << t);
    1069              :     UG_LOGN("somaPos: " << posAxSoma);
    1070              :     number radInvSoma = 1.0 / ((1+t)*somaRadius);
    1071              :     number gradIntegrandValSoma = radInvSoma * exp(-posNormSomaSq*radInvSoma*radInvSoma) * velSomaNorm;
    1072              :     vector3 blubSoma;
    1073              :     VecScale(blubSoma, posAxSoma, gradIntegrandValSoma);
    1074              :     VecScaleAdd(gradientOut, 1.0, gradientOut, 1.0 * (end-start), blubSoma);
    1075              : 
    1076              :     defectOut += gradIntegrandValSoma * 1.0 * (end-start);
    1077              :     UG_LOGN("defectOut (soma):" << gradIntegrandValSoma * 1.0 * (end-start));
    1078              : 
    1079              :     UG_LOGN("Soma part information... radius: " << (1+t)*somaRadius << ", "
    1080              :                  << "x: " << x << ", vel: " << velSoma << ", posAx: " << posAxSoma
    1081              :                  << ",radInv: "<< radInvSoma << ", velNorm: " << velSomaNorm
    1082              :                  << ",posNormSq: " << posNormSomaSq << ", w:" << 1.0 << ", j: "
    1083              :                  << 1 << ", start: " << start << ", end: " << end
    1084              :                  << ",defectOut neurite: " << gradIntegrandValSoma * 1.0 * (end-start)
    1085              :                  << ",gradIntegrand neurite:" << gradIntegrandValSoma
    1086              :                  << ",blub: " << blubSoma);
    1087              : 
    1088              :     /// TODO: Is the defect calculation correct? It seems we do not account for the added soma point to the integrand?
    1089              :     defectOut -= sqrt(PI)*exp(-1.0);
    1090              :     UG_LOGN("defectOut (total): " << defectOut);
    1091              :     UG_LOGN("gradientOut (total): " << gradientOut);
    1092              : 
    1093              :         // TODO: project gradient to surface of constant angle: Is this still correct to use after adding the soma point to the integrand?
    1094              :         VecScaleAppend(gradientOut, -VecDot(constAngleSurfNormal, gradientOut), constAngleSurfNormal);
    1095              : }
    1096              : 
    1097              : 
    1098              : static void newton_for_soma_bp_projection
    1099              : (
    1100              :     vector3& posOut,
    1101              :     const std::vector<NeuriteProjector::BPProjectionHelper>& vProjHelp,
    1102              :     const NeuriteProjector* np,
    1103              :         const vector3& constAngleSurfNormal
    1104              : )
    1105              : {
    1106              :         vector3 posStart = posOut;
    1107              :     UG_LOGN("posOut (iter):" << posOut);
    1108              :     // calculate start defect and gradient
    1109              :     number def;
    1110              :     number def_init;
    1111              :     vector3 grad;
    1112              :     bp_defect_and_gradient_soma(def, grad, vProjHelp, posOut, np, constAngleSurfNormal);
    1113              :         def_init = fabs(def);
    1114              :         // if initially gradient is zero, better starting position has to be chosen
    1115              :     UG_LOGN("def initial: " << def);
    1116              :     UG_LOGN("gradient initial: " << grad);
    1117              : 
    1118              :     // perform some kind of Newton search for the correct position
    1119              :     size_t maxIt = 100;
    1120              :     number minDef = 1e-8*sqrt(PI)*exp(-1.0);
    1121              :     size_t iter = 0;
    1122              :     number corr = 1.0;
    1123              : 
    1124              :     while (fabs(def) > minDef && fabs(def) > 1e-8 * def_init && ++iter <= maxIt)
    1125              :     {
    1126              :         UG_LOGN("First while loop")
    1127              :         vector3 posTest;
    1128              :         vector3 gradTest;
    1129              :         number defTest;
    1130              :         // start with reduced step size to prevent overshooting in case the start position is too bad
    1131              :         corr /= 2;
    1132              :         UG_LOGN("-def: " << -def);
    1133              :         UG_LOGN("VecNormSquared(grad): " << VecNormSquared(grad));
    1134              :         UG_LOGN("grad: " << grad);
    1135              :         VecScaleAdd(posTest, 1.0, posOut, -(1.0-corr)*def / VecNormSquared(grad), grad);
    1136              :         bp_defect_and_gradient_soma(defTest, gradTest, vProjHelp, posTest, np, constAngleSurfNormal);
    1137              :         UG_COND_THROW(std::fabs(VecNorm(gradTest)) < SMALL, "Quasi-zero gradient: Iteration will always fail.");
    1138              : 
    1139              :         // line search
    1140              :         number lambda = 0.5;
    1141              :         number bestDef = defTest;
    1142              :         vector3 bestGrad = gradTest;
    1143              :         vector3 bestPos = posTest;
    1144              :         while (fabs(defTest) >= fabs(def) && lambda > 0.001)
    1145              :         {
    1146              :                 UG_LOGN("Second while loop")
    1147              :             VecScaleAdd(posTest, 1.0, posOut, -lambda*def / VecNormSquared(grad), grad);
    1148              :             bp_defect_and_gradient_soma(defTest, gradTest, vProjHelp, posTest, np, constAngleSurfNormal);
    1149              :             if (fabs(defTest) < fabs(bestDef))
    1150              :             {
    1151              :                 bestDef = defTest;
    1152              :                 bestGrad = gradTest;
    1153              :                 bestPos = posTest;
    1154              :             }
    1155              :             lambda *= 0.5;
    1156              :         }
    1157              :         def = bestDef;
    1158              :         grad = gradTest;
    1159              :         posOut = bestPos;
    1160              :     }
    1161              :     UG_COND_THROW(def != def,
    1162              :         "Newton iteration did not converge for soma branching point projection at " << posStart << ". Defect is NaN.")
    1163              :     UG_COND_THROW(fabs(def) > minDef && fabs(def) > 1e-8 * def_init,
    1164              :         "Newton iteration did not converge for soma branching point projection at " << posStart << ".")
    1165              : }
    1166              : #endif
    1167              : 
    1168              : 
    1169              : #if 0
    1170              : static void pos_on_neurite_spine
    1171              : (
    1172              :         vector3& posOut,
    1173              :         const NeuriteProjector::Neurite& neurite,
    1174              :         size_t neuriteID,
    1175              :         float& t
    1176              : )
    1177              : {
    1178              :         const std::vector<NeuriteProjector::Section>& vSections = neurite.vSec;
    1179              : 
    1180              :         // find correct section
    1181              :         NeuriteProjector::Section cmpSec(t);
    1182              :         std::vector<NeuriteProjector::Section>::const_iterator it =
    1183              :                 std::lower_bound(vSections.begin(), vSections.end(), cmpSec, NeuriteProjector::CompareSections());
    1184              : 
    1185              :         UG_COND_THROW(it == vSections.end(),
    1186              :                 "Could not find section for parameter t = " << t << " in neurite " << neuriteID << ".");
    1187              : 
    1188              :         // calculate correct axial position
    1189              :         // and velocity dir
    1190              :         vector3 vel_dummy;
    1191              :         number radius_dummy;
    1192              :         compute_position_and_velocity_in_section(posOut, vel_dummy, radius_dummy, it, t);
    1193              : }
    1194              : #endif
    1195              : 
    1196              : 
    1197            0 : static void newton_for_bp_projection
    1198              : (
    1199              :         vector3& posOut,
    1200              :         const std::vector<NeuriteProjector::BPProjectionHelper>& vProjHelp,
    1201              :         float rad,
    1202              :         const vector3& constAngleSurfNormal,
    1203              :         const NeuriteProjector* np
    1204              : )
    1205              : {
    1206              :         vector3 posStart = posOut;
    1207              : 
    1208              :         // calculate start defect and gradient
    1209              :         number def;
    1210              :         number def_init;
    1211              :         vector3 grad;
    1212              : 
    1213            0 :         bp_defect_and_gradient(def, grad, vProjHelp, posOut, rad, constAngleSurfNormal, np);
    1214            0 :         def_init = fabs(def);
    1215              : 
    1216              :         // perform some kind of Newton search for the correct position
    1217              :         size_t maxIt = 10;
    1218              :         number minDef = 1e-8*sqrt(PI)*exp(-1.0);
    1219              :         size_t iter = 0;
    1220              :         number corr = 1.0;
    1221              : 
    1222              :         //UG_LOGN(iter << "  " << def << "  " << grad << "  " << posOut);
    1223            0 :         while (fabs(def) > minDef && fabs(def) > 1e-8 * def_init && ++iter <= maxIt)
    1224              :         {
    1225            0 :                 corr /= 2;  // start with reduced step size to prevent overshooting in case the start position is too bad
    1226              :                 vector3 posTest;
    1227              :                 vector3 gradTest;
    1228              :                 number defTest;
    1229            0 :                 VecScaleAdd(posTest, 1.0, posOut, -(1.0-corr)*def / VecNormSquared(grad), grad);
    1230            0 :                 bp_defect_and_gradient(defTest, gradTest, vProjHelp, posTest, rad, constAngleSurfNormal, np);
    1231              : 
    1232              :                 // line search
    1233              :                 number lambda = 0.5;
    1234            0 :                 number bestDef = defTest;
    1235              :                 vector3 bestGrad = gradTest;
    1236              :                 vector3 bestPos = posTest;
    1237            0 :                 while (fabs(defTest) >= fabs(def) && lambda > 0.001)
    1238              :                 {
    1239              :                         //UG_LOGN("    line search with lambda = " << lambda);
    1240            0 :                         VecScaleAdd(posTest, 1.0, posOut, -lambda*def / VecNormSquared(grad), grad);
    1241            0 :                         bp_defect_and_gradient(defTest, gradTest, vProjHelp, posTest, rad, constAngleSurfNormal, np);
    1242            0 :                         if (fabs(defTest) < fabs(bestDef))
    1243              :                         {
    1244              :                                 bestDef = defTest;
    1245              :                                 bestGrad = gradTest;
    1246              :                                 bestPos = posTest;
    1247              :                         }
    1248            0 :                         lambda *= 0.5;
    1249              :                 }
    1250            0 :                 def = bestDef;
    1251              :                 grad = gradTest;
    1252              :                 posOut = bestPos;
    1253              : 
    1254              :                 //UG_LOGN(iter << "  " << def << "  " << grad << "  " << posOut);
    1255              :         }
    1256            0 :         UG_COND_THROW(def != def,
    1257              :                 "Newton iteration did not converge for branching point projection at " << posStart << ". Defect is NaN.")
    1258            0 :         UG_COND_THROW(fabs(def) > minDef && fabs(def) > 1e-8 * def_init,
    1259              :                 "Newton iteration did not converge for branching point projection at " << posStart << ".")
    1260            0 : }
    1261              : 
    1262              : 
    1263            0 : static void pos_in_neurite
    1264              : (
    1265              :         vector3& posOut,
    1266              :         const NeuriteProjector::Neurite& neurite,
    1267              :         size_t neuriteID,
    1268              :         float t,
    1269              :         float angle,
    1270              :         float rad
    1271              : )
    1272              : {
    1273              :         /// TODO: A similiar procedure as below has to be used for the soma/neurite BP iteration
    1274              :         const std::vector<NeuriteProjector::Section>& vSections = neurite.vSec;
    1275              : 
    1276              :         // find correct section
    1277            0 :         NeuriteProjector::Section cmpSec(t);
    1278              :         std::vector<NeuriteProjector::Section>::const_iterator it =
    1279            0 :                 std::lower_bound(vSections.begin(), vSections.end(), cmpSec, NeuriteProjector::CompareSections());
    1280              : 
    1281            0 :         UG_COND_THROW(it == vSections.end(),
    1282              :                 "Could not find section for parameter t = " << t << " in neurite " << neuriteID << ".");
    1283              : 
    1284              :         // calculate correct axial position
    1285              :         // and velocity dir
    1286              :         vector3 posAx, vel;
    1287              :         number radius;
    1288            0 :         compute_position_and_velocity_in_section(posAx, vel, radius, it, t);
    1289              : 
    1290              :         // calculate orthonormal basis vectors
    1291              :         vector3 projRefDir, thirdDir;
    1292            0 :         compute_ONB(vel, projRefDir, thirdDir, vel, neurite.refDir);
    1293              : 
    1294              :         // calculate new position
    1295            0 :         VecScaleAdd(posOut, 1.0, posAx, rad*radius*cos(angle), projRefDir, rad*radius*sin(angle), thirdDir);
    1296            0 : }
    1297              : 
    1298              : 
    1299            0 : static void bp_newton_start_pos
    1300              : (
    1301              :         vector3& initOut,
    1302              :         uint32_t neuriteID,
    1303              :         float t,
    1304              :         float angle,
    1305              :         float rad,
    1306              :         const vector3& constAngleSurfaceNormal,
    1307              :         const IVertexGroup* parent,
    1308              :         const NeuriteProjector* np
    1309              : )
    1310              : {
    1311              :         // Usually, we will want to just use the average position v_avg of all
    1312              :         // parent element vertices as start position for the Newton iteration.
    1313              :         // However, if a dendrite is strongly bent in the vicinity of a BP,
    1314              :         // this position can be quite far away from the final position.
    1315              :         // In these cases, we rather want to start in the position v_spl defined
    1316              :         // in the dendrite by the axial, radial and angular coordinates.
    1317              : 
    1318              :         // In order to detect these cases, we calculate
    1319              :         //     q = ||v_avg - v_spl|| / r(t),
    1320              :         // where r(t) is the dendrite radius at the axial position of interest,
    1321              :         // as well as
    1322              :         //     p = diam(parent) / r(t)
    1323              :         // where diam(parent) is the diameter of the parent element.
    1324              :         // This is used to distinguish a situation as described above from one
    1325              :         // where we are in the middle of a BP and q is large, but we still want
    1326              :         // to use simple averaging.
    1327              : 
    1328              :         // To avoid hard switching between cases, we calculate a continuous weighting
    1329              :         // function w and use
    1330              :         //     w*v_spl + (1-w)*v_avg
    1331              :         // as the starting position.
    1332              : 
    1333              :         vector3 pos_avg_onNeurite;
    1334            0 :         pos_in_neurite(pos_avg_onNeurite, np->neurite(neuriteID), neuriteID, t, angle, rad);
    1335              : 
    1336              :         // special case: no parents (happens when a vertex is being projected without refinement)
    1337            0 :         if (!parent)
    1338              :         {
    1339              :                 // start at position given by the parameterization
    1340              :                 initOut = pos_avg_onNeurite;
    1341            0 :                 return;
    1342              :         }
    1343              : 
    1344              :         // find correct section
    1345            0 :         const std::vector<NeuriteProjector::Section>& vSections = np->neurite(neuriteID).vSec;
    1346            0 :         NeuriteProjector::Section cmpSec(t);
    1347              :         std::vector<NeuriteProjector::Section>::const_iterator it =
    1348            0 :                 std::lower_bound(vSections.begin(), vSections.end(), cmpSec, NeuriteProjector::CompareSections());
    1349            0 :         UG_COND_THROW(it == vSections.end(),
    1350              :                 "Could not find section for parameter t = " << t << " in neurite " << neuriteID << ".");
    1351              : 
    1352              :         // calculate radius
    1353              :         number radius = 0.0;
    1354              :         compute_radius_in_section(radius, it, t);
    1355              : 
    1356              :         // calculate element diameter
    1357            0 :         number diamSq = 0.0;
    1358              :         const size_t nVrt = parent->size();
    1359            0 :         for (size_t i = 0; i < nVrt; ++i)
    1360              :         {
    1361            0 :                 const vector3& posi = np->geometry()->pos(parent->vertex(i));
    1362            0 :                 for (size_t j = i+1; j < nVrt; ++j)
    1363            0 :                         diamSq = std::max(diamSq, VecDistanceSq(posi, np->geometry()->pos(parent->vertex(j))));
    1364              :         }
    1365              : 
    1366              :         vector3 pos_avg;
    1367            0 :         np->average_pos_from_parent(pos_avg, parent);
    1368              : 
    1369            0 :         number distSq = VecDistanceSq(pos_avg_onNeurite, pos_avg);
    1370              : 
    1371              :         number qSq = 0.0;
    1372            0 :         if (radius*radius <= 1e-8*distSq)
    1373              :                 qSq = 1e8;
    1374              :         else
    1375            0 :                 qSq = distSq / (radius*radius);
    1376            0 :         const number w1 = qSq*qSq / (qSq*qSq + 1e-4); // in [0,1] with w1 = 0.5 for q = 0.1;
    1377              : 
    1378              :         number pSq = 0.0;
    1379            0 :         if (radius*radius <= 1e-8*diamSq)
    1380              :                 pSq = 1e8;
    1381              :         else
    1382            0 :                 pSq = diamSq / (radius*radius);
    1383            0 :         const number w2 = pSq*pSq / (pSq*pSq + 256); // in [0,1] with w2 = 0.5 for p = 4
    1384              : 
    1385            0 :         VecScaleAdd(initOut, w1*w2, pos_avg_onNeurite, 1.0-w1*w2, pos_avg);
    1386              : 
    1387              :         // project onto plane of constant angle
    1388              :         vector3 ptToSurf;
    1389              :         VecSubtract(ptToSurf, pos_avg_onNeurite, initOut);
    1390              :         VecScaleAdd(initOut, 1.0, initOut, VecDot(ptToSurf, constAngleSurfaceNormal), constAngleSurfaceNormal);
    1391              : }
    1392              : 
    1393              : 
    1394              : #if 0
    1395              : static void pos_on_surface_soma_bp
    1396              : (
    1397              :                 vector3& posOut,
    1398              :             const NeuriteProjector::Neurite& neurite,
    1399              :                 size_t neuriteID,
    1400              :             float& t,
    1401              :             float& angle,
    1402              :             const IVertexGroup* parent,
    1403              :             const NeuriteProjector* np,
    1404              :             float rad,
    1405              :             Vertex* vrt,
    1406              :             const NeuriteProjector::SomaBranchingRegion& sr,
    1407              :             const Grid::VertexAttachmentAccessor<Attachment<NeuriteProjector::SurfaceParams> >& aaSurfParams
    1408              : ) {
    1409              :         // if vertex is on the neurite backbone, simply project onto spline
    1410              :         // (Newton iteration will not succeed for Jacobian is zero)
    1411              :         if (rad < 1e-5)
    1412              :         {
    1413              :                 pos_in_neurite(posOut, np->neurite(neuriteID), neuriteID, t, angle, rad);
    1414              :                 return;
    1415              :         }
    1416              : 
    1417              :     // 1. preparations for Newton method:
    1418              :     // save integration start and end positions of soma connection
    1419              :     // also save a section iterator to start from
    1420              :     std::vector<NeuriteProjector::BPProjectionHelper> vProjHelp(1);
    1421              :     number& start = vProjHelp[0].start;
    1422              :     number& end = vProjHelp[0].end;
    1423              :     std::vector<NeuriteProjector::Section>::const_iterator& sec_start = vProjHelp[0].sec_start;
    1424              :         number range = 0;
    1425              :         /// FIXME: This method generates a range (which is negative) far too large, c
    1426              :         /// reate only range up to first sections secEnd param at most and not negative
    1427              :         try {range = np->axial_range_around_soma_region(sr, rad, neuriteID, vrt); }
    1428              :         UG_CATCH_THROW("Range around soma region could not be calculated.");
    1429              :         UG_LOGN("range: " << range);
    1430              :         /// Test: Too small range will never succeed in integration. Why?!
    1431              :         /*
    1432              :         start = aaSurfParams[vrt].axial;
    1433              :         end = aaSurfParams[vrt].axial+0.00001;
    1434              :         */
    1435              :         /// FIXME: Integrates only outside of the soma for now now, but need to
    1436              :         // integrate around soma or ER start, e.g. [start-range, start+range]
    1437              :         /// soma starts at 0 ER starts at -1
    1438              :         start = 0;
    1439              :         end = 0.001;
    1440              :         UG_LOGN("Range: (start: " << start << ", end: " << end << ")");
    1441              :         vProjHelp[0].sr = &sr;
    1442              :         sec_start = (&np->neurite(neuriteID).vSec)->begin();
    1443              : 
    1444              :     // 2. determine suitable start position for Newton iteration
    1445              :     pos_on_surface_soma(posOut, np->neurite(neuriteID), np, parent);
    1446              :         /*if (parent) {
    1447              :                 bp_newton_start_pos(posOut, neuriteID, t, angle, rad, parent, np);
    1448              :         }
    1449              :         */
    1450              : 
    1451              :         // also calculate normal of constant-angle surface
    1452              :         vector3 s, v;
    1453              :         number radius;
    1454              :         compute_position_and_velocity_in_section(s, v, radius, sec_start, t);
    1455              :         vector3 projRefDir;
    1456              :         vector3 thirdDir;
    1457              :         vector3 constAngleSurfNormal;
    1458              :         compute_ONB(constAngleSurfNormal, projRefDir, thirdDir, v, neurite.refDir);
    1459              :         VecScaleAdd(constAngleSurfNormal, sin(angle), projRefDir, -cos(angle), thirdDir);
    1460              :         UG_LOGN("posOut (initial): " << posOut);
    1461              : 
    1462              :     // 3. perform Newton iteration
    1463              :     try {newton_for_soma_bp_projection(posOut, vProjHelp, np, constAngleSurfNormal);}
    1464              :     UG_CATCH_THROW("Newton iteration for neurite projection at branching point failed.");
    1465              : }
    1466              : #endif
    1467              : 
    1468              : 
    1469            0 : static void pos_in_bp
    1470              : (
    1471              :         vector3& posOut,
    1472              :         const NeuriteProjector::Neurite& neurite,
    1473              :         uint32_t neuriteID,
    1474              :         float& t,
    1475              :         float& angle,
    1476              :         float rad,
    1477              :         std::vector<NeuriteProjector::BranchingRegion>::const_iterator& it,
    1478              :         const IVertexGroup* parent,
    1479              :         const NeuriteProjector* np
    1480              : )
    1481              : {
    1482              :         // if vertex is on the neurite backbone, simply project onto spline
    1483              :         // (Newton iteration will not succeed for Jacobian is zero)
    1484            0 :         if (rad < 1e-5)
    1485              :         {
    1486            0 :                 pos_in_neurite(posOut, np->neurite(neuriteID), neuriteID, t, angle, rad);
    1487            0 :                 return;
    1488              :         }
    1489              : 
    1490              :         // preparations for Newton method:
    1491              :         // save integration start and end positions of all BRs of this BP,
    1492              :         // also save a section iterator to start from
    1493              :         SmartPtr<NeuriteProjector::BranchingPoint> bp = it->bp;
    1494              :         size_t nParts = bp->vRegions.size();
    1495            0 :         std::vector<NeuriteProjector::BPProjectionHelper> vProjHelp(nParts);
    1496              :         const std::vector<NeuriteProjector::Section>* secs;
    1497            0 :         for (size_t i = 0; i < nParts; ++i)
    1498              :         {
    1499              :                 number& start = vProjHelp[i].start;
    1500              :                 number& end = vProjHelp[i].end;
    1501              :                 std::vector<NeuriteProjector::Section>::const_iterator& sec_start = vProjHelp[i].sec_start;
    1502              : 
    1503            0 :                 uint32_t nid = bp->vNid[i];
    1504            0 :                 const NeuriteProjector::BranchingRegion* br = bp->vRegions[i];
    1505              : 
    1506            0 :                 UG_COND_THROW(!br, "Requested branching region not accessible.");
    1507              : 
    1508              :                 // integrate over twice the size of the original BR (i.e., 10 times rad*radius in each direction)
    1509              :                 number range = 0.0;
    1510            0 :                 try {range = np->axial_range_around_branching_region(nid,
    1511            0 :                         (size_t) (br - &np->neurite(nid).vBR[0]), 10.0*rad);}
    1512            0 :                 UG_CATCH_THROW("Range around branching region could not be calculated.");
    1513            0 :                 start = std::max(br->t - range, 0.0);
    1514            0 :                 end = std::min(br->t + range, 1.0);
    1515              : 
    1516            0 :                 secs = &np->neurite(nid).vSec;
    1517            0 :                 if (start == 0.0)
    1518            0 :                         sec_start = secs->begin();
    1519              :                 else
    1520              :                 {
    1521              :                         NeuriteProjector::Section cmpSec(start);
    1522            0 :                         sec_start = std::lower_bound(secs->begin(), secs->end(), cmpSec, NeuriteProjector::CompareSections());
    1523            0 :                         UG_COND_THROW(sec_start == secs->end(),
    1524              :                                 "Could not find section for start parameter t = " << start << ".");
    1525              :                 }
    1526              :         }
    1527              : 
    1528              :         // also calculate normal of constant-angle surface
    1529              :         vector3 constAngleSurfNormal;
    1530            0 :         NeuriteProjector::Section cmpSec(t);
    1531              :         const std::vector<NeuriteProjector::Section>& vSections = neurite.vSec;
    1532              :         std::vector<NeuriteProjector::Section>::const_iterator secIt =
    1533            0 :                 std::lower_bound(vSections.begin(), vSections.end(), cmpSec, NeuriteProjector::CompareSections());
    1534              : 
    1535              :         vector3 s, v;
    1536              :         number radius;
    1537            0 :         compute_position_and_velocity_in_section(s, v, radius, secIt, t);
    1538              :         vector3 projRefDir;
    1539              :         vector3 thirdDir;
    1540            0 :         compute_ONB(constAngleSurfNormal, projRefDir, thirdDir, v, neurite.refDir);
    1541            0 :         VecScaleAdd(constAngleSurfNormal, sin(angle), projRefDir, -cos(angle), thirdDir);
    1542              : 
    1543              :         // determine suitable start position for Newton iteration
    1544            0 :         bp_newton_start_pos(posOut, neuriteID, t, angle, rad, constAngleSurfNormal, parent, np);
    1545              :         //if (parent)
    1546              :         //      np->average_pos_from_parent(posOut, parent);
    1547              :         //pos_in_neurite(posOut, np->neurite(neuriteID), neuriteID, t, angle, rad);
    1548              : 
    1549              :         // perform Newton iteration
    1550            0 :         try {newton_for_bp_projection(posOut, vProjHelp, rad, constAngleSurfNormal, np);}
    1551            0 :         UG_CATCH_THROW("Newton iteration for neurite projection at branching point failed"
    1552              :                 " in neurite " << neuriteID << ".");
    1553              : 
    1554              : 
    1555              :         // update the surface param info to the new position
    1556              :         // In order not to have to compute zeros of a 5th order polynomial,
    1557              :         // we make a linearity approximation here:
    1558              :         // s(t) = s(t0) + v(t0)*(t-t0);
    1559              :         // v(t) = v(t0)
    1560              :         // Then from v(t) * (s(t)-pos) = 0, we get:
    1561              :         // t = t0 + v(t0) * (pos - s(t0)) / (v(t0)*v(t0))
    1562              :         VecSubtract(s, posOut, s);
    1563            0 :         t += VecProd(v,s) / VecProd(v,v);
    1564              : 
    1565              :         // and now angle
    1566            0 :         if (rad > 1e-5)
    1567            0 :                 angle = compute_angle(v, projRefDir, thirdDir, s);
    1568              : 
    1569              :         // and radius
    1570              :         //rad = VecNorm(s) / radius;
    1571              : 
    1572              :         /*
    1573              :         // if we have ended up outside of the BP, then use the usual positioning
    1574              :         if (t > it->tend)
    1575              :         {
    1576              :                 vector3 posAx;
    1577              :                 number radius;
    1578              :                 compute_position_and_velocity_in_section(posAx, v, radius, secIt, t);
    1579              :                 VecScaleAdd(posOut, 1.0, posAx, rad*radius*cos(angle), projRefDir, rad*radius*sin(angle), thirdDir);
    1580              :         }
    1581              :         */
    1582            0 : }
    1583              : 
    1584              : 
    1585            0 : static void pos_on_surface_tip
    1586              : (
    1587              :         vector3& posOut,
    1588              :         const NeuriteProjector::Neurite& neurite,
    1589              :         const IVertexGroup* parent,
    1590              :         const NeuriteProjector* np,
    1591              :         number rad
    1592              : )
    1593              : {
    1594              :         const std::vector<NeuriteProjector::Section>& vSections = neurite.vSec;
    1595              : 
    1596              :         // initial position: regular refinement
    1597              :         // in case there is no parent (projection of an existing vertex), keep the position
    1598            0 :         if (parent)
    1599            0 :                 np->average_pos_from_parent(posOut, parent);
    1600              : 
    1601              :         // project to half-sphere with given radius over tip center
    1602              :         // TODO: One might think about something more sophisticated,
    1603              :         // as the current approach ensures only continuity of the radius
    1604              :         // at tips, but not differentiability.
    1605              :         const NeuriteProjector::Section& sec = vSections[vSections.size()-1];
    1606            0 :         vector3 tipCenter(sec.splineParamsX[3], sec.splineParamsY[3], sec.splineParamsZ[3]);
    1607            0 :         number radius = sec.splineParamsR[3];
    1608              : 
    1609              :         vector3 radialVec;
    1610              :         VecSubtract(radialVec, posOut, tipCenter);
    1611            0 :         number radNorm = sqrt(VecProd(radialVec, radialVec));
    1612              : 
    1613              :         // only project if we are not in the center of the half sphere
    1614            0 :         if (radNorm >= 1e-5*rad*radius)
    1615              :         {
    1616            0 :                 VecScale(radialVec, radialVec, rad*radius/radNorm);
    1617              :                 VecAdd(posOut, tipCenter, radialVec);
    1618              :         }
    1619            0 : }
    1620              : 
    1621            0 : number NeuriteProjector::axial_range_around_soma_region
    1622              : (
    1623              :         const SomaBranchingRegion& sr,
    1624              :         const size_t numRadii,
    1625              :         const size_t nid,
    1626              :         Vertex* vrt
    1627              : ) const
    1628              : {
    1629              :         // soma parameters
    1630              :         ///const float somaRad = sr.radius;
    1631              :         vector3 bp = sr.bp;
    1632              :         std::vector<NeuriteProjector::Section>::const_iterator secIt;
    1633            0 :         try {secIt = get_soma_section_iterator(nid, sr.t);}
    1634            0 :         UG_CATCH_THROW("Could not get section iterator to soma region: " << sr.t);
    1635              :         vector3 secStartPos;
    1636              :         vector3 secEndPos;
    1637            0 :         const number te = secIt->endParam;
    1638              :         number ts = 0.0;
    1639            0 :         compute_first_section_end_points(secStartPos, secEndPos, secIt);
    1640            0 :         const number neuriteLengthApprox = VecDistance(secEndPos, secStartPos) / (te - ts);
    1641              :         const number* s = &secIt->splineParamsR[0];
    1642            0 :         number radius = s[0]*(te-sr.t) + s[1];
    1643            0 :         radius = radius*(te-sr.t) + s[2];
    1644            0 :         radius = radius*(te-sr.t) + s[3];
    1645            0 :         const number bpToVrt = VecDistance(bp, position(vrt));
    1646            0 :         const number neurite_length_to_soma_bp= neuriteLengthApprox + bpToVrt;
    1647            0 :         const number rad = (numRadii * radius)/neurite_length_to_soma_bp;
    1648            0 :         return rad;
    1649              : }
    1650              : 
    1651            0 : bool NeuriteProjector::is_in_axial_range_around_soma_region
    1652              : (
    1653              :         const SomaBranchingRegion& sr,
    1654              :         size_t nid,
    1655              :         Vertex* vrt
    1656              : ) const {
    1657              :         ///const number radius = axial_range_around_soma_region(sr, 0.1, nid, vrt);
    1658              :         const number radius = 0.1;
    1659            0 :         UG_LOGN("Soma Branching Region center: " << sr.center);
    1660              :         UG_LOGN("Soma Branching Region raidus: " << radius);
    1661            0 :         return IsElementInsideSphere<Vertex>(vrt, sr.center, radius);
    1662              : }
    1663              : 
    1664            0 : number NeuriteProjector::push_into_place(Vertex* vrt, const IVertexGroup* parent)
    1665              : {
    1666              :         // average axial and angular params from parent;
    1667              :         // also decide on neuriteID for aaSurfParams.
    1668              :         // the mapping parameters 1d<->2d are not needed on higher levels of refinment
    1669              :         uint32_t neuriteID;
    1670              :         float t;
    1671              :         float angle;
    1672              :         float rad;
    1673            0 :         if (parent) {
    1674            0 :                 average_params(neuriteID, t, angle, rad, parent);
    1675              :         } else {
    1676            0 :                 neuriteID = m_aaSurfParams[vrt].neuriteID;
    1677            0 :                 t = m_aaSurfParams[vrt].axial;
    1678            0 :                 angle = m_aaSurfParams[vrt].angular;
    1679            0 :                 rad = m_aaSurfParams[vrt].radial;
    1680              :         }
    1681              : 
    1682              :         //UG_LOGN("averaged params: nid " << neuriteID << "   t " << t << "   angle "
    1683              :         //      << angle << "   rad " << rad);
    1684              : 
    1685            0 :         const uint32_t plainNID = (neuriteID << 12) >> 12;
    1686              : 
    1687            0 :         UG_COND_THROW(plainNID >= m_vNeurites.size(), "Requested neurite ID which is not present.");
    1688              : 
    1689              :         const Neurite& neurite = m_vNeurites[plainNID];
    1690              :         const std::vector<BranchingRegion>& vBR = neurite.vBR;
    1691              :         const std::vector<SomaBranchingRegion>& vSBR = neurite.vSBR;
    1692              : 
    1693              :         ///UG_LOGN("Number of soma branching regions for neurite with id: " << plainNID << ": " << vSBR.size());
    1694              : 
    1695              : 
    1696              : 
    1697              :         // vector for new position
    1698            0 :         vector3 pos(position(vrt));
    1699              : 
    1700              :         // FIVE CASES can occur:
    1701              :         // 1. We are at a branching point.
    1702              :         // 2. We are at a soma branching point
    1703              :         // 3. We are well inside a regular piece of neurite.
    1704              :         // 4. We are well inside a regular piece of soma.
    1705              :         // 5. We are at the tip of a neurite.
    1706              : 
    1707              :         /// This works due to the fact that vBR is sorted ascending
    1708              :         bool isBP = false;
    1709            0 :         BranchingRegion cmpBR(t);
    1710              :         std::vector<BranchingRegion>::const_iterator it =
    1711            0 :                 std::upper_bound(vBR.begin(), vBR.end(), cmpBR, CompareBranchingRegionEnds());
    1712              : 
    1713              :         /// This works due to the fact that vSBR is sorted ascending
    1714              :         bool isSP = false;
    1715            0 :         SomaBranchingRegion cmpSBR(t);
    1716              :         std::vector<SomaBranchingRegion>::const_iterator it2 =
    1717            0 :                 std::lower_bound(vSBR.begin(), vSBR.end(), cmpSBR, CompareSomaBranchingRegionsEnd());
    1718              : 
    1719            0 :         if (it2 != vSBR.end()) {
    1720            0 :                 isSP = is_in_axial_range_around_soma_region(*it2, plainNID, vrt);
    1721            0 :                 if (isSP) {
    1722            0 :                         UG_LOGN("Soma branching point: " << this->pos(vrt));
    1723              :                 }
    1724              :         }
    1725              : 
    1726            0 :         if (it != vBR.end())
    1727              :         {
    1728              :                 number range = 0.0;
    1729            0 :                 try {range = axial_range_around_branching_region(plainNID, std::distance(vBR.begin(), it), 5.0*rad);}
    1730            0 :                 UG_CATCH_THROW("Range around branching region could not be determined.");
    1731            0 :                 if (it->t - t < range)
    1732              :                 {
    1733              :                         isBP = true;
    1734              :                 }
    1735              :         }
    1736              : 
    1737            0 :         if (!isBP && it != vBR.begin())
    1738              :         {
    1739              :                 --it;
    1740              :                 number range = 0.0;
    1741            0 :                 try {range = axial_range_around_branching_region(plainNID, std::distance(vBR.begin(), it), 5.0*rad);}
    1742            0 :                 UG_CATCH_THROW("Range around branching region could not be determined.");
    1743            0 :                 if (t - it->t < range)
    1744              :                 {
    1745              :                         isBP = true;
    1746              :                 }
    1747              :         }
    1748              : 
    1749              :         // case 1: branching point
    1750            0 :         if (isBP)
    1751              :         {
    1752            0 :                 pos_in_bp(pos, neurite, plainNID, t, angle, rad, it, parent, this);
    1753              :         }
    1754              : 
    1755              :         // case 2: soma/neurite or er/neurite branching point
    1756            0 :         else if (isSP)
    1757              :         {
    1758              :                 /// This should integrate along the neurite [0, END_SOMA_BRANCHING_REGION]
    1759              :                 /// but not [-range, range] to avoid dints in the soma interior close to
    1760              :                 /// the surface. A new position for the vertex based on the average of the
    1761              :                 /// (first) neurite section and the soma sphere position and radius will
    1762              :                 /// be calculated based on the SomaRegion information stored in a struct.
    1763              :                 /// FIXME: Optimize iteration: Find better starting position for iteration start
    1764              :                 ///pos_on_surface_soma_bp(pos, neurite, neuriteID, t, angle, parent, this, rad, vrt, *it2, m_aaSurfParams);
    1765              :         }
    1766              : 
    1767              :         // case 3: normal neurite position
    1768            0 :         else if (t >= 0.0 && t <= 1.0)
    1769              :         {
    1770            0 :                 pos_in_neurite(pos, neurite, plainNID, t, angle, rad);
    1771              :         }
    1772              : 
    1773              :         // case 4: normal soma position
    1774            0 :         else if (t < 0 && t >= -1.0)
    1775              :         {
    1776              :                 ///pos_on_surface_soma(pos, neurite, this, parent);
    1777              :         }
    1778              : 
    1779              :         // case 5: tip of neurite
    1780              :         else
    1781              :         {
    1782            0 :                 pos_on_surface_tip(pos, neurite, parent, this, rad);
    1783              :         }
    1784              : 
    1785              :         // save new surface params for new vertex
    1786            0 :         m_aaSurfParams[vrt].neuriteID = neuriteID;
    1787            0 :         m_aaSurfParams[vrt].axial = t;
    1788            0 :         m_aaSurfParams[vrt].angular = angle;
    1789            0 :         m_aaSurfParams[vrt].radial = rad;
    1790              : 
    1791              :         // set position
    1792              :         set_pos(vrt, pos);
    1793              : 
    1794            0 :         return 1.0;
    1795              : }
    1796              : 
    1797              : 
    1798              : 
    1799              : // -------------------------------------------------------- //
    1800              : // DO NOT CHANGE below this line! Needed for serialization. //
    1801              : 
    1802            0 : std::ostream& operator<<(std::ostream& os, const NeuriteProjector::SurfaceParams& surfParams)
    1803              : {
    1804              :         using std::ostringstream;
    1805            0 :         ostringstream strs;
    1806            0 :         strs << surfParams.neuriteID << " ";
    1807            0 :         strs << surfParams.axial << " ";
    1808            0 :         strs << surfParams.angular << " ";
    1809            0 :         strs << surfParams.radial;
    1810            0 :         os << strs.str();
    1811            0 :         return os;
    1812            0 : }
    1813              : 
    1814            0 : std::istream& operator>>(std::istream& in, NeuriteProjector::SurfaceParams& surfParams)
    1815              : {
    1816              :         std::string temp;
    1817              :         using boost::lexical_cast;
    1818              : 
    1819            0 :         in >> temp;
    1820            0 :         surfParams.neuriteID = lexical_cast<uint32>(temp);
    1821              :         temp.clear();
    1822              : 
    1823            0 :         in >> temp;
    1824            0 :         surfParams.axial = (lexical_cast<float>(temp));
    1825              :         temp.clear();
    1826              : 
    1827            0 :         in >> temp;
    1828            0 :         surfParams.angular = lexical_cast<float>(temp);
    1829              :         temp.clear();
    1830              : 
    1831            0 :         in >> temp;
    1832            0 :         surfParams.radial = lexical_cast<float>(temp);
    1833              :         temp.clear();
    1834              : 
    1835            0 :         return in;
    1836              : }
    1837              : 
    1838            0 : std::ostream& operator<<(std::ostream& os, const NeuriteProjector::Mapping& mapping)
    1839              : {
    1840              :         /// Standard precision for UGX coord export is 18. Is this even correct to use?
    1841              :         /// Shouldn't one use std::numeric_limits<number>::digits10+1?
    1842              :         using std::ostringstream;
    1843            0 :         ostringstream strs;
    1844            0 :         for (size_t i = 0; i < mapping.v1.size(); i++) {
    1845            0 :                 strs << std::setprecision(18) << mapping.v1[i] << " ";
    1846              :         }
    1847            0 :         for (size_t i = 0; i < mapping.v1.size(); i++) {
    1848            0 :                 strs << std::setprecision(18) << mapping.v2[i] << " ";
    1849              :         }
    1850            0 :         strs << mapping.lambda;
    1851            0 :         os << strs.str();
    1852            0 :         return os;
    1853            0 : }
    1854              : 
    1855            0 : std::istream& operator>>(std::istream& in, NeuriteProjector::Mapping& mapping)
    1856              : {
    1857              :         std::string temp;
    1858              :         using boost::lexical_cast;
    1859              : 
    1860            0 :         for (size_t i = 0; i < mapping.v1.size(); i++) {
    1861            0 :                 in >> temp;
    1862            0 :                 mapping.v1[i] = (lexical_cast<number>(temp));
    1863              :         }
    1864              : 
    1865            0 :         for (size_t i = 0; i < mapping.v2.size(); i++) {
    1866            0 :                 in >> temp;
    1867            0 :                 mapping.v2[i] = (lexical_cast<number>(temp));
    1868              :         }
    1869              : 
    1870            0 :         in >> temp;
    1871            0 :         mapping.lambda = (lexical_cast<number>(temp));
    1872              :         temp.clear();
    1873              : 
    1874            0 :         return in;
    1875              : }
    1876              : 
    1877              : 
    1878              : } // namespace ug
        

Generated by: LCOV version 2.0-1