go-hep.org/x/hep@v0.38.1/fads/propagator.go (about)

     1  // Copyright ©2017 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package fads
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"reflect"
    11  	"sort"
    12  
    13  	"go-hep.org/x/hep/fmom"
    14  	"go-hep.org/x/hep/fwk"
    15  )
    16  
    17  type Propagator struct {
    18  	fwk.TaskBase
    19  
    20  	radius  float64
    21  	radius2 float64
    22  	halflen float64
    23  	bz      float64
    24  
    25  	input  string
    26  	output string
    27  
    28  	hadrons string
    29  	eles    string
    30  	muons   string
    31  }
    32  
    33  func (tsk *Propagator) Configure(ctx fwk.Context) error {
    34  	var err error
    35  
    36  	tsk.radius2 = tsk.radius * tsk.radius
    37  	if tsk.radius < 1.0e-2 {
    38  		return fmt.Errorf("%s: too small radius value (%v)", tsk.Name(), tsk.radius)
    39  	}
    40  
    41  	if tsk.halflen < 1.0e-2 {
    42  		return fmt.Errorf("%s: too small 1/2-length value (%v)", tsk.Name(), tsk.halflen)
    43  	}
    44  
    45  	err = tsk.DeclInPort(tsk.input, reflect.TypeOf([]Candidate{}))
    46  	if err != nil {
    47  		return err
    48  	}
    49  
    50  	err = tsk.DeclOutPort(tsk.output, reflect.TypeOf([]Candidate{}))
    51  	if err != nil {
    52  		return err
    53  	}
    54  
    55  	err = tsk.DeclOutPort(tsk.hadrons, reflect.TypeOf([]Candidate{}))
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	err = tsk.DeclOutPort(tsk.eles, reflect.TypeOf([]Candidate{}))
    61  	if err != nil {
    62  		return err
    63  	}
    64  
    65  	err = tsk.DeclOutPort(tsk.muons, reflect.TypeOf([]Candidate{}))
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	return err
    71  }
    72  
    73  func (tsk *Propagator) StartTask(ctx fwk.Context) error {
    74  	var err error
    75  
    76  	return err
    77  }
    78  
    79  func (tsk *Propagator) StopTask(ctx fwk.Context) error {
    80  	var err error
    81  
    82  	return err
    83  }
    84  
    85  func (tsk *Propagator) Process(ctx fwk.Context) error {
    86  	var err error
    87  	store := ctx.Store()
    88  	msg := ctx.Msg()
    89  
    90  	v, err := store.Get(tsk.input)
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	input := v.([]Candidate)
    96  	msg.Debugf(">>> candidates: %v\n", len(input))
    97  
    98  	output := make([]Candidate, 0)
    99  	defer func() {
   100  		err = store.Put(tsk.output, output)
   101  	}()
   102  
   103  	hadrons := make([]Candidate, 0)
   104  	eles := make([]Candidate, 0)
   105  	muons := make([]Candidate, 0)
   106  	defer func() {
   107  		err = store.Put(tsk.hadrons, hadrons)
   108  		if err != nil {
   109  			return
   110  		}
   111  		err = store.Put(tsk.eles, eles)
   112  		if err != nil {
   113  			return
   114  		}
   115  		err = store.Put(tsk.muons, muons)
   116  		if err != nil {
   117  			return
   118  		}
   119  	}()
   120  
   121  	const (
   122  		cLight  = 2.99792458e8
   123  		cLight2 = cLight * cLight
   124  	)
   125  
   126  	for i := range input {
   127  		cand := &input[i]
   128  		x := cand.Pos.X() * 1e-3
   129  		y := cand.Pos.Y() * 1e-3
   130  		z := cand.Pos.Z() * 1e-3
   131  
   132  		// is particle inside cylinder ?
   133  		if math.Hypot(x, y) > tsk.radius || math.Abs(z) > tsk.halflen {
   134  			continue
   135  		}
   136  
   137  		px := cand.Mom.Px()
   138  		py := cand.Mom.Py()
   139  		pt2 := px*px + py*py
   140  		if pt2 < 1e-9 {
   141  			continue
   142  		}
   143  
   144  		q := float64(cand.Charge())
   145  		if math.Abs(q) < 1e-9 || math.Abs(tsk.bz) < 1e-9 {
   146  			// solve pt2*t^2 + 2(px.x + py.y)*t + (radius2 - x.x - y.y) = 0
   147  			v := px*y - py*x
   148  			discr2 := pt2*tsk.radius2 - v*v
   149  			if discr2 < 0 {
   150  				// no solution
   151  				continue
   152  			}
   153  
   154  			v = px*x + py*y
   155  			discr := math.Sqrt(discr2)
   156  			t1 := (-v + discr) / pt2
   157  			t2 := (-v - discr) / pt2
   158  			t := 0.0
   159  			switch t1 < 0 {
   160  			case true:
   161  				t = t2
   162  			case false:
   163  				t = t1
   164  			}
   165  
   166  			pz := cand.Mom.Pz()
   167  			e := cand.Mom.E()
   168  
   169  			zt := z + pz*t
   170  			if math.Abs(zt) > tsk.halflen {
   171  				invpz := 1.0 / pz
   172  				t3 := (+tsk.halflen - z) * invpz
   173  				t4 := (-tsk.halflen - z) * invpz
   174  				if t3 < 0 {
   175  					t = t4
   176  				} else {
   177  					t = t3
   178  				}
   179  			}
   180  
   181  			xt := x + px*t
   182  			yt := y + py*t
   183  			zt = z + pz*t
   184  
   185  			mother := cand
   186  			c := cand.Clone()
   187  			c.Pos = fmom.NewPxPyPzE(xt*1e3, yt*1e3, zt*1e3, cand.Pos.T()+t*e*1e3)
   188  			c.Add(mother)
   189  
   190  			output = append(output, *c)
   191  			if math.Abs(q) > 1e-9 {
   192  				pid := max(c.Pid, 0)
   193  				switch pid {
   194  				case 11:
   195  					eles = append(eles, *c)
   196  				case 13:
   197  					muons = append(muons, *c)
   198  				default:
   199  					hadrons = append(hadrons, *c)
   200  				}
   201  			}
   202  		} else {
   203  			// 1.  initial transverse momentum p_{T0} : Part->pt
   204  			//     initial transverse momentum direction \phi_0 = -atan(p_X0/p_Y0)
   205  			//     relativistic gamma : gamma = E/mc² ; gammam = gamma \times m
   206  			//     giration frequency \omega = q/(gamma m) fBz
   207  			//     helix radius r = p_T0 / (omega gamma m)
   208  
   209  			e := cand.Mom.E()
   210  			pt := cand.Mom.Pt()
   211  			pz := cand.Mom.Pz()
   212  
   213  			gammam := e * 1e9 / cLight2  // in eV/c^2
   214  			omega := q * tsk.bz / gammam // omega is in [89875518 / s]
   215  			r := pt / (q * tsk.bz) * 1e9 / cLight
   216  
   217  			phi0 := math.Atan2(py, px) // [rad] in [-pi,pi)
   218  
   219  			// 2. helix axis coordinates
   220  			xc := x + r*math.Sin(phi0)
   221  			yc := y - r*math.Cos(phi0)
   222  			rc := math.Hypot(xc, yc)
   223  			phic := math.Atan2(yc, xc)
   224  			phi := phic
   225  			if xc < 0 {
   226  				phi += math.Pi
   227  			}
   228  
   229  			// 3. time evaluation t = TMath::Min(t_r, t_z)
   230  			//    t_r : time to exit from the sides
   231  			//    t_z : time to exit from the front or the back
   232  			tr := 0.0 // in [ns]
   233  			signpz := -1.0
   234  			if pz > 0 {
   235  				signpz = +1.0
   236  			}
   237  
   238  			tz := 0.0
   239  			if pz == 0 {
   240  				tz = 1e99
   241  			} else {
   242  				tz = gammam / (pz * 1e9 / cLight) * (-z + tsk.halflen*signpz)
   243  			}
   244  
   245  			absr := math.Abs(r)
   246  			t := 0.0
   247  			if rc+absr < tsk.radius {
   248  				// helix does not cross the cylinder sides
   249  				t = tz
   250  			} else {
   251  				asinrho := math.Asin((tsk.radius*tsk.radius - rc*rc - r*r) * 0.5 / (absr * rc))
   252  				delta := phi0 - phi
   253  				if delta < -math.Pi {
   254  					delta += 2.0 * math.Pi
   255  				}
   256  				if delta > math.Pi {
   257  					delta -= 2.0 * math.Pi
   258  				}
   259  
   260  				t1 := (delta + asinrho) / omega
   261  				t2 := (delta + math.Pi - asinrho) / omega
   262  				t3 := (delta + math.Pi + asinrho) / omega
   263  				t4 := (delta - asinrho) / omega
   264  				t5 := (delta - math.Pi - asinrho) / omega
   265  				t6 := (delta - math.Pi + asinrho) / omega
   266  
   267  				if t1 < 0 {
   268  					t1 = 1.0e99
   269  				}
   270  				if t2 < 0 {
   271  					t2 = 1.0e99
   272  				}
   273  				if t3 < 0 {
   274  					t3 = 1.0e99
   275  				}
   276  				if t4 < 0 {
   277  					t4 = 1.0e99
   278  				}
   279  				if t5 < 0 {
   280  					t5 = 1.0e99
   281  				}
   282  				if t6 < 0 {
   283  					t6 = 1.0e99
   284  				}
   285  
   286  				tra := math.Min(t1, math.Min(t2, t3))
   287  				trb := math.Min(t4, math.Min(t5, t6))
   288  				tr = math.Min(tra, trb)
   289  				t = math.Min(tr, tz)
   290  			}
   291  
   292  			// 4. position in terms of x(t), y(t), z(t)
   293  			xt := xc + r*math.Sin(omega*t-phi0)
   294  			yt := yc + r*math.Cos(omega*t-phi0)
   295  			zt := z + pz*1.0e9/cLight/gammam*t
   296  			rt := math.Hypot(xt, yt)
   297  
   298  			if rt > 0.0 {
   299  				mother := cand
   300  				c := cand.Clone()
   301  				c.Pos = fmom.NewPxPyPzE(xt*1e3, yt*1e3, zt*1e3, cand.Pos.T()+t*cLight*1e3)
   302  				c.Add(mother)
   303  
   304  				output = append(output, *c)
   305  				if math.Abs(q) > 1e-9 {
   306  					pid := max(c.Pid, 0)
   307  					switch pid {
   308  					case 11:
   309  						eles = append(eles, *c)
   310  					case 13:
   311  						muons = append(muons, *c)
   312  					default:
   313  						hadrons = append(hadrons, *c)
   314  					}
   315  				}
   316  
   317  			}
   318  
   319  		}
   320  	}
   321  
   322  	sort.Sort(ByPt(output))
   323  	msg.Debugf(">>> output:     %v\n", len(output))
   324  	return err
   325  }
   326  
   327  func init() {
   328  	fwk.Register(reflect.TypeOf(Propagator{}),
   329  		func(typ, name string, mgr fwk.App) (fwk.Component, error) {
   330  			var err error
   331  			tsk := &Propagator{
   332  				TaskBase: fwk.NewTask(typ, name, mgr),
   333  			}
   334  			tsk.radius = 1.0
   335  			err = tsk.DeclProp("Radius", &tsk.radius)
   336  			if err != nil {
   337  				return nil, err
   338  			}
   339  			tsk.radius2 = tsk.radius * tsk.radius
   340  
   341  			tsk.halflen = 3.0
   342  			err = tsk.DeclProp("HalfLength", &tsk.halflen)
   343  			if err != nil {
   344  				return nil, err
   345  			}
   346  
   347  			tsk.bz = 0.0
   348  			err = tsk.DeclProp("Bz", &tsk.bz)
   349  			if err != nil {
   350  				return nil, err
   351  			}
   352  
   353  			tsk.input = "/fads/StableParticles"
   354  			err = tsk.DeclProp("Input", &tsk.input)
   355  			if err != nil {
   356  				return nil, err
   357  			}
   358  
   359  			tsk.output = "StableParticles"
   360  			err = tsk.DeclProp("Output", &tsk.output)
   361  			if err != nil {
   362  				return nil, err
   363  			}
   364  
   365  			tsk.hadrons = "ChargedHadrons"
   366  			err = tsk.DeclProp("ChargedHadrons", &tsk.hadrons)
   367  			if err != nil {
   368  				return nil, err
   369  			}
   370  
   371  			tsk.eles = "Electrons"
   372  			err = tsk.DeclProp("Electrons", &tsk.eles)
   373  			if err != nil {
   374  				return nil, err
   375  			}
   376  
   377  			tsk.muons = "Muons"
   378  			err = tsk.DeclProp("Muons", &tsk.muons)
   379  			if err != nil {
   380  				return nil, err
   381  			}
   382  
   383  			return tsk, err
   384  		},
   385  	)
   386  }