Jpp test-rotations-old
the software that should make you happy
Loading...
Searching...
No Matches
JMarkovPathGenerator.hh
Go to the documentation of this file.
1#ifndef H_MARKOV_PATH
2#define H_MARKOV_PATH
3
4/**
5 * \author mjongen
6 */
7
8// c++ standard library includes
9#include<iostream>
10#include<iomanip>
11#include<cmath>
12#include<cstdlib>
13
14// root includes
15#include "TObject.h"
16#include "TH1F.h"
17#include "TRandom3.h"
18
19// JPP includes
21
22namespace JMARKOV {}
23namespace JPP { using namespace JMARKOV; }
24
25namespace JMARKOV {
26
27 /**
28 The JMarkovPathGenerator generates ensembles of photon paths using a
29 Markov Chain Monte Carlo (MCMC).
30 In the Markov chain, the number of scatterings does not change.
31 Therefore the ensembles have to be generated separately for each given
32 number of scatterings.
33
34 The small modification of the path that is proposed in each step, is
35 defined by the function 'randomPathChange'. This function is virtual
36 so that it can be overrided in a derived class.
37 The initial and final vertex are fixed and one of the other vertices
38 is displaced in a random direction following an exponential distribution.
39
40 The source, target and scattering models are used to calculate the
41 probability density of the path.
42
43 Random numbers are drawn from gRandom.
44
45 By default, the coordinates are remapped such that the 1/r^2 singularities
46 in the path probability density disappear. This option can be turned off
47 (but do not do it, unless you want to convince yourself that it is
48 necessary) with the setCoordinateRemapping method.
49 **/
51 public:
52
53 /// standard constructor
64
65 /**
66 Generate an ensemble of n paths with a fixed number of scatterings by
67 MCMC-sampling the given scattering model.
68 The paths start at the source (a well-defined position) and end on the
69 target (either a fixed position or, in the case of a finite source, the
70 surface of a sphere).
71
72 start_path is a photon path that is used a starting point for the MCMC.
73 It has to be supplied by the user (due to the various source and target
74 profiles it is not straightforward to generate one automatically) and
75 must have a path probability density != 0.
76 The number of scatterings is also defined by the start_path.
77
78 nsteps_burn_in is the number of steps taken for burn-in.
79
80 nsteps_save determines the number of MCMC steps between the paths
81 that are saved to the ensemble.
82 **/
83 std::vector<JPhotonPath> generateEnsemble( int n, const JPhotonPath& start_path, JSourceModel* src, JScatteringModel* sm, JTargetModel* trg, double lambda_abs, int nsteps_burn_in, int nsteps_save ) ;
84
85 /// get the number of Markov steps taken in the last call to generateEnsemble (after burn-in)
87
88 /// get the number of accepted steps taken during the last call to generateEnsemble (after burn-in)
90
91 /// get the number of rejected steps during the last call to generateEnsemble (after burn-in)
93
94 /// get the fraction of accepted steps during the last call to generateEnsemble (after burn-in)
96 if( getNsteps() <= 0 ) return 0 ;
97 return 1.0*getNacceptedSteps()/getNsteps() ;
98 }
99
100 /// get the fraction of steps that were accepted when a radial step was performed during the last call to generateEnsemble (after burn-in)
102 if( naccepted_r + nrejected_r <= 0 ) return 0 ;
103 return 1.0*naccepted_r/(naccepted_r+nrejected_r) ;
104 }
105
106 /// get the fraction of steps that were accepted when a tangential step was performed during the last call to generateEnsemble (after burn-in)
108 if( naccepted_t+nrejected_t <= 0 ) return 0 ;
109 return 1.0*naccepted_t/(naccepted_t+nrejected_t) ;
110 }
111
112 /// make one Markov chain step for a path
113 bool doMarkovStep( JSourceModel* src, JScatteringModel* sm, JTargetModel* trg, double lambda_abs, JPhotonPath& p ) ;
114
115 virtual int randomPathChange( JPhotonPath& p, JTargetModel* trg ) ;
116
117 /// set the average step size in degrees for the impact point on the target
118 void setTargetStepSize_deg( double val ) {
119 stepsize_angle_target = val * M_PI / 180 ;
120 }
121
122 /// set the average step size in [m] in the radial direction for the scattering vertices
123 void setRadialStepSize_m( double val ) {
124 stepsize_r = val ;
125 }
126
127 /**
128 Set the average step size theta in degrees for steps in the tangential direction
129 for the scattering vertices.
130 When r * theta is larger than the radial step size, the effective theta will be
131 temporarily set to a smaller value to avoid taking steps that are too large.
132 **/
133 void setTangentialStepSize_deg( double val ) {
134 stepsize_angle = val * M_PI / 180 ;
135 }
136
137 /// activate or deactive coordinate remapping
138 void setCoordinateRemapping( bool val=true ) {
140 }
141
142 /// returns true when coordinate remapping is activated, false otherwise
144
145
146 private:
147
148 /// return R0; the length scale used in the coordinate remapping
149 inline double getR0() { return 3 * stepsize_r ; }
150
151 /**
152 return the distance between the remapped vertex and the previous vertex
153 given the distance between the not-remapped vertex and the previous vertex
154 **/
155 double getRemappedDistance(double r) {
156 if( 3.0*sqrt(3)*r < getR0() ) return pow(r*getR0()*getR0(),1.0/3.0) ;
157 return r + 2.0 / (3.0*sqrt(3.0))*getR0() ;
158 }
159
160 /// inverse of getRemappedDistance
161 double getUnmappedDistance(double rprime) {
162 if( sqrt(3.0)*rprime < getR0() ) return rprime*rprime*rprime/(getR0()*getR0()) ;
163 return rprime - 2.0/(3.0*sqrt(3.0))*getR0() ;
164 }
165
166 /**
167 Return the remapped vertex position of x.
168 xprev is the (not remapped!) coordinate of the previous vertex in the photon path.
169 **/
171 double r = x.getDistance(xprev) ;
172 double rprime = getRemappedDistance(r) ;
173 return xprev + (rprime/r)*(x-xprev) ;
174 }
175
176 /**
177 Inverse of getRemappedPosition. Again, xprev is the (not remapped!) coordinate of the previous
178 vertex in the photon path.
179 **/
181 double rprime = xprime.getDistance(xprev) ;
182 double r = getUnmappedDistance(rprime) ;
183 return xprev + (r/rprime)*(xprime-xprev) ;
184 }
185
186 /// returns a remapped version of the photon path
188 JPhotonPath pprime(p) ;
189 for( int i=1; i<(int)pprime.size()-1; ++i ) {
190 pprime[i] = getRemappedPosition(p[i-1],p[i]) ;
191 }
192 return pprime ;
193 }
194
195 /// inverse of getRemappedPhotonPath
197 JPhotonPath p(pprime) ;
198 for( int i=1; i<(int)p.size()-1; ++i ) {
199 p[i] = getUnmappedPosition(p[i-1],p[i]) ;
200 }
201 return p ;
202 }
203
204 /**
205 Returns the conversion factor J needed to compute the path probability density
206 in the remapped coordinates, for a given vertex.
207
208 xprime is the remapped vertex position
209 xprev is the (not remapped!) position of the previous vertex
210
211 In casual notation (the ' indicates remapped coordinates):
212
213 rho(x) dV = rho(x) r^2 dr dOmega
214 = rho(x) r^2 / r'^2 dr/dr' r'^2 dr' dOmega
215 = rho( x(x') ) r^2 / r'^2 dr/dr' dV' <--- since dOmega == dOmega'
216 := J x rho( x(x') ) dV'
217 := rho'(x') dV'
218 Where J := r^2 / r'^2 dr/dr'
219
220 The point of this is that we typically pick r proportional to r'^3, so that
221 dr/dr' is proportional to r'^2, which cancels the factor 1/r'^2 in rho'.
222 The new factor r^2 then cancels the typical 1/r^2 singularity present in rho.
223 **/
224 double getRemappingCorrection( const JPosition3D& xprev, const JPosition3D& xprime ) {
225 double rprime = xprev.getDistance(xprime) ;
226 double r = getUnmappedDistance(rprime) ;
227 if( sqrt(3.0)*rprime >= getR0() ) {
228 // J = r^2 / r'^2 * dr/dr'
229 // dr/dr' = 1
230 return r*r/(rprime*rprime) ;
231 }
232 // J = r^2 / r'^2 * dr/dr'
233 // dr/dr' = 3 * r'^2/R0^2
234 // ==> J = 3 * r^2 / R0^2
235 return 3*r*r/(getR0()*getR0()) ;
236 }
237
238 /**
239 Return the remapping correction for an entire photon path (product of the
240 remapping correction for the individual scattering vertices).
241
242 p is the photon path in normal coordinates
243 pprime is the remapped version of p
244 **/
245 double getRemappingCorrection( const JPhotonPath& p, const JPhotonPath& pprime ) {
246 double J = 1 ;
247 for( int i=1; i<(int)p.size()-1; ++i ) {
248 J *= getRemappingCorrection(p[i-1],pprime[i]) ;
249 }
250 return J ;
251 }
252
253
255 int naccepted_r ; // number of accepted radial steps
256 int nrejected_r ; // number of rejected radial steps
257 int naccepted_t ; // number of accepted tangential steps
258 int nrejected_t ; // number of rejected tangential steps
260 double stepsize_r ;
262 } ;
263
265 int nscat = p.n-2 ;
266 int type = 0 ;
267
268 if( nscat>0 ) {
269 // pick vertex from 1 to nscat+1 (so excluding the start and end point)
270 int nv = gRandom->Integer(nscat)+1 ;
271
272 // distance w.r.t. previous vertex
273 double r = p[nv-1].getDistance(p[nv]) ;
274
275 if( gRandom->Uniform() > 0.5 ) {
276 // with P=1/2, take a step in the radial direction
277 type = 1 ;
278
279 // to avoid nasty phase space Jacobian issues, I use this rather primitive method
280 // where I simply generate a new position with a step in a random direction, then
281 // only apply the radial part of the change
282 //
283 // NOTE: simply choosing
284 // *) r+dr with P proportional to (r+dr)^2
285 // *) r-dr with P proportional to (r-dr)^2
286 // does not work. Trust me. I've tried.
287 double _r = gRandom->Exp(stepsize_r) ;
288 double _x, _y, _z ;
289 gRandom->Sphere( _x, _y, _z, _r ) ;
290 double _rnew = ( JPosition3D(_x,_y,_z) + JPosition3D(0,0,r) ).getLength() ;
291 double dr = _rnew - r ;
292
293 p[nv] = p[nv] + dr * JPosition3D(JDirection3D(p[nv]-p[nv-1])) ;
294
295 } else {
296 // with P=1/2, take a step in the tangential direction
297 type = 2 ;
298
299 // for large radii, limit the maximum angle over which we can move
300 // so that the average change in distance does not exceed stepsize_r too much
301 double _stepsize_angle = min(stepsize_angle,stepsize_r/r) ;
302 double theta = gRandom->Exp(_stepsize_angle) ;
303 double phi = gRandom->Uniform(2.0*M_PI) ;
304 JDirection3D dir( sin(theta)*cos(phi), sin(theta)*sin(phi), cos(theta) ) ;
305 JDirection3D xdir( p[nv] - p[nv-1] ) ;
306 JRotation3D rot(xdir) ;
307 dir = dir.rotate_back(rot) ;
308 p[nv] = p[nv-1] + r * JVector3D(dir) ;
309 }
310 }
311
312 // in case of a finite target, also move the end point
313 if( trg->getRadius() > 0 ) {
314 const double dct = 1-cos(stepsize_angle_target) ;
315 double ct = -2 ;
316 while( ct<=-1 ) ct = 1-gRandom->Exp(dct) ;
317 double theta = acos(ct) ;
318 double phi = gRandom->Uniform(2*M_PI) ;
319 JDirection3D original_dir( p.back()-trg->getPosition() ) ;
320 const JRotation3D R( original_dir ) ;
321 JDirection3D new_dir( JAngle3D(theta,phi) ) ;
322 new_dir = new_dir.rotate_back(R) ;
323 p.back() = trg->getPosition() + trg->getRadius() * JVector3D(new_dir) ;
324 }
325
326 return type ;
327 }
328
329 std::vector<JPhotonPath> JMarkovPathGenerator::generateEnsemble( int n, const JPhotonPath& start_path, JSourceModel* src, JScatteringModel* sm, JTargetModel* trg, double lambda_abs, int nsteps_burn_in, int nsteps_save ) {
330 vector<JPhotonPath> result ;
331
332 JPhotonPath testpath(start_path) ;
333
334 // check that the starting path is okay
335 double dist = testpath[0].getDistanceSquared(src->getPosition()) ;
336 if( dist != 0 ) {
337 cerr << "ERROR in generateEnsemble: testpath position " << testpath[0]
338 << "does not match source position " << src->getPosition() << "!" << endl ;
339 return result ;
340 }
341 {
342 double rho = getPhotonPathProbabilityDensity(testpath,src,sm,trg,lambda_abs) ;
343 if( rho == 0 ) {
344 cerr << "ERROR in generateEnsemble: testpath probability density = 0" << endl ;
345 return result ;
346 }
347 }
348
349 // convert the path to remapped coordinates
350 if( apply_coordinate_remapping ) testpath = getRemappedPhotonPath(testpath) ;
351
352 // burn-in
353 int i = 0 ;
354 while( i!=nsteps_burn_in ) {
355 doMarkovStep(src,sm,trg,lambda_abs,testpath) ;
356 ++i ;
357 }
358
359 // ensemble generation
360 i = 0 ;
361 naccepted_r = 0 ;
362 nrejected_r = 0 ;
363 naccepted_t = 0 ;
364 nrejected_t = 0 ;
365 while( i!=n ) {
366 // take nsteps_save steps
367 int j = 0 ;
368 while( j!=nsteps_save ) {
369 if( doMarkovStep(src,sm,trg,lambda_abs,testpath) ) {
370 ++j ;
371 }
372 }
373 // save the path to the ensemble
375 result.push_back( getUnmappedPhotonPath(testpath) ) ;
376 } else {
377 result.push_back( testpath ) ;
378 }
379 ++i ;
380 }
381
382 return result ;
383 }
384
386 int nscat = p.n-2 ;
387 if( nscat == 0 && !(trg->getRadius()>0) ) {
388 return false ;
389 }
390
391 // copy path
392 JPhotonPath copy = p ;
393
394 double rhoBefore ;
396 rhoBefore = getPhotonPathProbabilityDensity(copy,src,sm,trg,lambda_abs) ;
397 } else {
398 // we are working in remapped coordinates, so we have to calculate a corrected
399 // probability density
400 JPhotonPath copy_unmapped = getUnmappedPhotonPath(copy) ;
401 rhoBefore = getPhotonPathProbabilityDensity(copy_unmapped,src,sm,trg,lambda_abs) ;
402 rhoBefore *= getRemappingCorrection(copy_unmapped,copy) ;
403 }
404
405 if( rhoBefore == 0 ) {
406 cerr << "FATAL ERROR: starting probability density = 0" << endl ;
407 exit(1) ;
408 }
409
410 // apply random change to the path
411 int type = randomPathChange(copy,trg) ;
412
413 // recalculate log L
414 double rhoAfter ;
416 rhoAfter = getPhotonPathProbabilityDensity(copy,src,sm,trg,lambda_abs) ;
417 } else {
418 // we are working in remapped coordinates, so we have to calculate a corrected
419 // probability density
420 JPhotonPath copy_unmapped = getUnmappedPhotonPath(copy) ;
421 rhoAfter = getPhotonPathProbabilityDensity(copy_unmapped,src,sm,trg,lambda_abs) ;
422 rhoAfter *= getRemappingCorrection(copy_unmapped,copy) ;
423 }
424
425 // use Metropolis-Hastings algorithm to keep or reject change
426 if( rhoAfter > rhoBefore ) {
427 // accept the change
428 p = copy ;
429 if( type < 2 ) ++naccepted_r ;
430 if( type == 2 ) ++naccepted_t ;
431 return true ;
432 } else {
433 // accept the change with some probability
434 double P = rhoAfter/rhoBefore ;
435 if( gRandom->Uniform()<P ) {
436 p = copy ;
437 if( type < 2 ) ++naccepted_r ;
438 if( type == 2 ) ++naccepted_t ;
439 return true ;
440 }
441 }
442 if( type < 2 ) ++nrejected_r ;
443 if( type == 2 ) ++nrejected_t ;
444 return false ;
445 }
446
447}
448
449#endif
Data structure for angles in three dimensions.
Definition JAngle3D.hh:35
Data structure for direction in three dimensions.
JDirection3D & rotate_back(const JRotation3D &R)
Rotate back.
Data structure for position in three dimensions.
Data structure for vector in three dimensions.
Definition JVector3D.hh:36
double getDistance(const JVector3D &pos) const
Get distance to point.
Definition JVector3D.hh:270
The JMarkovPathGenerator generates ensembles of photon paths using a Markov Chain Monte Carlo (MCMC).
int getNsteps()
get the number of Markov steps taken in the last call to generateEnsemble (after burn-in)
void setRadialStepSize_m(double val)
set the average step size in [m] in the radial direction for the scattering vertices
JMarkovPathGenerator()
standard constructor
double getRemappedDistance(double r)
return the distance between the remapped vertex and the previous vertex given the distance between th...
double getRemappingCorrection(const JPhotonPath &p, const JPhotonPath &pprime)
Return the remapping correction for an entire photon path (product of the remapping correction for th...
void setCoordinateRemapping(bool val=true)
activate or deactive coordinate remapping
int getNrejectedSteps()
get the number of rejected steps during the last call to generateEnsemble (after burn-in)
bool doMarkovStep(JSourceModel *src, JScatteringModel *sm, JTargetModel *trg, double lambda_abs, JPhotonPath &p)
make one Markov chain step for a path
void setTargetStepSize_deg(double val)
set the average step size in degrees for the impact point on the target
bool getCoordinateRemapping()
returns true when coordinate remapping is activated, false otherwise
JPosition3D getUnmappedPosition(const JPosition3D &xprev, const JPosition3D &xprime)
Inverse of getRemappedPosition.
JPhotonPath getUnmappedPhotonPath(const JPhotonPath &pprime)
inverse of getRemappedPhotonPath
double getR0()
return R0; the length scale used in the coordinate remapping
void setTangentialStepSize_deg(double val)
Set the average step size theta in degrees for steps in the tangential direction for the scattering v...
double getFractionAccepted_radial()
get the fraction of steps that were accepted when a radial step was performed during the last call to...
JPhotonPath getRemappedPhotonPath(const JPhotonPath &p)
returns a remapped version of the photon path
int getNacceptedSteps()
get the number of accepted steps taken during the last call to generateEnsemble (after burn-in)
std::vector< JPhotonPath > generateEnsemble(int n, const JPhotonPath &start_path, JSourceModel *src, JScatteringModel *sm, JTargetModel *trg, double lambda_abs, int nsteps_burn_in, int nsteps_save)
Generate an ensemble of n paths with a fixed number of scatterings by MCMC-sampling the given scatter...
virtual int randomPathChange(JPhotonPath &p, JTargetModel *trg)
double getUnmappedDistance(double rprime)
inverse of getRemappedDistance
double getRemappingCorrection(const JPosition3D &xprev, const JPosition3D &xprime)
Returns the conversion factor J needed to compute the path probability density in the remapped coordi...
double getFractionAccepted_tangential()
get the fraction of steps that were accepted when a tangential step was performed during the last cal...
JPosition3D getRemappedPosition(const JPosition3D &xprev, const JPosition3D &x)
Return the remapped vertex position of x.
double getFractionAccepted()
get the fraction of accepted steps during the last call to generateEnsemble (after burn-in)
A photon path.
Virtual base class for a scattering model.
Virtual base class for a light source.
const JPosition3D & getPosition() const
Virtual base class for a light detector ("photon target").
const JPosition3D & getPosition() const
double getPhotonPathProbabilityDensity(JPhotonPath &p, JSourceModel *src, JScatteringModel *sm, JTargetModel *trg, double lambda_abs)
Return the probability density for a photon path with the given ingredients.
This name space includes all other name spaces (except KM3NETDAQ, KM3NET and ANTARES).