OpenWalnut  1.5.0dev
WTuringPatternCreator.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 <iostream>
26 #include <memory>
27 #include <vector>
28 
29 #include <boost/random.hpp>
30 #include <core/common/exceptions/WPreconditionNotMet.h>
31 
32 #include "WTuringPatternCreator.h"
33 
34 WTuringPatternCreator::WTuringPatternCreator( std::shared_ptr< WProgress > const progress, std::size_t numThreads )
35  : m_numThreads( numThreads ),
36  m_numIterations( 100 ),
37  m_spotIrregularity( 0.1 ),
38  m_spotSize( 0.1 ),
39  m_progress( progress )
40 {
41  WPrecond( numThreads > 0, "" );
42 }
43 
45 {
46 }
47 
49 {
50  WPrecond( iter > 0, "Invalid number of iterations!" );
51 
52  m_numIterations = iter;
53 }
54 
56 {
57  WPrecond( irr >= 0.0f && irr <= 1.0f, "Spot irregularity must be in [0,1]!" );
58 
59  m_spotIrregularity = irr;
60 }
61 
63 {
64  WPrecond( size >= 0.0f && size <= 1.0f, "Spot size must be in [0,1]!" );
65 
66  m_spotSize = size;
67 }
68 
69 std::shared_ptr< std::vector< float > > WTuringPatternCreator::create( std::size_t sizeX, std::size_t sizeY, std::size_t sizeZ )
70 {
71  // some constants, maybe change to parameters
72  float const spotFactor = ( 0.02f + 0.58f * ( 1.0f - m_spotSize ) ) / 15.0f;
73  float const d1 = 0.125f;
74  float const d2 = 0.03125f;
75  float const speed = 1.0f;
76 
77  std::shared_ptr< std::vector< float > > concentration1( new std::vector< float >( sizeX * sizeY * sizeZ, 4.0f ) );
78  std::vector< float > concentration2( sizeX * sizeY * sizeZ, 4.0f );
79  std::vector< float > delta1( sizeX * sizeY * sizeZ, 0.0f );
80  std::vector< float > delta2( sizeX * sizeY * sizeZ, 0.0f );
81  std::vector< float > noise( sizeX * sizeY * sizeZ );
82 
83  boost::mt19937 generator( std::time( 0 ) );
84 
85  float noiseRange = 0.1f + 4.9f * m_spotIrregularity;
86 
87  boost::uniform_real< float > dist( 12.0f - noiseRange, 12.0f + noiseRange );
88  boost::variate_generator< boost::mt19937&, boost::uniform_real< float > > rand( generator, dist );
89 
90  // initialize noise
91  for( std::size_t k = 0; k < sizeX * sizeY * sizeZ; ++k )
92  {
93  noise[ k ] = rand();
94  }
95 
96  // The threads will notify this condition when they are all done with their calculations for one iteration.
97  std::shared_ptr< WConditionSet > continueCondition( new WConditionSet() );
98  continueCondition->setResetable( true, true );
99 
100  // We notify this condition to wake all threads and make them calculate for the next iteration.
101  std::shared_ptr< WConditionSet > waitCondition( new WConditionSet() );
102  continueCondition->setResetable( true, true );
103 
104  // The threads use this counter to find out whether all of them have finished for the current iteration.
105  WCounter counter;
106 
107  std::vector< std::shared_ptr< PatternThread > > threads;
108 
109  for( std::size_t k = 0; k < m_numThreads; ++k )
110  {
111  threads.push_back( std::shared_ptr< PatternThread >( new PatternThread( k, m_numThreads, continueCondition, waitCondition, &counter ) ) );
112  threads[ k ]->setDomainSize( sizeX, sizeY, sizeZ );
113  threads[ k ]->setSpotFactor( spotFactor );
114  threads[ k ]->setDiffusionConstants( d1, d2 );
115  threads[ k ]->setBufferPointers( concentration1.get(), &concentration2, &noise, &delta1, &delta2 );
116  threads[ k ]->run();
117  }
118 
119  // A short sleep to give the threads time to initialize themselves.
120  boost::this_thread::sleep( boost::posix_time::seconds( 1 ) );
121 
122  // Progress is from 0 to number of voxels. We want it to show the percent of iterations done, though.
123  double stepsPerIteration = sizeX * sizeY * sizeZ / m_numIterations;
124  std::size_t currentStep = 0;
125 
126  // iterate
127  for( std::size_t iter = 0; iter < m_numIterations; ++iter )
128  {
129  // let the threads calculate their parts of the simulation
130  waitCondition->notify(); // notify the threads to start
131  continueCondition->wait(); // wait for them to finish
132 
133  // applying the change in concentration is not too costly, we do it here
134  for( std::size_t k = 0; k < sizeX * sizeY * sizeZ; ++k )
135  {
136  ( *concentration1 )[ k ] += speed * delta1[ k ];
137  concentration2[ k ] += speed * delta2[ k ];
138  }
139 
140  // update progress
141  std::size_t newStep = static_cast< std::size_t >( iter * stepsPerIteration ) + 1;
142  m_progress->increment( newStep - currentStep );
143  currentStep = newStep;
144  }
145 
146  for( std::size_t k = 0; k < m_numThreads; ++k )
147  {
148  threads[ k ]->finish();
149  }
150  waitCondition->notify();
151 
152  // Give the threads time to swedish.
153  boost::this_thread::sleep( boost::posix_time::seconds( 1 ) );
154 
155  return concentration1;
156 }
157 
159  std::shared_ptr< WCondition > const mainThreadContinueCondition,
160  std::shared_ptr< WCondition > const waitForMainThreadCondition,
161  WCounter* const counter )
162  : WThreadedRunner(),
163  m_id( id ),
164  m_maxThreads( max ),
165  m_sizeX( 2 ),
166  m_sizeY( 2 ),
167  m_sizeZ( 2 ),
168  m_spotFactor( 0.5 ),
169  m_diffusionConstant1( 0.5 ),
170  m_diffusionConstant2( 0.5 ),
171  m_mainThreadContinueCondition( mainThreadContinueCondition ),
172  m_waitForMainThreadCondition( waitForMainThreadCondition ),
173  m_counter( counter ),
174  m_stop( false )
175 {
176 }
177 
179 {
180 }
181 
183 {
184  WPrecond( d1 >= 0.0 && d1 <= 1.0, "" );
185  WPrecond( d2 >= 0.0 && d2 <= 1.0, "" );
186 
187  m_diffusionConstant1 = d1;
188  m_diffusionConstant2 = d2;
189 }
190 
192 {
193  WPrecond( spotFactor > 0.0, "" );
194 
195  m_spotFactor = spotFactor;
196 }
197 
198 void WTuringPatternCreator::PatternThread::setDomainSize( std::size_t sizeX, std::size_t sizeY, std::size_t sizeZ )
199 {
200  WPrecond( sizeX > 0, "" );
201  WPrecond( sizeY > 0, "" );
202  WPrecond( sizeZ > 0, "" );
203 
204  m_sizeX = sizeX;
205  m_sizeY = sizeY;
206  m_sizeZ = sizeZ;
207 }
208 
210 {
211  WPrecond( m_concentration1 != 0, "Invalid pointer!" );
212  WPrecond( m_concentration2 != 0, "Invalid pointer!" );
213  WPrecond( m_noise != 0, "Invalid pointer!" );
214  WPrecond( m_delta1 != 0, "Invalid pointer!" );
215  WPrecond( m_delta2 != 0, "Invalid pointer!" );
216 
217  std::size_t const numVoxels = m_sizeX * m_sizeY * m_sizeZ;
218 
219  // The start and end voxels for this thread. Our voxels to process are [ start, end - 1 ] .
220  std::size_t start = m_id * ( numVoxels / m_maxThreads );
221  std::size_t end = ( m_id + 1 ) * ( numVoxels / m_maxThreads );
222 
223  // If we are the last thread, make sure to not exceed the number of voxels (if the number of voxel is not divisible by the number of threads).
224  if( m_id == m_maxThreads - 1 )
225  {
226  end = numVoxels;
227  }
228 
229  while( !m_stop )
230  {
231  // Wait for the main (i.e. module-) thread to notify we are ready to start this iteration.
232  m_waitForMainThreadCondition->wait();
233 
234  // If we are to stop, just return to destroy the thread.
235  if( m_stop )
236  return;
237 
238  for( std::size_t idx = start; idx < end; ++idx )
239  {
240  std::size_t i = idx % m_sizeX;
241  std::size_t j = ( idx / m_sizeX ) % m_sizeY;
242  std::size_t k = ( idx / m_sizeX ) / m_sizeY;
243 
244  std::size_t iNext = ( i + 1 ) % m_sizeX;
245  std::size_t iPrev = ( i + m_sizeX - 1 ) % m_sizeX;
246 
247  std::size_t jNext = ( j + 1 ) % m_sizeY;
248  std::size_t jPrev = ( j + m_sizeY - 1 ) % m_sizeY;
249 
250  std::size_t kNext = ( k + 1 ) % m_sizeZ;
251  std::size_t kPrev = ( k + m_sizeZ - 1 ) % m_sizeZ;
252 
253  // estimate change in concentrations using 3d laplace filter
254  float dc1 = 0.0;
255  dc1 += ( *m_concentration1 )[ iPrev + j * m_sizeX + k * m_sizeX * m_sizeY ];
256  dc1 += ( *m_concentration1 )[ iNext + j * m_sizeX + k * m_sizeX * m_sizeY ];
257  dc1 += ( *m_concentration1 )[ i + jPrev * m_sizeX + k * m_sizeX * m_sizeY ];
258  dc1 += ( *m_concentration1 )[ i + jNext * m_sizeX + k * m_sizeX * m_sizeY ];
259  dc1 += ( *m_concentration1 )[ i + j * m_sizeX + kPrev * m_sizeX * m_sizeY ];
260  dc1 += ( *m_concentration1 )[ i + j * m_sizeX + kNext * m_sizeX * m_sizeY ];
261  dc1 -= 6.0f * ( *m_concentration1 )[ idx ];
262 
263  float dc2 = 0.0;
264  dc2 += ( *m_concentration2 )[ iPrev + j * m_sizeX + k * m_sizeX * m_sizeY ];
265  dc2 += ( *m_concentration2 )[ iNext + j * m_sizeX + k * m_sizeX * m_sizeY ];
266  dc2 += ( *m_concentration2 )[ i + jPrev * m_sizeX + k * m_sizeX * m_sizeY ];
267  dc2 += ( *m_concentration2 )[ i + jNext * m_sizeX + k * m_sizeX * m_sizeY ];
268  dc2 += ( *m_concentration2 )[ i + j * m_sizeX + kPrev * m_sizeX * m_sizeY ];
269  dc2 += ( *m_concentration2 )[ i + j * m_sizeX + kNext * m_sizeX * m_sizeY ];
270  dc2 -= 6.0f * ( *m_concentration2 )[ idx ];
271 
272  // diffusion
273  ( *m_delta1 )[ idx ] = m_spotFactor * ( 16.0f - ( *m_concentration1 )[ idx ] * ( *m_concentration2 )[ idx ] )
274  + m_diffusionConstant1 * dc1;
275  ( *m_delta2 )[ idx ] = m_spotFactor * ( ( *m_concentration1 )[ idx ] * ( *m_concentration2 )[ idx ]
276  - ( *m_concentration2 )[ idx ] - ( *m_noise )[ idx ] ) + m_diffusionConstant2 * dc2;
277  }
278 
279  // Increase counter to tell the other threads we are done.
280  // If we are the last to increment, we need to notify the main thread we are all done with this iteration.
281  if( static_cast< std::size_t >( ++( *m_counter ) ) == m_maxThreads )
282  {
283  m_mainThreadContinueCondition->notify();
284  m_counter->reset(); // Don't forget to reset the counter for the next iteration.
285  }
286  }
287 }
288 
289 void WTuringPatternCreator::PatternThread::setBufferPointers( std::vector< float > const* concentration1, std::vector< float > const* concentration2,
290  std::vector< float > const* noise, std::vector< float >* delta1,
291  std::vector< float >* delta2 )
292 {
293  WPrecond( concentration1 != 0, "Invalid pointer!" );
294  WPrecond( concentration2 != 0, "Invalid pointer!" );
295  WPrecond( noise != 0, "Invalid pointer!" );
296  WPrecond( delta1 != 0, "Invalid pointer!" );
297  WPrecond( delta2 != 0, "Invalid pointer!" );
298 
299  m_concentration1 = concentration1;
300  m_concentration2 = concentration2;
301  m_noise = noise;
302  m_delta1 = delta1;
303  m_delta2 = delta2;
304 }
305 
307 {
308  m_stop = true;
309 }
310 
Class allowing multiple conditions to be used for one waiting cycle.
Definition: WConditionSet.h:44
This is a simple but thread-safe counter.
Definition: WCounter.h:37
Base class for all classes needing to be executed in a separate thread.
A thread calculating the reaction for a certain part of the domain.
void setDomainSize(std::size_t sizeX, std::size_t sizeY, std::size_t sizeZ)
Set the domain size.
void setDiffusionConstants(float d1, float d2)
Set the diffusion constants.
PatternThread(std::size_t id, std::size_t max, std::shared_ptr< WCondition > const mainThreadContinueCondition, std::shared_ptr< WCondition > const waitForMainThreadCondition, WCounter *const counter)
Constructor.
void setBufferPointers(std::vector< float > const *concentration1, std::vector< float > const *concentration2, std::vector< float > const *noise, std::vector< float > *delta1, std::vector< float > *delta2)
Set the pointers to the buffer all threads share.
void setSpotFactor(float spotFactor)
Set the spot factor.
void finish()
Tells the thread to finish.
virtual void threadMain()
The entry point for the thread.
std::shared_ptr< std::vector< float > > create(std::size_t sizeX, std::size_t sizeY, std::size_t sizeZ)
Creates the 3D pattern and writes it into a vector of floats.
void setSpotIrregularity(float irr)
Sets the spot irregularity parameter.
float m_spotSize
The spot size parameter.
void setSpotSize(float size)
Sets the spotsize parameter.
float m_spotIrregularity
The spot irregularity parameter.
void setNumIterations(std::size_t iter)
Sets the number of iterations for the chemical simulation that creates the pattern.
WTuringPatternCreator(std::shared_ptr< WProgress > const progress, std::size_t numThreads=boost::thread::hardware_concurrency())
Constructor.
float m_numIterations
The number of iterations for the simulation.
std::size_t m_numThreads
The number of threads to use.
std::shared_ptr< WProgress > m_progress
The progress to increment.