go-hep.org/x/hep@v0.38.1/fads/cmd/fads-app/main.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  // fads-app is a command that runs a simple ATLAS-like detector simulation,
     6  // modelled after the C++ Delphes ATLAS data-card.
     7  //
     8  // Example:
     9  //
    10  //	$> fads-app -help
    11  //	Usage: fads-app [options] <hepmc-input-file>
    12  //
    13  //	ex:
    14  //	 $ fads-app -l=INFO -evtmax=-1 ./testdata/hepmc.data
    15  //
    16  //	options:
    17  //	  -cpu-prof
    18  //	    	enable CPU profiling
    19  //	  -evtmax int
    20  //	    	number of events to process (default -1)
    21  //	  -l string
    22  //	    	log level (DEBUG|INFO|WARN|ERROR) (default "INFO")
    23  //	  -nprocs int
    24  //	    	number of concurrent events to process (default -1)
    25  //	  -o string
    26  //	    	name of output events file (default "data.rio")
    27  //	  -trace string
    28  //	    	path to file where to store traces
    29  //
    30  //	$> fads-app ./testdata/hepmc.data
    31  //	::: fads-app...
    32  //	app                  INFO workers done: 1/4
    33  //	app                  INFO workers done: 2/4
    34  //	app                  INFO workers done: 3/4
    35  //	app                  INFO workers done: 4/4
    36  //	app                  INFO cpu: 340.092148ms
    37  //	app                  INFO mem: alloc:          17963 kB
    38  //	app                  INFO mem: tot-alloc:      35590 kB
    39  //	app                  INFO mem: n-mallocs:      55399
    40  //	app                  INFO mem: n-frees:        54777
    41  //	app                  INFO mem: gc-pauses:          2 ms
    42  //	::: fads-app... [done] (time=343.533436ms)
    43  package main
    44  
    45  import (
    46  	"flag"
    47  	"fmt"
    48  	"log"
    49  	"math"
    50  	"os"
    51  	"reflect"
    52  	"runtime/pprof"
    53  	"runtime/trace"
    54  	"time"
    55  
    56  	"go-hep.org/x/hep/fads"
    57  	"go-hep.org/x/hep/fastjet"
    58  	"go-hep.org/x/hep/fwk"
    59  	"go-hep.org/x/hep/fwk/job"
    60  	"go-hep.org/x/hep/fwk/rio"
    61  	"go-hep.org/x/hep/hepmc"
    62  )
    63  
    64  var (
    65  	lvl     = flag.String("l", "INFO", "log level (DEBUG|INFO|WARN|ERROR)")
    66  	evtmax  = flag.Int("evtmax", -1, "number of events to process")
    67  	nprocs  = flag.Int("nprocs", -1, "number of concurrent events to process")
    68  	cpuprof = flag.Bool("cpu-prof", false, "enable CPU profiling")
    69  	ptrace  = flag.String("trace", "", "path to file where to store traces")
    70  	output  = flag.String("o", "data.rio", "name of output events file")
    71  
    72  	abs  = math.Abs
    73  	sqrt = math.Sqrt
    74  	pow  = math.Pow
    75  	tanh = math.Tanh
    76  )
    77  
    78  func main() {
    79  	flag.Usage = func() {
    80  		fmt.Fprintf(os.Stderr, `Usage: fads-app [options] <hepmc-input-file>
    81  
    82  ex:
    83   $ fads-app -l=INFO -evtmax=-1 ./testdata/hepmc.data
    84  
    85  options:
    86  `,
    87  		)
    88  		flag.PrintDefaults()
    89  	}
    90  
    91  	flag.Parse()
    92  
    93  	start := time.Now()
    94  
    95  	fmt.Printf("::: fads-app...\n")
    96  	if *cpuprof {
    97  		f, err := os.Create("cpu.prof")
    98  		if err != nil {
    99  			panic(err)
   100  		}
   101  		defer f.Close()
   102  		err = pprof.StartCPUProfile(f)
   103  		if err != nil {
   104  			log.Fatalf("could not start CPU profile: %+v", err)
   105  		}
   106  		defer pprof.StopCPUProfile()
   107  	}
   108  
   109  	if *ptrace != "" {
   110  		f, err := os.Create(*ptrace)
   111  		if err != nil {
   112  			log.Fatalf(
   113  				"error creating trace-file [%s]: %v\n",
   114  				*ptrace,
   115  				err,
   116  			)
   117  		}
   118  		defer f.Close()
   119  		err = trace.Start(f)
   120  		if err != nil {
   121  			log.Fatalf(
   122  				"error starting runtime/trace: %v\n",
   123  				err,
   124  			)
   125  		}
   126  		defer trace.Stop()
   127  	}
   128  
   129  	app := job.New(job.P{
   130  		"EvtMax":   int64(*evtmax),
   131  		"NProcs":   *nprocs,
   132  		"MsgLevel": job.MsgLevel(*lvl),
   133  	})
   134  
   135  	// propagate particles in cylinder
   136  	app.Create(job.C{
   137  		Type: "go-hep.org/x/hep/fads.Propagator",
   138  		Name: "pprop",
   139  		Props: job.P{
   140  			"Input":          "/fads/StableParticles",
   141  			"Output":         "/fads/pprop/StableParticles",
   142  			"ChargedHadrons": "/fads/pprop/ChargedHadrons",
   143  			"Electrons":      "/fads/pprop/Electrons",
   144  			"Muons":          "/fads/pprop/Muons",
   145  
   146  			// radius of the magnetic field coverage, in meters
   147  			"Radius": 1.15,
   148  			// half-length of the magnetic field coverage, in meters
   149  			"HalfLength": 3.51,
   150  			// magnetic field
   151  			"Bz": 2.0,
   152  		},
   153  	})
   154  
   155  	input := "testdata/hepmc.data"
   156  	//input := "testdata/full.hepmc.data"
   157  	if flag.NArg() > 0 {
   158  		input = flag.Arg(0)
   159  	}
   160  
   161  	// read HepMC data
   162  	app.Create(job.C{
   163  		Type: "go-hep.org/x/hep/fwk.InputStream",
   164  		Name: "hepmc-streamer",
   165  		Props: job.P{
   166  			"Ports": []fwk.Port{
   167  				{
   168  					Name: "/fads/McEvent",
   169  					Type: reflect.TypeOf(hepmc.Event{}),
   170  				},
   171  			},
   172  			"Streamer": &fads.HepMcStreamer{
   173  				Name: input,
   174  			},
   175  		},
   176  	})
   177  
   178  	// transform HepMC data into fads collection
   179  	app.Create(job.C{
   180  		Type: "go-hep.org/x/hep/fads.HepMcReader",
   181  		Name: "hepmcreader",
   182  		Props: job.P{
   183  			"Input": "/fads/McEvent",
   184  		},
   185  	})
   186  
   187  	// charged hadron tracking efficiency
   188  	app.Create(job.C{
   189  		Type: "go-hep.org/x/hep/fads.Efficiency",
   190  		Name: "charged-hadron-trk-eff",
   191  		Props: job.P{
   192  			"Input":  "/fads/pprop/ChargedHadrons",
   193  			"Output": "/fads/charged-hadron-trk-eff/ChargedHadrons",
   194  			"Eff": func(eta, pt float64) float64 {
   195  				switch {
   196  				case (pt <= 0.1):
   197  					return (0.00)
   198  				case (math.Abs(eta) <= 1.5) && (pt > 0.1 && pt <= 1.0):
   199  					return (0.70)
   200  				case (math.Abs(eta) <= 1.5) && (pt > 1.0):
   201  					return (0.95)
   202  				case (math.Abs(eta) > 1.5 && math.Abs(eta) <= 2.5) && (pt > 0.1 && pt <= 1.0):
   203  					return (0.60)
   204  				case (math.Abs(eta) > 1.5 && math.Abs(eta) <= 2.5) && (pt > 1.0):
   205  					return (0.85)
   206  				case (math.Abs(eta) > 2.5):
   207  					return (0.00)
   208  				}
   209  
   210  				return 0
   211  			},
   212  		},
   213  	})
   214  
   215  	// electron tracking efficiency
   216  	app.Create(job.C{
   217  		Type: "go-hep.org/x/hep/fads.Efficiency",
   218  		Name: "electron-trk-eff",
   219  		Props: job.P{
   220  			"Input":  "/fads/pprop/Electrons",
   221  			"Output": "/fads/electron-trk-eff/Electrons",
   222  			"Eff": func(eta, pt float64) float64 {
   223  				switch {
   224  				case pt <= 0.1:
   225  					return 0
   226  				case math.Abs(eta) <= 1.5 && (pt > 0.1 && pt <= 1.0):
   227  					return 0.70
   228  				case math.Abs(eta) <= 1.5 && (pt > 1.0 && pt <= 100):
   229  					return 0.95
   230  				case (math.Abs(eta) <= 1.5) && (pt > 100):
   231  					return 0.99
   232  				case (math.Abs(eta) > 1.5 && math.Abs(eta) <= 2.5) && (pt > 0.1 && pt <= 1.0):
   233  					return 0.50
   234  				case (math.Abs(eta) > 1.5 && math.Abs(eta) <= 2.5) && (pt > 1.0 && pt <= 100):
   235  					return 0.83
   236  				case (math.Abs(eta) > 1.5 && math.Abs(eta) <= 2.5) && (pt > 100):
   237  					return 0.90
   238  				case (math.Abs(eta) > 2.5):
   239  					return 0
   240  				}
   241  
   242  				return 0
   243  			},
   244  		},
   245  	})
   246  
   247  	// muon tracking efficiency
   248  	app.Create(job.C{
   249  		Type: "go-hep.org/x/hep/fads.Efficiency",
   250  		Name: "muon-trk-eff",
   251  		Props: job.P{
   252  			"Input":  "/fads/pprop/Muons",
   253  			"Output": "/fads/muon-trk-eff/Muons",
   254  			"Eff": func(eta, pt float64) float64 {
   255  				switch {
   256  				case (pt <= 0.1):
   257  					return 0.00
   258  				case (math.Abs(eta) <= 1.5) && (pt > 0.1 && pt <= 1.0):
   259  					return (0.75)
   260  				case (math.Abs(eta) <= 1.5) && (pt > 1.0):
   261  					return (0.99)
   262  				case (math.Abs(eta) > 1.5 && math.Abs(eta) <= 2.5) && (pt > 0.1 && pt <= 1.0):
   263  					return (0.70)
   264  				case (math.Abs(eta) > 1.5 && math.Abs(eta) <= 2.5) && (pt > 1.0):
   265  					return (0.98)
   266  				case (math.Abs(eta) > 2.5):
   267  					return (0.00)
   268  				}
   269  
   270  				return 0
   271  			},
   272  		},
   273  	})
   274  
   275  	// momentum resolution for charged tracks
   276  	app.Create(job.C{
   277  		Type: "go-hep.org/x/hep/fads.MomentumSmearing",
   278  		Name: "charged-hadron-mom-smearing",
   279  		Props: job.P{
   280  			"Input":  "/fads/charged-hadron-trk-eff/ChargedHadrons",
   281  			"Output": "/fads/charged-hadron-mom-smearing/ChargedHadrons",
   282  			"Resolution": func(eta, pt float64) float64 {
   283  				switch {
   284  				case (abs(eta) <= 1.5) && (pt > 0.1 && pt <= 1.0):
   285  					return 0.02
   286  				case (abs(eta) <= 1.5) && (pt > 1.0 && pt <= 1.0e1):
   287  					return (0.01)
   288  				case (abs(eta) <= 1.5) && (pt > 1.0e1 && pt <= 2.0e2):
   289  					return (0.03)
   290  				case (abs(eta) <= 1.5) && (pt > 2.0e2):
   291  					return (0.05)
   292  				case (abs(eta) > 1.5 && abs(eta) <= 2.5) && (pt > 0.1 && pt <= 1.0):
   293  					return (0.03)
   294  				case (abs(eta) > 1.5 && abs(eta) <= 2.5) && (pt > 1.0 && pt <= 1.0e1):
   295  					return (0.02)
   296  				case (abs(eta) > 1.5 && abs(eta) <= 2.5) && (pt > 1.0e1 && pt <= 2.0e2):
   297  					return (0.04)
   298  				case (abs(eta) > 1.5 && abs(eta) <= 2.5) && (pt > 2.0e2):
   299  					return (0.05)
   300  				}
   301  				return 0
   302  			},
   303  		},
   304  	})
   305  
   306  	// energy resolution for electrons
   307  	app.Create(job.C{
   308  		Type: "go-hep.org/x/hep/fads.EnergySmearing",
   309  		Name: "electron-ene-smearing",
   310  		Props: job.P{
   311  			"Input":  "/fads/electron-trk-eff/Electrons",
   312  			"Output": "/fads/electron-ene-smearing/Electrons",
   313  			"Resolution": func(eta, ene float64) float64 {
   314  				switch {
   315  				case (abs(eta) <= 2.5) && (ene > 0.1 && ene <= 2.5e1):
   316  					return (ene * 0.015)
   317  				case (abs(eta) <= 2.5) && (ene > 2.5e1):
   318  					return sqrt(pow(ene*0.005, 2) + ene*pow(0.05, 2) + pow(0.25, 2))
   319  				case (abs(eta) > 2.5 && abs(eta) <= 3.0):
   320  					return sqrt(pow(ene*0.005, 2) + ene*pow(0.05, 2) + pow(0.25, 2))
   321  				case (abs(eta) > 3.0 && abs(eta) <= 5.0):
   322  					return sqrt(pow(ene*0.107, 2) + ene*pow(2.08, 2))
   323  				}
   324  
   325  				return 0
   326  			},
   327  		},
   328  	})
   329  
   330  	// momentum resolution for muons
   331  	app.Create(job.C{
   332  		Type: "go-hep.org/x/hep/fads.MomentumSmearing",
   333  		Name: "muon-mom-smearing",
   334  		Props: job.P{
   335  			"Input":  "/fads/muon-trk-eff/Muons",
   336  			"Output": "/fads/muon-mom-smearing/Muons",
   337  			"Resolution": func(eta, pt float64) float64 {
   338  				switch {
   339  				case (abs(eta) <= 1.5) && (pt > 0.1 && pt <= 1.0):
   340  					return (0.03)
   341  				case (abs(eta) <= 1.5) && (pt > 1.0 && pt <= 5.0e1):
   342  					return (0.03)
   343  				case (abs(eta) <= 1.5) && (pt > 5.0e1 && pt <= 1.0e2):
   344  					return (0.04)
   345  				case (abs(eta) <= 1.5) && (pt > 1.0e2):
   346  					return (0.07)
   347  				case (abs(eta) > 1.5 && abs(eta) <= 2.5) && (pt > 0.1 && pt <= 1.0):
   348  					return (0.04)
   349  				case (abs(eta) > 1.5 && abs(eta) <= 2.5) && (pt > 1.0 && pt <= 5.0e1):
   350  					return (0.04)
   351  				case (abs(eta) > 1.5 && abs(eta) <= 2.5) && (pt > 5.0e1 && pt <= 1.0e2):
   352  					return (0.05)
   353  				case (abs(eta) > 1.5 && abs(eta) <= 2.5) && (pt > 1.0e2):
   354  					return (0.10)
   355  				}
   356  				return 0
   357  			},
   358  		},
   359  	})
   360  
   361  	// track merger
   362  	app.Create(job.C{
   363  		Type: "go-hep.org/x/hep/fads.Merger",
   364  		Name: "track-merger",
   365  		Props: job.P{
   366  			"Inputs": []string{
   367  				"/fads/charged-hadron-mom-smearing/ChargedHadrons",
   368  				"/fads/electron-ene-smearing/Electrons",
   369  				"/fads/muon-mom-smearing/Muons",
   370  			},
   371  			"Output":         "/fads/track-merger/tracks",
   372  			"MomentumOutput": "/fads/track-merger/momentum",
   373  			"EnergyOutput":   "/fads/track-merger/energy",
   374  		},
   375  	})
   376  
   377  	phi10 := make([]float64, 0, 37)
   378  	for i := -18; i <= 18; i++ {
   379  		phi10 = append(phi10, float64(i)*math.Pi/18.0)
   380  	}
   381  
   382  	phi20 := make([]float64, 0, 19)
   383  	for i := -9; i <= 9; i++ {
   384  		phi20 = append(phi20, float64(i)*math.Pi/9.0)
   385  	}
   386  
   387  	// calorimeter
   388  	app.Create(job.C{
   389  		Type: "go-hep.org/x/hep/fads.Calorimeter",
   390  		Name: "calo",
   391  		Props: job.P{
   392  			"Particles":   "/fads/pprop/StableParticles",
   393  			"Tracks":      "/fads/track-merger/tracks",
   394  			"Towers":      "/fads/calo/towers",
   395  			"Photons":     "/fads/calo/photons",
   396  			"EFlowTracks": "/fads/calo/eflowtracks",
   397  			"EFlowTowers": "/fads/calo/eflowtowers",
   398  
   399  			"EtaPhiBins": fads.NewEtaPhiGrid(
   400  				[]fads.EtaPhiBin{
   401  					// 10-degrees towers: 0 <= |eta| <= 3.2
   402  					{
   403  						EtaBins: []float64{
   404  							-3.2,
   405  							-2.5, -2.4, -2.3, -2.2, -2.1, -2.0,
   406  							-1.9, -1.8, -1.7, -1.6, -1.5, -1.4, -1.3, -1.2, -1.1, -1.0,
   407  							-0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, +0.0,
   408  							+0.1, +0.2, +0.3, +0.4, +0.5, +0.6, +0.7, +0.8, +0.9,
   409  							+1.0, +1.1, +1.2, +1.3, +1.4, +1.5, +1.6, +1.7, +1.8, +1.9,
   410  							+2.0, +2.1, +2.2, +2.3, +2.4, +2.5, +2.6,
   411  							+3.3,
   412  						},
   413  						PhiBins: phi10,
   414  					},
   415  
   416  					// 20-degrees towers: 2.8 <= |eta| <= 4.9
   417  					{
   418  						EtaBins: []float64{
   419  							-4.9, -4.7, -4.5, -4.3, -4.1,
   420  							-3.9, -3.7, -3.5, -3.3, -3.0,
   421  							-2.8, -2.6,
   422  							+2.8, +3.0, +3.2, +3.5, +3.7, +3.9,
   423  							+4.1, +4.3, +4.5, +4.7, +4.9,
   424  						},
   425  						PhiBins: phi20,
   426  					},
   427  				},
   428  			),
   429  
   430  			// default energy fractions: abs(pid) -> {ECal, HCal}
   431  			"EnergyFraction": map[int]fads.EneFrac{
   432  				0: {
   433  					ECal: 0,
   434  					HCal: 1,
   435  				},
   436  				// energy fractions for ele,gamma and pi0
   437  				11:  {ECal: 1, HCal: 0},
   438  				22:  {ECal: 1, HCal: 0},
   439  				111: {ECal: 1, HCal: 0},
   440  				// energy fractions for muons, neutrinos and neutralinos
   441  				12:      {ECal: 0.0, HCal: 0.0},
   442  				13:      {ECal: 0.0, HCal: 0.0},
   443  				14:      {ECal: 0.0, HCal: 0.0},
   444  				16:      {ECal: 0.0, HCal: 0.0},
   445  				1000022: {ECal: 0.0, HCal: 0.0},
   446  				1000023: {ECal: 0.0, HCal: 0.0},
   447  				1000025: {ECal: 0.0, HCal: 0.0},
   448  				1000035: {ECal: 0.0, HCal: 0.0},
   449  				1000045: {ECal: 0.0, HCal: 0.0},
   450  				// energy fractions for K0short and Lambda
   451  				310:  {ECal: 0.3, HCal: 0.7},
   452  				3122: {ECal: 0.3, HCal: 0.7},
   453  			},
   454  
   455  			// ecal resolution (eta and energy)
   456  			// http://arxiv.org/pdf/physics/0608012v1 jinst8_08_s08003
   457  			// http://villaolmo.mib.infn.it/ICATPP9th_2005/Calorimetry/Schram.p.pdf
   458  			// http://www.physics.utoronto.ca/~krieger/procs/ComoProceedings.pdf
   459  			"ECalResolution": func(eta, ene float64) float64 {
   460  				switch {
   461  				case (abs(eta) <= 3.2):
   462  					return sqrt(pow(ene, 2)*pow(0.0017, 2) + ene*pow(0.101, 2))
   463  
   464  				case (abs(eta) > 3.2 && abs(eta) <= 4.9):
   465  					return sqrt(pow(ene, 2)*pow(0.0350, 2) + ene*pow(0.285, 2))
   466  				}
   467  				return 0
   468  			},
   469  
   470  			// hcal resolution (eta and energy)
   471  			// http://arxiv.org/pdf/hep-ex/0004009v1
   472  			// http://villaolmo.mib.infn.it/ICATPP9th_2005/Calorimetry/Schram.p.pdf
   473  			"HCalResolution": func(eta, ene float64) float64 {
   474  				switch {
   475  				case (abs(eta) <= 1.7):
   476  					return sqrt(pow(ene, 2)*pow(0.0302, 2) + ene*pow(0.5205, 2) + pow(1.59, 2))
   477  				case (abs(eta) > 1.7 && abs(eta) <= 3.2):
   478  					return sqrt(pow(ene, 2)*pow(0.0500, 2) + ene*pow(0.706, 2))
   479  				case (abs(eta) > 3.2 && abs(eta) <= 4.9):
   480  					return sqrt(pow(ene, 2)*pow(0.9420, 2) + ene*pow(0.075, 2))
   481  				}
   482  				return 0
   483  			},
   484  		},
   485  	})
   486  
   487  	// eflow merger
   488  	app.Create(job.C{
   489  		Type: "go-hep.org/x/hep/fads.Merger",
   490  		Name: "eflow-merger",
   491  		Props: job.P{
   492  			"Inputs": []string{
   493  				"/fads/calo/eflowtracks",
   494  				"/fads/calo/eflowtowers",
   495  			},
   496  			"Output":         "/fads/eflow-merger/eflow",
   497  			"MomentumOutput": "/fads/eflow-merger/momentum",
   498  			"EnergyOutput":   "/fads/eflow-merger/energy",
   499  		},
   500  	})
   501  
   502  	// photon efficiency
   503  	app.Create(job.C{
   504  		Type: "go-hep.org/x/hep/fads.Efficiency",
   505  		Name: "photon-eff",
   506  		Props: job.P{
   507  			"Input":  "/fads/calo/photons",
   508  			"Output": "/fads/photon-eff/photons",
   509  			"Eff": func(eta, pt float64) float64 {
   510  				switch {
   511  				case (pt <= 10.0):
   512  					return 0.00
   513  				case (abs(eta) <= 1.5) && (pt > 10.0):
   514  					return 0.95
   515  				case (abs(eta) > 1.5 && abs(eta) <= 2.5) && (pt > 10.0):
   516  					return 0.85
   517  				case (abs(eta) > 2.5):
   518  					return 0.00
   519  				}
   520  
   521  				return 0
   522  			},
   523  		},
   524  	})
   525  
   526  	// photon isolation
   527  	app.Create(job.C{
   528  		Type: "go-hep.org/x/hep/fads.Isolation",
   529  		Name: "photon-iso",
   530  		Props: job.P{
   531  			"Candidates": "/fads/photon-eff/photons",
   532  			"Isolations": "/fads/eflow-merger/eflow",
   533  			"Output":     "/fads/photon-iso/photons",
   534  
   535  			"DeltaRMax":  0.5,
   536  			"PtMin":      0.5,
   537  			"PtRatioMax": 0.1,
   538  		},
   539  	})
   540  
   541  	// electron efficiency
   542  	app.Create(job.C{
   543  		Type: "go-hep.org/x/hep/fads.Efficiency",
   544  		Name: "electron-eff",
   545  		Props: job.P{
   546  			"Input":  "/fads/electron-ene-smearing/Electrons",
   547  			"Output": "/fads/electron-eff/electrons",
   548  			"Eff": func(eta, pt float64) float64 {
   549  				switch {
   550  				case (pt <= 10.0):
   551  					return (0.00)
   552  				case (abs(eta) <= 1.5) && (pt > 10.0):
   553  					return (0.95)
   554  				case (abs(eta) > 1.5 && abs(eta) <= 2.5) && (pt > 10.0):
   555  					return (0.85)
   556  				case (abs(eta) > 2.5):
   557  					return (0.00)
   558  				}
   559  				return 0
   560  			},
   561  		},
   562  	})
   563  
   564  	// electron isolation
   565  	app.Create(job.C{
   566  		Type: "go-hep.org/x/hep/fads.Isolation",
   567  		Name: "electron-iso",
   568  		Props: job.P{
   569  			"Candidates": "/fads/electron-eff/electrons",
   570  			"Isolations": "/fads/eflow-merger/eflow",
   571  			"Output":     "/fads/electron-iso/electrons",
   572  
   573  			"DeltaRMax":  0.5,
   574  			"PtMin":      0.5,
   575  			"PtRatioMax": 0.1,
   576  		},
   577  	})
   578  
   579  	// muon efficiency
   580  	app.Create(job.C{
   581  		Type: "go-hep.org/x/hep/fads.Efficiency",
   582  		Name: "muon-eff",
   583  		Props: job.P{
   584  			"Input":  "/fads/muon-mom-smearing/Muons",
   585  			"Output": "/fads/muon-eff/muons",
   586  			"Eff": func(eta, pt float64) float64 {
   587  				switch {
   588  				case (pt <= 10.0):
   589  					return (0.00)
   590  				case (abs(eta) <= 1.5) && (pt > 10.0):
   591  					return (0.95)
   592  				case (abs(eta) > 1.5 && abs(eta) <= 2.7) && (pt > 10.0):
   593  					return (0.85)
   594  				case (abs(eta) > 2.7):
   595  					return (0.00)
   596  				}
   597  				return 0
   598  			},
   599  		},
   600  	})
   601  
   602  	// muon isolation
   603  	app.Create(job.C{
   604  		Type: "go-hep.org/x/hep/fads.Isolation",
   605  		Name: "muon-iso",
   606  		Props: job.P{
   607  			"Candidates": "/fads/muon-eff/muons",
   608  			"Isolations": "/fads/eflow-merger/eflow",
   609  			"Output":     "/fads/muon-iso/muons",
   610  
   611  			"DeltaRMax":  0.5,
   612  			"PtMin":      0.5,
   613  			"PtRatioMax": 0.1,
   614  		},
   615  	})
   616  
   617  	// missing-et merger
   618  	app.Create(job.C{
   619  		Type: "go-hep.org/x/hep/fads.Merger",
   620  		Name: "missing-et",
   621  		Props: job.P{
   622  			"Inputs": []string{
   623  				"/fads/calo/eflowtracks",
   624  				"/fads/calo/eflowtowers",
   625  			},
   626  			"Output":         "/fads/missing-et",
   627  			"MomentumOutput": "/fads/missing-et/momentum",
   628  			"EnergyOutput":   "/fads/missing-et/energy",
   629  		},
   630  	})
   631  
   632  	// mc truth jet finder
   633  	app.Create(job.C{
   634  		Type: "go-hep.org/x/hep/fads.FastJetFinder",
   635  		Name: "mc-jet-finder",
   636  		Props: job.P{
   637  			"Input":  "/fads/StableParticles",
   638  			"Output": "/fads/mc-jet-finder/jets",
   639  			"Rho":    "/fads/mc-jet-finder/rho",
   640  
   641  			"JetAlgorithm": fastjet.AntiKtAlgorithm,
   642  			"ParameterR":   0.6,
   643  
   644  			"JetPtMin": 20.0,
   645  		},
   646  	})
   647  
   648  	// jet finder
   649  	app.Create(job.C{
   650  		Type: "go-hep.org/x/hep/fads.FastJetFinder",
   651  		Name: "fastjet-finder",
   652  		Props: job.P{
   653  			"Input":  "/fads/calo/towers",
   654  			"Output": "/fads/fastjet-finder/jets",
   655  			"Rho":    "/fads/fastjet-finder/rho",
   656  
   657  			"JetAlgorithm": fastjet.AntiKtAlgorithm,
   658  			"ParameterR":   0.6,
   659  
   660  			"JetPtMin": 20.0,
   661  		},
   662  	})
   663  
   664  	// jet energy scale
   665  	app.Create(job.C{
   666  		Type: "go-hep.org/x/hep/fads.EnergyScale",
   667  		Name: "jet-ene-scale",
   668  		Props: job.P{
   669  			"Input":  "/fads/fastjet-finder/jets",
   670  			"Output": "/fads/jet-ene-scale/jets",
   671  			"Scale":  func(eta, pt float64) float64 { return 1.08 },
   672  		},
   673  	})
   674  
   675  	// b-tagging
   676  	app.Create(job.C{
   677  		Type: "go-hep.org/x/hep/fads.BTagging",
   678  		Name: "btag",
   679  		Props: job.P{
   680  			"Partons": "/fads/Partons",
   681  			"Jets":    "/fads/jet-ene-scale/jets",
   682  			"Output":  "/fads/btag/jets",
   683  
   684  			"BitNumber":    uint(0),
   685  			"DeltaR":       0.5,
   686  			"PartonPtMin":  1.0,
   687  			"PartonEtaMax": 2.5,
   688  
   689  			// efficiency formula: [pdg-code] -> (pt,eta)
   690  			// pdg-code: the highest PDG code of a quark or gluon inside a DeltaR-cone
   691  			//           around the jet axis
   692  			//           gluon's pdg-code has the lowest priority.
   693  			"Eff": map[int]func(pt, eta float64) float64{
   694  				// default efficiency (mis-identification rate)
   695  				0: func(pt, eta float64) float64 { return 0.001 },
   696  
   697  				// efficiency for c-jets (mis-identification rate)
   698  				4: func(pt, eta float64) float64 {
   699  					switch {
   700  					case pt <= 15.0:
   701  						return (0.000)
   702  
   703  					case (abs(eta) <= 1.2) && (pt > 15.0):
   704  						return (0.2 * tanh(pt*0.03-0.4))
   705  
   706  					case (abs(eta) > 1.2 && abs(eta) <= 2.5) && (pt > 15.0):
   707  						return (0.1 * tanh(pt*0.03-0.4))
   708  
   709  					case (abs(eta) > 2.5):
   710  						return (0.000)
   711  					}
   712  					return 0
   713  				},
   714  
   715  				// efficiency for b-jets
   716  				5: func(pt, eta float64) float64 {
   717  					switch {
   718  					case (pt <= 15.0):
   719  						return (0.000)
   720  					case (abs(eta) <= 1.2) && (pt > 15.0):
   721  						return (0.5 * tanh(pt*0.03-0.4))
   722  					case (abs(eta) > 1.2 && abs(eta) <= 2.5) && (pt > 15.0):
   723  						return (0.4 * tanh(pt*0.03-0.4))
   724  					case (abs(eta) > 2.5):
   725  						return (0.000)
   726  					}
   727  					return 0
   728  				},
   729  			},
   730  		},
   731  	})
   732  
   733  	// tau-tagging
   734  	app.Create(job.C{
   735  		Type: "go-hep.org/x/hep/fads.TauTagging",
   736  		Name: "tau-tag",
   737  		Props: job.P{
   738  			"Particles": "/fads/AllParticles",
   739  			"Partons":   "/fads/Partons",
   740  			"Jets":      "/fads/btag/jets",
   741  			"Output":    "/fads/tau-tag/jets",
   742  
   743  			"DeltaR":    0.5,
   744  			"TauPtMin":  1.0,
   745  			"TauEtaMax": 2.5,
   746  
   747  			// efficiency formula: [pdg-code] -> (pt,eta)
   748  			"Eff": map[int]func(pt, eta float64) float64{
   749  				// default efficiency (mis-identification rate)
   750  				0: func(pt, eta float64) float64 { return 0.001 },
   751  
   752  				// efficiency for tau-jets
   753  				15: func(pt, eta float64) float64 { return 0.4 },
   754  			},
   755  		},
   756  	})
   757  
   758  	// find uniquely identified photons/electrons/taus/jets
   759  	app.Create(job.C{
   760  		Type: "go-hep.org/x/hep/fads.UniqueObjectFinder",
   761  		Name: "uobj-finder",
   762  		Props: job.P{
   763  			"Keys": []fads.ObjPair{
   764  				{
   765  					In:  "/fads/photon-iso/photons",
   766  					Out: "/fads/uobj-finder/photons",
   767  				},
   768  				{
   769  					In:  "/fads/electron-iso/electrons",
   770  					Out: "/fads/uobj-finder/electrons",
   771  				},
   772  				{
   773  					In:  "/fads/muon-iso/muons",
   774  					Out: "/fads/uobj-finder/muons",
   775  				},
   776  				{
   777  					In:  "/fads/tau-tag/jets",
   778  					Out: "/fads/uobj-finder/jets",
   779  				},
   780  			},
   781  		},
   782  	})
   783  
   784  	// scalar HT merger
   785  	app.Create(job.C{
   786  		Type: "go-hep.org/x/hep/fads.Merger",
   787  		Name: "scalar-ht",
   788  		Props: job.P{
   789  			"Inputs": []string{
   790  				"/fads/uobj-finder/jets",
   791  				"/fads/uobj-finder/electrons",
   792  				"/fads/uobj-finder/photons",
   793  				"/fads/uobj-finder/muons",
   794  			},
   795  			"Output":         "/fads/scalar-ht",
   796  			"MomentumOutput": "/fads/scalar-ht/momentum",
   797  			"EnergyOutput":   "/fads/scalar-ht/energy",
   798  		},
   799  	})
   800  
   801  	// output
   802  	app.Create(job.C{
   803  		Type: "go-hep.org/x/hep/fwk.OutputStream",
   804  		Name: "rio-output",
   805  		Props: job.P{
   806  			"Ports": []fwk.Port{
   807  				{
   808  					Name: "/fads/McEvent",
   809  					Type: reflect.TypeOf(hepmc.Event{}),
   810  				},
   811  				{
   812  					Name: "/fads/uobj-finder/jets",
   813  					Type: reflect.TypeOf([]fads.Candidate{}),
   814  				},
   815  				{
   816  					Name: "/fads/uobj-finder/electrons",
   817  					Type: reflect.TypeOf([]fads.Candidate{}),
   818  				},
   819  				{
   820  					Name: "/fads/uobj-finder/photons",
   821  					Type: reflect.TypeOf([]fads.Candidate{}),
   822  				},
   823  				{
   824  					Name: "/fads/uobj-finder/muons",
   825  					Type: reflect.TypeOf([]fads.Candidate{}),
   826  				},
   827  			},
   828  			"Streamer": &rio.OutputStreamer{
   829  				Name: *output,
   830  			},
   831  		},
   832  	})
   833  
   834  	app.Run()
   835  	fmt.Printf("::: fads-app... [done] (time=%v)\n", time.Since(start))
   836  }