OpenWalnut  1.5.0dev
WMEigenSystem.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <memory>
26 #include <string>
27 #include <vector>
28 
29 #include "WMEigenSystem.h"
30 #include "WMEigenSystem.xpm"
31 #include "core/dataHandler/WDataSetDTI.h"
32 #include "core/dataHandler/WDataSetVector.h"
33 #include "core/kernel/WKernel.h"
34 #include "core/kernel/WModuleInputData.h"
35 #include "core/kernel/WModuleOutputData.h"
36 
37 // This line is needed by the module loader to actually find your module. Do not remove. Do NOT add a ";" here.
38 W_LOADABLE_MODULE( WMEigenSystem )
39 
41  WModule()
42 {
43 }
44 
46 {
47  // Cleanup!
48 }
49 
50 std::shared_ptr< WModule > WMEigenSystem::factory() const
51 {
52  // See "src/modules/template/" for an extensively documented example.
53  return std::shared_ptr< WModule >( new WMEigenSystem() );
54 }
55 
56 const char** WMEigenSystem::getXPMIcon() const
57 {
58  return WMEigenSystem_xpm;
59 }
60 const std::string WMEigenSystem::getName() const
61 {
62  return "Eigen System";
63 }
64 
65 const std::string WMEigenSystem::getDescription() const
66 {
67  // Specify your module description here. Be detailed. This text is read by the user.
68  // See "src/modules/template/" for an extensively documented example.
69  return "No documentation yet. "
70  "The best person to ask for documenation is probably the modules's creator, i.e. \"math\"";
71 }
72 
74 {
75  // Put the code for your connectors here. See "src/modules/template/" for an extensively documented example.
76  m_tensorIC = WModuleInputData< WDataSetDTI >::createAndAdd( shared_from_this(), "tensorInput", "The tensor field" );
77 
78  m_evecOutputs.push_back( WModuleOutputData< WDataSetVector >::createAndAdd( shared_from_this(), "evec1Output", "The 1. eigenvector field" ) );
79  m_evecOutputs.push_back( WModuleOutputData< WDataSetVector >::createAndAdd( shared_from_this(), "evec2Output", "The 2. eigenvector field" ) );
80  m_evecOutputs.push_back( WModuleOutputData< WDataSetVector >::createAndAdd( shared_from_this(), "evec3Output", "The 3. eigenvector field" ) );
81  m_evecOutputs.push_back( WModuleOutputData< WDataSetVector >::createAndAdd( shared_from_this(), "evalsOutput", "All three eigenvalues" ) );
82 
83  m_evalOutputs.push_back( WModuleOutputData< WDataSetScalar >::createAndAdd( shared_from_this(), "eval1Output", "The 1. eigenvalue field" ) );
84  m_evalOutputs.push_back( WModuleOutputData< WDataSetScalar >::createAndAdd( shared_from_this(), "eval2Output", "The 2. eigenvalue field" ) );
85  m_evalOutputs.push_back( WModuleOutputData< WDataSetScalar >::createAndAdd( shared_from_this(), "eval3Output", "The 3. eigenvalue field" ) );
86 
88 }
89 
91 {
92  // for selecting the strategy
93  std::shared_ptr< WItemSelection > strategies( new WItemSelection() );
94  strategies->addItem( "LibEigen", "Eigensystem is computed via libEigen and its SelfAdjointEigenSolver" );
95  strategies->addItem( "Jacobi", "Self implemented Jacobi iterative eigen decomposition" );
96  m_strategySelector = m_properties->addProperty( "Strategy", "How the eigen system should be computed",
97  strategies->getSelectorFirst() );
98 
101 
103 }
104 
106 {
107  // Put the code for your requirements here. See "src/modules/template/" for an extensively documented example.
108 }
109 
111 {
112  m_moduleState.setResetable( true, true );
113  m_moduleState.add( m_tensorIC->getDataChangedCondition() );
114  m_moduleState.add( m_strategySelector->getCondition() );
115 
116  ready();
117 
118  while( !m_shutdownFlag() )
119  {
120  infoLog() << "Waiting.";
122 
123  if( m_shutdownFlag() )
124  {
125  break;
126  }
127 
128  std::shared_ptr< WDataSetDTI > tensors = m_tensorIC->getData();
129 
130  if( !m_eigenPool && tensors )
131  {
132  // start the eigenvector computation if input is DTI double or float otherwise continue
133  // when the computation finishes, we'll be notified by the threadspool's threadsDoneCondition
134  resetEigenFunction( tensors );
135  if( !m_eigenPool )
136  {
137  continue;
138  }
139  resetProgress( tensors->getValueSet()->size(), "Compute eigen system" );
140  m_eigenPool->run();
141  infoLog() << "Computing eigen systems...";
142  }
143  else if( m_eigenPool && m_eigenPool->status() == W_THREADS_FINISHED )
144  {
145  // the computation of the eigenvectors has finished we have a new field of eigen systems
146  m_currentProgress->finish();
147  WAssert( m_eigenOperationFloat || m_eigenOperationDouble, "Bug: No result is available. Checked double and float!" );
148 
149  if( tensors->getValueSet()->getDataType() == W_DT_DOUBLE )
150  {
151  updateOCs( m_eigenOperationDouble->getResult() );
152  }
153  else
154  {
155  updateOCs( m_eigenOperationFloat->getResult() );
156  }
157 
158  m_eigenPool.reset();
159  infoLog() << "Computing eigen systems done.";
160  }
161  }
162 }
163 
164 void WMEigenSystem::updateOCs( std::shared_ptr< const WDataSetSingle > es )
165 {
166  WAssert( es, "Bug: updating the output with no data makes no sense." );
167  std::shared_ptr< const WValueSet< double > > vs = std::dynamic_pointer_cast< WValueSet< double > >( es->getValueSet() );
168  WAssert( vs, "Bug: invalid value-set from WThreadedPerVoxelOperation dataset" );
169  std::shared_ptr< WGrid > grid = es->getGrid();
170 
171  typedef std::shared_ptr< std::vector< double > > DataPointer;
172  std::vector< DataPointer > vecdata;
173  vecdata.push_back( DataPointer( new std::vector< double >( vs->size() * 3 ) ) );
174  vecdata.push_back( DataPointer( new std::vector< double >( vs->size() * 3 ) ) );
175  vecdata.push_back( DataPointer( new std::vector< double >( vs->size() * 3 ) ) );
176  vecdata.push_back( DataPointer( new std::vector< double >( vs->size() * 3 ) ) );
177  std::vector< DataPointer > valdata;
178  valdata.push_back( DataPointer( new std::vector< double >( vs->size() ) ) );
179  valdata.push_back( DataPointer( new std::vector< double >( vs->size() ) ) );
180  valdata.push_back( DataPointer( new std::vector< double >( vs->size() ) ) );
181 
182  for( size_t i = 0; i < vs->size(); ++i )
183  {
184  WValue< double > sys = vs->getWValue( i );
185  // eigenvalues
186  ( *vecdata[0] )[ i * 3 ] = sys[0];
187  ( *valdata[0] )[ i ] = sys[0];
188  ( *vecdata[0] )[ i * 3 + 1] = sys[4];
189  ( *valdata[1] )[ i ] = sys[4];
190  ( *vecdata[0] )[ i * 3 + 2] = sys[8];
191  ( *valdata[2] )[ i ] = sys[8];
192  // first eigenvector
193  ( *vecdata[1] )[ i * 3 ] = sys[1];
194  ( *vecdata[1] )[ i * 3 + 1] = sys[2];
195  ( *vecdata[1] )[ i * 3 + 2] = sys[3];
196  // second eigenvector
197  ( *vecdata[2] )[ i * 3 ] = sys[5];
198  ( *vecdata[2] )[ i * 3 + 1] = sys[6];
199  ( *vecdata[2] )[ i * 3 + 2] = sys[7];
200  // third eigenvector
201  ( *vecdata[3] )[ i * 3 ] = sys[9];
202  ( *vecdata[3] )[ i * 3 + 1] = sys[10];
203  ( *vecdata[3] )[ i * 3 + 2] = sys[11];
204  }
205  typedef std::shared_ptr< WDataSetVector > PDSV;
206  typedef std::shared_ptr< WDataSetScalar > PDSS;
207  typedef WValueSet< double > WVSDBL;
208  typedef std::shared_ptr< WVSDBL > PWVSDBL;
209 
210  m_evecOutputs[0]->updateData( PDSV( new WDataSetVector( PWVSDBL( new WVSDBL( 1, 3, vecdata[1], W_DT_DOUBLE ) ), grid ) ) );
211  m_evecOutputs[1]->updateData( PDSV( new WDataSetVector( PWVSDBL( new WVSDBL( 1, 3, vecdata[2], W_DT_DOUBLE ) ), grid ) ) );
212  m_evecOutputs[2]->updateData( PDSV( new WDataSetVector( PWVSDBL( new WVSDBL( 1, 3, vecdata[3], W_DT_DOUBLE ) ), grid ) ) );
213  m_evecOutputs[3]->updateData( PDSV( new WDataSetVector( PWVSDBL( new WVSDBL( 1, 3, vecdata[0], W_DT_DOUBLE ) ), grid ) ) );
214  m_evalOutputs[0]->updateData( PDSS( new WDataSetScalar( PWVSDBL( new WVSDBL( 0, 1, valdata[0], W_DT_DOUBLE ) ), grid ) ) );
215  m_evalOutputs[1]->updateData( PDSS( new WDataSetScalar( PWVSDBL( new WVSDBL( 0, 1, valdata[1], W_DT_DOUBLE ) ), grid ) ) );
216  m_evalOutputs[2]->updateData( PDSS( new WDataSetScalar( PWVSDBL( new WVSDBL( 0, 1, valdata[2], W_DT_DOUBLE ) ), grid ) ) );
217 }
218 
219 boost::array< double, 12 > WMEigenSystem::computeEigenSystem( WTensorSym< 2, 3, double > const& m ) const
220 {
221  RealEigenSystem sys;
222  jacobiEigenvector3D( m, &sys );
223  boost::array< double, 12 > result = { { sys[0].first, sys[0].second[0],
224  sys[0].second[1],
225  sys[0].second[2],
226  sys[1].first, sys[1].second[0],
227  sys[1].second[1],
228  sys[1].second[2],
229  sys[2].first, sys[2].second[0],
230  sys[2].second[1],
231  sys[2].second[2] } }; // NOLINT
232  return result;
233 }
234 
236 {
238  m( 0, 0 ) = static_cast< double >( input[ 0 ] );
239  m( 0, 1 ) = static_cast< double >( input[ 1 ] );
240  m( 0, 2 ) = static_cast< double >( input[ 2 ] );
241  m( 1, 1 ) = static_cast< double >( input[ 3 ] );
242  m( 1, 2 ) = static_cast< double >( input[ 4 ] );
243  m( 2, 2 ) = static_cast< double >( input[ 5 ] );
244 
246 
247  return computeEigenSystem( m );
248 }
249 
251 {
253  m( 0, 0 ) = static_cast< double >( input[ 0 ] );
254  m( 0, 1 ) = static_cast< double >( input[ 1 ] );
255  m( 0, 2 ) = static_cast< double >( input[ 2 ] );
256  m( 1, 1 ) = static_cast< double >( input[ 3 ] );
257  m( 1, 2 ) = static_cast< double >( input[ 4 ] );
258  m( 2, 2 ) = static_cast< double >( input[ 5 ] );
259 
261 
262  return computeEigenSystem( m );
263 }
264 
266 {
267  Eigen::Matrix3d m;
268  m << input[0], input[1], input[2],
269  input[1], input[3], input[4],
270  input[2], input[4], input[5];
271 
272  return applyEigenSolver( m );
273 }
274 
276 {
277  Eigen::Matrix3d m;
278  m << input[0], input[1], input[2],
279  input[1], input[3], input[4],
280  input[2], input[4], input[5];
281 
282  return applyEigenSolver( m );
283 }
284 
285 boost::array< double, 12 > WMEigenSystem::applyEigenSolver( const Eigen::Matrix3d &m ) const
286 {
287  Eigen::SelfAdjointEigenSolver< Eigen::Matrix3d > es( m );
288  const Eigen::Matrix3d &evecs = es.eigenvectors();
289  const Eigen::Vector3d &evals = es.eigenvalues();
290  boost::array< double, 12 > result = { { evals( 0 ), evecs( 0, 0 ),
291  evecs( 1, 0 ),
292  evecs( 2, 0 ),
293  evals( 1 ), evecs( 0, 1 ),
294  evecs( 1, 1 ),
295  evecs( 2, 1 ),
296  evals( 2 ), evecs( 0, 2 ),
297  evecs( 1, 2 ),
298  evecs( 2, 2 ) } }; // NOLINT curly braces
299  return result;
300 }
301 
302 void WMEigenSystem::resetProgress( std::size_t todo, std::string name )
303 {
304  if( m_currentProgress )
305  {
306  m_currentProgress->finish();
307  }
308  m_currentProgress = std::shared_ptr< WProgress >( new WProgress( name, todo ) );
309  m_progress->addSubProgress( m_currentProgress );
310 }
311 
312 void WMEigenSystem::resetEigenFunction( std::shared_ptr< WDataSetDTI > tensors )
313 {
314  // check if we need to stop the currently running eigencomputation
315  if( m_eigenPool )
316  {
317  debugLog() << "Stopping eigencomputation.";
318  WThreadedFunctionStatus s = m_eigenPool->status();
319  if( s != W_THREADS_FINISHED && s != W_THREADS_ABORTED )
320  {
321  m_eigenPool->stop();
322  m_eigenPool->wait();
323  s = m_eigenPool->status();
324  WAssert( s == W_THREADS_FINISHED || s == W_THREADS_ABORTED, "" );
325  }
326  m_moduleState.remove( m_eigenPool->getThreadsDoneCondition() );
327  }
328  // the threadpool should have finished computing by now
329 
330  m_eigenOperationFloat = std::shared_ptr< TPVOFloat >();
331  m_eigenOperationDouble = std::shared_ptr< TPVODouble >();
332 
333  // create a new one
334  if( tensors->getValueSet()->getDataType() == W_DT_DOUBLE )
335  {
336  if( m_strategySelector->get().at( 0 )->getName() == "LibEigen" )
337  {
338  m_eigenOperationDouble = std::shared_ptr< TPVODouble >( new TPVODouble( tensors, boost::bind( &WMEigenSystem::eigenSolverDouble, this, boost::placeholders::_1 ) ) ); // NOLINT line length
339  }
340  else if( m_strategySelector->get().at( 0 )->getName() == "Jacobi" )
341  {
342  m_eigenOperationDouble = std::shared_ptr< TPVODouble >( new TPVODouble( tensors, boost::bind( &WMEigenSystem::eigenFuncDouble, this, boost::placeholders::_1 ) ) ); // NOLINT line length
343  }
344  else
345  {
346  WAssert( 0, "Bug: Invalid strategy for eigen decomposition selected!" );
347  }
348  m_eigenPool = std::shared_ptr< WThreadedFunctionBase >( new EigenFunctionTypeDouble( W_AUTOMATIC_NB_THREADS, m_eigenOperationDouble ) );
349  m_moduleState.add( m_eigenPool->getThreadsDoneCondition() );
350  }
351  else if( tensors->getValueSet()->getDataType() == W_DT_FLOAT )
352  {
353  if( m_strategySelector->get().at( 0 )->getName() == "LibEigen" )
354  {
355  m_eigenOperationFloat = std::shared_ptr< TPVOFloat >( new TPVOFloat( tensors, boost::bind( &WMEigenSystem::eigenSolverFloat, this, boost::placeholders::_1 ) ) ); // NOLINT line length
356  }
357  else if( m_strategySelector->get().at( 0 )->getName() == "Jacobi" )
358  {
359  m_eigenOperationFloat = std::shared_ptr< TPVOFloat >( new TPVOFloat( tensors, boost::bind( &WMEigenSystem::eigenFuncFloat, this, boost::placeholders::_1 ) ) ); // NOLINT line length
360  }
361  else
362  {
363  WAssert( 0, "Bug: Invalid strategy for eigen decomposition selected!" );
364  }
365  m_eigenPool = std::shared_ptr< WThreadedFunctionBase >( new EigenFunctionTypeFloat( W_AUTOMATIC_NB_THREADS, m_eigenOperationFloat ) );
366  m_moduleState.add( m_eigenPool->getThreadsDoneCondition() );
367  }
368  else
369  {
370  errorLog() << "Input data does not contain floating point values, skipping.";
371  m_eigenPool.reset();
372  }
373 }
virtual void wait() const
Wait for the condition.
void setResetable(bool resetable=true, bool autoReset=true)
Sets the resetable flag.
virtual void remove(std::shared_ptr< WCondition > condition)
Removes the specified condition.
virtual void add(std::shared_ptr< WCondition > condition)
Adds another condition to the set of conditions to wait for.
This data set type contains scalars as values.
This data set type contains vectors as values.
A class containing a list of named items.
Computes the eigensystem of a second order tensor field.
Definition: WMEigenSystem.h:55
void resetProgress(std::size_t todo, std::string name)
Resets the current progress to 0.
std::vector< std::shared_ptr< WModuleOutputData< WDataSetScalar > > > m_evalOutputs
Output scalar field , each for an eigenvalue field.
virtual void moduleMain()
Entry point after loading the module.
std::shared_ptr< TPVOFloat > m_eigenOperationFloat
the functor used for the calculation of the eigenvectors
virtual void requirements()
Initialize requirements for this module.
virtual std::shared_ptr< WModule > factory() const
Due to the prototype design pattern used to build modules, this method returns a new instance of this...
WThreadedFunction< TPVOFloat > EigenFunctionTypeFloat
the thread pool type for the eigencomputation (float input)
std::shared_ptr< WProgress > m_currentProgress
Indicating current work progress.
WThreadedPerVoxelOperation< double, 6, double, 12 > TPVODouble
the threaded per-voxel function for the eigenvector computation (double input)
virtual const std::string getDescription() const
Gives back a description of this module.
TPVOFloat::OutTransmitType const eigenSolverFloat(TPVOFloat::TransmitType const &input)
Computes the eigen system for double input parameters via using applyEigenSolver.
boost::array< double, 12 > computeEigenSystem(WTensorSym< 2, 3, double > const &m) const
Is used by every thread to compute the eigensystem for the given tensor.
boost::array< double, 12 > applyEigenSolver(const Eigen::Matrix3d &m) const
Copies the eigenvalues and eigenvectors from the libEigen output format into the double array.
virtual void connectors()
Initialize the connectors this module is using.
TPVOFloat::OutTransmitType const eigenFuncFloat(TPVOFloat::TransmitType const &input)
The function that computes the eigenvectors from the input tensor field.
TPVODouble::OutTransmitType const eigenSolverDouble(TPVODouble::TransmitType const &input)
Computes the eigen system for double input parameters via using applyEigenSolver.
virtual ~WMEigenSystem()
Destructs this module.
std::shared_ptr< WModuleInputData< WDataSetDTI > > m_tensorIC
Input tensor field.
std::shared_ptr< WThreadedFunctionBase > m_eigenPool
The threadpool for the eigenvector computation.
WPropSelection m_strategySelector
List for selecting the strategy.
std::vector< EigenOutputConnector > m_evecOutputs
Ouput vector field for principal eigenvectors as well as one for all eigenvalues at once.
WThreadedPerVoxelOperation< float, 6, double, 12 > TPVOFloat
the threaded per-voxel function for the eigenvector computation (float input)
virtual const std::string getName() const
Gives back the name of this module.
void resetEigenFunction(std::shared_ptr< WDataSetDTI > tensors)
Resets the threaded function/threadpool.
virtual void properties()
Initialize the properties for this module.
void updateOCs(std::shared_ptr< const WDataSetSingle > es)
Update the output connectors out of the computed eigensystem field.
WMEigenSystem()
Constructs a new module for eigensystem computation.
std::shared_ptr< TPVODouble > m_eigenOperationDouble
the functor used for the calculation of the eigenvectors
virtual const char ** getXPMIcon() const
Get the icon for this module in XPM format.
TPVODouble::OutTransmitType const eigenFuncDouble(TPVODouble::TransmitType const &input)
The function that computes the eigenvectors from the input tensor field.
WThreadedFunction< TPVODouble > EigenFunctionTypeDouble
the thread pool type for the eigencomputation (double input)
static PtrType createAndAdd(std::shared_ptr< WModule > module, std::string name="", std::string description="")
Convenience method to create a new instance of this in data connector with proper type and add it to ...
Class offering an instantiate-able data connection between modules.
Class representing a single module of OpenWalnut.
Definition: WModule.h:72
virtual void properties()
Initialize properties in this function.
Definition: WModule.cpp:212
wlog::WStreamedLogger debugLog() const
Logger instance for comfortable debug logging.
Definition: WModule.cpp:575
std::shared_ptr< WProperties > m_properties
The property object for the module.
Definition: WModule.h:640
void ready()
Call this whenever your module is ready and can react on property changes.
Definition: WModule.cpp:505
WConditionSet m_moduleState
The internal state of the module.
Definition: WModule.h:703
wlog::WStreamedLogger errorLog() const
Logger instance for comfortable error logging.
Definition: WModule.cpp:570
std::shared_ptr< WProgressCombiner > m_progress
Progress indicator used as parent for all progress' of this module.
Definition: WModule.h:652
wlog::WStreamedLogger infoLog() const
Logger instance for comfortable info logging.
Definition: WModule.cpp:565
virtual void connectors()
Initialize connectors in this function.
Definition: WModule.cpp:208
Class managing progress inside of modules.
Definition: WProgress.h:42
Implements a symmetric tensor that has the same number of components in every direction.
Definition: WTensorSym.h:73
boost::array< Output_T, numOutputs > OutTransmitType
the output type for the per-voxel operation
ValueSetType::SubArray const TransmitType
the input type for the per-voxel operation
WBoolFlag m_shutdownFlag
Condition getting fired whenever the thread should quit.
Base Class for all value set types.
Definition: WValueSet.h:47
Base class for all higher level values like tensors, vectors, matrices and so on.
Definition: WValue.h:41
void addTo(WPropSelection prop)
Add the PC_NOTEMPTY constraint to the property.
void addTo(WPropSelection prop)
Add the PC_SELECTONLYONE constraint to the property.