source: PSPA/Interface_Web/trunk/pspaWT/src/particleBeam.cc @ 243

Last change on this file since 243 was 243, checked in by lemeur, 11 years ago

rationalisation pour introduire nouveau logiciel

File size: 18.6 KB
Line 
1#include "particleBeam.h"
2#include "mathematicalConstants.h"
3#include "PhysicalConstants.h"
4#include "mathematicalTools.h"
5//#include <string>
6#include <stdio.h>
7#include <algorithm>
8#include <sstream>
9//#include "environmentVariables.h"
10#include <Wt/WApplication>
11
12using namespace std;
13
14particleBeam::particleBeam()  {
15  // rij_transportMoments_.resize(6);
16  // unsigned dim=0;
17  // unsigned k;
18  // for ( k=0; k < 6; k++){
19  //   rij_transportMoments_.at(k).resize(++dim);
20  // }
21  P0Transport_ = 0.0;
22  particleRepresentationOk_ = false;
23  momentRepresentationOk_ = false;
24}
25
26void particleBeam::clear() {
27  goodPartic_.clear();
28  rij_.raz();
29  P0Transport_ = 0.0;
30  particleRepresentationOk_ = false;
31  momentRepresentationOk_ = false;
32}
33
34int particleBeam::getNbParticles() const {
35  return goodPartic_.size();
36}
37
38const beam2Moments&   particleBeam::getTransportMoments() const  { 
39    return rij_;
40}
41
42double particleBeam::getSigmaTransportij(unsigned i, unsigned j)  {
43  if ( i < 1 || i > 6 || j < 1 || j > 6 ) {
44    cerr << " particleBeam::getSigmaTransportij() indices out of range  " << endl;
45    return 0.0;
46  }
47  if ( !momentRepresentationOk_ ) {
48    cerr << " particleBeam::getSigmaTransportij() beam is not in moment representation " << endl;
49    return 0.0;
50  }
51
52  i--;
53  j--;
54  if ( j > i ) {
55    unsigned aux = i;
56    i = j;
57    j = aux;
58  }
59  return ( rij_.getMatrix().at(i) ).at(j);
60}
61
62
63double particleBeam::getUnnormalizedEmittanceX() {
64  double r = getSigmaTransportij(2,1);
65  double rac = (1 - r*r);
66  if ( rac <= 0.0 ) {
67    return 0.0;
68  }
69  rac = sqrt(1 - r*r);
70  return  10.*getSigmaTransportij(1,1) * getSigmaTransportij(2,2) * rac; // en pi.mm.mrad
71}
72
73double particleBeam::getP0Transport() const { 
74  return P0Transport_;
75}
76
77void particleBeam::set2Moments(beam2Moments& moments) {
78  rij_ = moments;
79  momentRepresentationOk_ = true;
80}
81
82void particleBeam::setWithParticles(vector<double>& centroid, bareParticle& referencePart, vector<bareParticle>& particles) {
83  centroid_.clear();
84  centroid_ = centroid;
85  referenceParticle_ = referencePart;
86  goodPartic_.clear();
87  goodPartic_ = particles;
88  particleRepresentationOk_ = true;
89}
90
91
92bool particleBeam::particleRepresentationOk() const {
93  return particleRepresentationOk_;
94}
95bool particleBeam::momentRepresentationOk() const {
96  return momentRepresentationOk_;
97}
98
99void  particleBeam::addParticle( bareParticle p)
100{
101  goodPartic_.push_back(p);
102}
103
104const vector<bareParticle>& particleBeam::getParticleVector() const
105{
106  return goodPartic_;
107}
108vector<bareParticle>& particleBeam::getParticleVector() 
109{
110  return goodPartic_;
111}
112
113
114// bool  particleBeam::setFromTransport(string workingDir, string elLab, const nomdElements elem)
115// {
116//   string elementLabel = elLab;
117//   // transformer le label en majuscules ; je ne suis pas sur que ca
118//   // marchera a tous les coups (glm)
119//   std::transform(elementLabel.begin(), elementLabel.end(), elementLabel.begin(), (int (*)(int))std::toupper);
120
121//   cout << " particleBeam::setFromTransport on cherche element " << elementLabel << endl;
122//   string buf;
123//   ifstream infile;
124//   //  string nameIn = WORKINGAREA + "transport.output";
125//   string nameIn = workingDir + "transport.output";
126//   infile.open(nameIn.c_str(), ios::in);
127//   if (!infile) {
128//     cerr << " particleBeam::setFromTransport : error re-opening transport output stream " << nameIn << endl;
129//     return false;
130//   }
131//   //  else cout << " particleBeam::setFromTransport() : ouverture du fichier " << nameIn << endl;
132
133//   string::size_type nn = string::npos;
134//   while ( getline(infile, buf) ) {
135//     //      cout << " buf= "  << buf << endl;
136//     nn = buf.find(elementLabel);
137//     //     cout << " string::npos= " << string::npos << " nn= " << nn << endl;
138//     if ( nn != string::npos ) {
139//       //       cout << " particleBeam::setFromTransport : element " << elementLabel << " trouve " << endl;
140//       break;
141//     }
142//   }
143
144//   if ( nn == string::npos ) {
145//     cerr << " particleBeam::setFromTransport : element " << elementLabel << " non trouve dans le fichier  " << nameIn << endl;
146//     return false;
147//   }
148//   if (elem.getElementType() == bend ) {
149//     getline(infile, buf);
150//     getline(infile, buf);
151//   }
152//   readTransportMoments(infile);
153//   impressionDesMoments();
154//   infile.close();
155//   momentRepresentationOk_ = true;
156//   return true;
157// }
158
159// bool  particleBeam::setFromTransport(string workingDir, string elLab, const nomdElements elem)
160// {
161//   string elementLabel = elLab;
162//   // transformer le label en majuscules ; je ne suis pas sur que ca
163//   // marchera a tous les coups (glm)
164//   std::transform(elementLabel.begin(), elementLabel.end(), elementLabel.begin(), (int (*)(int))std::toupper);
165
166//   cout << " particleBeam::setFromTransport on cherche element " << elementLabel << endl;
167//   ifstream infile;
168//   //  string nameIn = WORKINGAREA + "transport.output";
169//   string nameIn = workingDir + "transport.output";
170//   infile.open(nameIn.c_str(), ios::in);
171//   if (!infile) {
172//     cerr << " particleBeam::setFromTransport : error re-opening transport output stream " << nameIn << endl;
173//     return false;
174//   }
175//   //  else cout << " particleBeam::setFromTransport() : ouverture du fichier " << nameIn << endl;
176
177//   string::size_type nn = string::npos;
178//   string  fichier;
179//   string buf;
180//   unsigned compteur = 0;
181//   while ( getline(infile, buf) ) {
182//       fichier += buf+"\n";
183//     nn = buf.find(elementLabel);
184//     if ( nn != string::npos ) {
185//       compteur++;
186//       //      break;
187//     }
188//   }
189//   infile.close();
190//   cout << " compteur= " << compteur << endl;
191//   if ( compteur == 0 ) {
192//     cerr << " particleBeam::setFromTransport : element " << elementLabel << " non trouve dans le fichier  " << nameIn << endl;
193//     return false;
194//   }
195
196
197//   stringstream fichierStream(fichier);
198//   buf.clear();
199//   unsigned relu = 0;
200//   while ( std::getline(fichierStream, buf) ) {
201//     //    cout << " relecture buffer : " << buf << endl;
202//     nn = buf.find(elementLabel);
203//     if ( nn != string::npos ) {
204//       relu++;
205//       if ( relu == compteur ) {
206//      cout << " TROUVE !" << endl;
207//      break;
208//       }
209//     }
210//   }
211//
212//   readTransportMoments(fichierStream);
213//   //  impressionDesMoments();
214//   momentRepresentationOk_ = true;
215//   return true;
216// }
217
218
219// bool particleBeam::setFromParmela(string workingDir,unsigned numeroElement, double referencefrequency) {
220//   unsigned  k;
221//   FILE* filefais;
222//   string nomfilefais = workingDir + "parmdesz";
223//   cout << " nom fichier desz : " << nomfilefais << endl;
224//   filefais = fopen(nomfilefais.c_str(), "r");
225
226//   if ( filefais == (FILE*)0 ) {
227//     cerr << " particleBeam::setFromParmela() erreur a l'ouverture du fichier" << nomfilefais  << endl;;
228//     return false;
229//   }
230//   else cout << " particleBeam::setFromParmela() : ouverture du fichier " << nomfilefais << endl;
231
232//   parmelaParticle partic;
233//   std::vector<parmelaParticle> faisceau;
234
235//   cout << " particleBeam::setFromParmela : numeroElement = " << numeroElement << endl;
236//   unsigned indexElement = numeroElement-1;
237 
238
239
240
241//   int testNombrePartRef =0;
242//   double phaseRef;
243
244//   while( partic.readFromParmelaFile(filefais) > 0 ) {
245//     if ( partic.ne == (int)indexElement )
246//       {
247//      faisceau.push_back(partic);
248
249//      if ( partic.np == 1 ) {
250//        // en principe on est sur la particule de reference
251//        if ( fabs(partic.xx) > EPSILON || fabs(partic.yy) > EPSILON || fabs(partic.xxp) > EPSILON  || fabs(partic.yyp) > EPSILON) {
252//          printf(" ATTENTION part. reference douteuse  \n");
253//          partic.imprim();
254//        }
255//        phaseRef = partic.phi;
256//        TRIDVECTOR  posRef(partic.xx,partic.yy,0.0);
257//        TRIDVECTOR betagammaRef(partic.xxp*partic.begamz, partic.yyp*partic.begamz, partic.begamz);
258//        referenceParticle_ = bareParticle(posRef, betagammaRef);
259//        testNombrePartRef++;
260//        if ( testNombrePartRef != 1 ) {
261//          cerr << " TROP DE PART. DE REF : " << testNombrePartRef << " !! " << endl;
262//          return false;
263//        }
264//      }
265//       }
266//   }
267
268//   if ( faisceau.size() == 0)
269//     {
270//       cerr << " particleBeam::setFromParmela echec lecture  element " << numeroElement << endl;
271//       return false;
272//     }
273 
274//   // facteur  c/ 360. pour calculer (c dphi) / (360.freq)
275//   // avec freq en Mhz et dphi en degres et résultat en cm:
276//   double FACTEUR =  83.3333;  // ameliorer la precision
277
278
279
280//   // pour l'instant on choisit un centroid nul;
281//   centroid_ = vector<double>(6,0.0);
282
283//   goodPartic_.clear();
284//   goodPartic_.resize(faisceau.size(), bareParticle());
285//   double x,xp,y,yp;
286//   double betagammaz;
287//   double betaz;
288//   double deltaz;
289//   double dephas;
290//   double g;
291//   TRIDVECTOR  pos;
292//   TRIDVECTOR betagamma;
293//   // contrairement a ce qu'indique la notice PARMELA, dans parmdesz, les xp et yp
294//   // sont donnes en radians
295//   for ( k=0; k < faisceau.size(); k++) {
296//     x=faisceau.at(k).xx;
297//     xp=faisceau.at(k).xxp;
298//     y=faisceau.at(k).yy;
299//     yp=faisceau.at(k).yyp;
300
301//     // dephasage par rapport a la reference 
302//     dephas = faisceau.at(k).phi - phaseRef; // degrés
303//     g = faisceau.at(k).wz/ERESTMeV;
304//     betagammaz = faisceau.at(k).begamz;
305//     betaz = betagammaz/(g+1.0);
306//     deltaz = FACTEUR * betaz * dephas / referencefrequency;
307//     x += xp * deltaz;
308//     y += yp * deltaz;
309//     pos.setComponents(x,y,deltaz);
310//     betagamma.setComponents(xp*betagammaz, yp*betagammaz, betagammaz);
311//     goodPartic_.at(k) = bareParticle(pos,betagamma);
312//   }
313//   particleRepresentationOk_ = true;
314//   return true;
315// }
316
317
318void particleBeam::getVariance(double& varx, double& vary, double& varz) const {
319  unsigned int k;
320  double x,y,z;
321  double xav = 0.;
322  double yav = 0.;
323  double zav = 0.;
324  double xavsq = 0.;
325  double yavsq = 0.;
326  double zavsq = 0.;
327
328  TRIDVECTOR pos;
329
330
331  for ( k = 0 ; k < goodPartic_.size(); k++) {
332    pos = goodPartic_.at(k).getPosition();
333    pos.getComponents(x,y,z);
334    //      partic_[k].getXYZ(x,y,z);
335    xav += x;
336    xavsq += x*x;
337    yav += y;
338    yavsq += y*y;
339    zav += z;
340    zavsq += z*z;
341  }
342
343  double aginv = double (goodPartic_.size());
344  aginv = 1.0/aginv;
345
346  varx =  aginv * ( xavsq - xav*xav*aginv );
347  vary =  aginv * ( yavsq - yav*yav*aginv );
348  varz =  aginv * ( zavsq - zav*zav*aginv );
349}
350
351
352void particleBeam::printAllXYZ() const {
353  cout << " dump du faisceau : " << endl;
354  cout <<  goodPartic_.size() << " particules " << endl;
355  unsigned int k;
356  for ( k = 0 ; k < goodPartic_.size(); k++)
357    {
358      double xx,yy,zz;
359      goodPartic_.at(k).getPosition().getComponents(xx,yy,zz);
360      cout << " part. numero " << k << "  x= " << xx << " y= " << yy  << " z= " << zz << endl;
361    }
362}
363
364
365
366void particleBeam::Zrange(double& zmin, double& zmax) const {
367  double z;
368  zmin = GRAND;
369  zmax = -zmin;
370
371  unsigned int k;
372  for ( k = 0 ; k < goodPartic_.size(); k++)
373    {
374      z = goodPartic_.at(k).getZ();
375      if ( z < zmin ) zmin = z;
376      else if ( z > zmax) zmax = z;         
377    }
378}
379
380
381
382string particleBeam::FileOutputFlow() const {
383  ostringstream sortie;
384  unsigned int k;
385  for ( k = 0 ; k < goodPartic_.size(); k++)
386    {
387      sortie << goodPartic_.at(k).FileOutputFlow() << endl;
388    }
389  sortie << endl;
390  return sortie.str();
391}
392
393bool particleBeam::FileInput( ifstream& ifs) {
394  bool test = true;
395  string dum1, dum2;
396  double dummy;
397  if ( !( ifs >> dum1 >> dum2 >> dummy) ) return false;
398 
399  bareParticle pp;
400  while ( pp.FileInput(ifs) )
401    {
402      addParticle( pp);
403    }
404  return test;
405}
406
407void particleBeam::buildMomentRepresentation() {
408
409  unsigned k,j,m;
410  double auxj, auxm;
411  if ( !particleRepresentationOk_)
412    {
413      cerr << " particleBeam::buildMomentRepresentation() vecteur de particules invalide" << endl;
414      return;
415    }
416
417  cout << " buildMomentRepresentation " << endl;
418  //  printAllXYZ();
419
420  double gref = referenceParticle_.getGamma() - 1.0;
421  double P_reference_MeV_sur_c = sqrt( gref*(gref+2) );
422
423  // initialisation des moments
424  razDesMoments();
425
426  // accumulation
427  TRIDVECTOR pos;
428  TRIDVECTOR begam;
429  double gamma;
430  double begamz;
431  double g;
432  double PMeVsc;
433  double del;
434  vector<double> part(6);
435
436    vector< vector<double> >& matrice = rij_.getMatrix();
437
438
439  for (k=0; k < goodPartic_.size(); k++) {
440    gamma = goodPartic_.at(k).getGamma();
441    pos = goodPartic_.at(k).getPosition();
442    begam= goodPartic_.at(k).getBetaGamma();
443    begamz = begam.getComponent(2);
444    g = gamma -1.0;
445    PMeVsc = sqrt( g*(g+2) );
446    del = 100.0 * ( PMeVsc -  P_reference_MeV_sur_c ) / P_reference_MeV_sur_c ; // en %
447
448    part[0] = pos.getComponent(0);
449    part[1] = begam.getComponent(0)/begamz;
450    part[2] = pos.getComponent(1);
451    part[3] = begam.getComponent(1)/begamz;
452    part[4] = pos.getComponent(2);
453    part[5] = del;
454
455    for ( j = 0; j < 6; j++) {
456      auxj = part.at(j) - centroid_.at(j);
457      for (m=0; m <= j; m++) 
458        {
459          auxm = part.at(m) - centroid_.at(m);
460
461          ( matrice.at(j) ).at(m) += auxj*auxm;
462          //      ( rij_transportMoments_.at(j) ).at(m) += auxj*auxm;
463
464
465          //          cout << " j= " << j << " m= " << m << " rjm= " << ( rij_transportMoments_.at(j) ).at(m) << endl;
466        }
467    }
468  }
469
470
471  // moyenne
472  double facmoy = 1.0/double( goodPartic_.size() );
473  for ( j = 0; j < 6; j++) {
474        ( matrice.at(j) ).at(j) = sqrt(( matrice.at(j) ).at(j) * facmoy );
475  }
476
477  for ( j = 0; j < 6; j++) {
478    auxj =  ( matrice.at(j) ).at(j);
479    for (m=0; m < j; m++) {
480      auxm = ( matrice.at(m) ).at(m);
481      (  matrice.at(j) ).at(m) *= facmoy/(auxj * auxm);
482    }
483  }
484   
485  ////////////////// si C21 = 1 , transport plante ! a voir //////////
486cout << " valeur initiale de  C21: " << ( matrice.at(1) ).at(0) << endl;
487  if ( ( matrice.at(1) ).at(0) >0.999999  ) {
488    ( matrice.at(1) ).at(0) = 0.999999;
489    cout << " j'ai fait la correction C21: " << ( matrice.at(1) ).at(0) << endl;
490  }
491 
492
493  // les longueurs sont en cm
494  // les angles en radians, on passe en mrad;
495
496  double uniteAngle = 1.0e+3;
497  ( matrice.at(1) ).at(1)  *= uniteAngle;
498  ( matrice.at(3) ).at(3)  *= uniteAngle;
499  P0Transport_ = 1.0e-3*ERESTMeV*P_reference_MeV_sur_c;
500
501  //  cout << " buildmomentrepresentation impression des moments " << endl;
502  //  impressionDesMoments();
503
504  momentRepresentationOk_ = true;
505}
506
507void particleBeam::impressionDesMoments() const {
508  rij_.impression();
509}
510
511void particleBeam::razDesMoments() {
512  rij_.raz();
513}
514
515
516// void particleBeam::readTransportMoments(ifstream& inp) {
517// rij_.readFromTransportOutput(inp);
518// }
519
520// void particleBeam::readTransportMoments(stringstream& inp) {
521// rij_.readFromTransportOutput(inp);
522// }
523
524double particleBeam::getXmaxRms() {
525  if ( !momentRepresentationOk_ ) buildMomentRepresentation();
526  return ( rij_.getMatrix().at(0) ).at(0);
527  // return ( rij_transportMoments_.at(0) ).at(0);
528}
529
530void particleBeam::donneesDessinEllipseXxp(vector<double>& xcor, vector<double>& ycor) {
531  int k;
532  double x,y;
533
534  if ( !momentRepresentationOk_ ) return;
535
536  xcor.clear();
537  ycor.clear();
538
539  double xm = ( rij_.getMatrix().at(0) ).at(0);
540  double ym = ( rij_.getMatrix().at(1) ).at(1);
541  double r  = ( rij_.getMatrix().at(1) ).at(0);
542
543  cout <<  " racs11= " << xm << " racs22= " << ym << " r12= " << r << endl;
544
545
546  int nbintv = 50;
547  if ( xm == 0.0 ) return;
548  double pas = 2.0 * xm / nbintv;
549
550  //  cout << " r= " << r << endl;
551  double rac = (1 - r*r);
552  if ( rac > 0.0 ) 
553    {
554      cout << " cas rac > " << endl;
555      rac = sqrt(1 - r*r);
556      double alpha = -r / rac;
557      double beta = xm / ( ym * rac);
558      //  double gamma = ym / ( xm * rac );
559      double epsil = xm * ym * rac;
560      double fac1 = -1.0 / ( beta * beta);
561      double fac2 = epsil/beta;
562      double fac3 = -alpha/beta;
563      double aux;
564      for ( k=0; k < nbintv; k++)
565        {
566          x = -xm + k*pas;
567          aux = fac1 * x * x + fac2;
568          //     cout << " aux2= " << aux << endl;
569          if ( aux <= 0.0 )
570            {
571              aux = 0.0;
572            }
573          else aux = sqrt(aux);
574     
575          //        y = fac3*x;
576          y = fac3*x + aux;
577          xcor.push_back(x);
578          ycor.push_back(y);
579        }
580
581      for ( k=0; k <= nbintv; k++)
582        {
583          x = xm - k*pas;
584          aux =  fac1 * x * x + fac2;
585          if ( aux <= 0.0 ) 
586            {
587              aux = 0.0;
588            }
589          else aux = sqrt(aux);
590          //   y = fac3*x;
591          y = fac3*x - aux;
592          xcor.push_back(x);
593          ycor.push_back(y);
594        }
595    }
596  else
597    // cas degenere
598    {
599      cout << " cas degenere " << endl;
600      double fac = ym/xm;
601      for ( k=0; k < nbintv; k++)
602        {
603          x = -xm + k*pas;
604          y = fac*x;
605          xcor.push_back(x);
606          ycor.push_back(y);
607        }
608       
609    }
610}
611
612void particleBeam::histogramme(vector<double>&xcor,vector<int>& hist,int& cnts)
613{
614  double gammin= GRAND;
615  double gammax= -gammin;
616  double Emoy= 0.0;
617  double ecatyp= 0.0;
618
619  for (unsigned int k = 0; k < goodPartic_.size(); k++)
620    {
621      double gamma = goodPartic_.at(k).getGamma();
622      double EMev = (gamma-1.0)*ERESTMeV;
623      if (gamma < gammin) gammin = gamma;
624      else if (gamma > gammax) gammax = gamma;
625      Emoy += EMev;
626      ecatyp += EMev*EMev;
627    }
628
629  double sum= (float)goodPartic_.size();
630  Emoy /= sum;
631  ecatyp /= sum;
632  ecatyp = sqrt(abs(ecatyp-Emoy*Emoy));
633
634  double Emin = (gammin-1.0)*ERESTMeV;
635  double Emax = (gammax-1.0)*ERESTMeV;
636  cout << "energie cinetique -moyenne " << Emoy << " Mev " << "-mini " << Emin << " Mev " << "-maxi " << Emax << " Mev " << "ecart type " << ecatyp*1000.0 << " Kev" << endl;
637
638  vector<double> Eshf;
639  for (unsigned int k = 0; k < goodPartic_.size(); k++)
640    {
641      double gamma = goodPartic_.at(k).getGamma();
642      double EMev = (gamma-1.0)*ERESTMeV;
643      Eshf.push_back(EMev-Emoy);
644    }
645
646  //////////////////////////////////////////////////////////////////////////////
647 
648  // demi fenetre en energie, et pas de l'histogramme
649  //  double hfene= max(3.*ecatyp-Emoy,Emoy-3.*ecatyp);
650  double hfene= 3.*ecatyp;
651  double hpas = hfene/25.;
652
653  cout << "demi fenetre " << hfene << ", hpas= " << hpas << endl;
654
655  double vmin = -hfene;
656  double dfen = 2.*hfene;
657  int ihist = dfen/hpas;
658  double phist = ihist*hpas;
659  double dpas = hpas-(dfen-phist);
660  if(dpas <= hpas*1.e-03) {
661    ihist++;
662    phist= ihist*hpas;
663  }
664  double vmax= vmin+hpas*ihist;
665 
666  cout << "'xAxisNumberOfBins= " << ihist <<", xAxisMinimum= " << vmin << ", xAxisMaximum= " << vmax << ", NParticules= " << Eshf.size() << ", phist " << phist << endl;
667
668  xcor= vector<double>(ihist);
669  for (int i = 0; i < ihist; ++i) {
670
671    // on gradue l'abcisse en pourcents
672    xcor[i]= 100.*( vmin+i*hpas );
673  }
674
675  hist= vector<int>(ihist,0);
676  for (unsigned i = 0; i < Eshf.size(); ++i) {
677    double var= Eshf[i]-vmin;
678    if(var < 0 || var >= phist) cout<<"out of range "<<var<<", ("<< i<<")"<< endl;
679    int k= var/hpas;
680    int kk= (int)floor(var/hpas);
681    if(i%20 == 0) cout<<"v("<<i<<")= " <<var<<" ["<<k<<"-"<<kk<<"], "<<endl; 
682    hist[kk]++;
683  }
684
685  cnts= 0;
686  for (int i = 0; i < ihist; ++i) {
687    if(hist.at(i) > 0) cnts++;
688    cout<<"("<<xcor.at(i)<<","<<hist.at(i)<<") ";
689  }
690  cout<< " ... cnts=  " << cnts << endl;
691}
Note: See TracBrowser for help on using the repository browser.