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 }