go-hep.org/x/hep@v0.38.1/fmom/ops_test.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 fmom
     6  
     7  import (
     8  	"reflect"
     9  	"testing"
    10  
    11  	"gonum.org/v1/gonum/floats/scalar"
    12  	"gonum.org/v1/gonum/spatial/r3"
    13  )
    14  
    15  func p3equal(p1, p2 r3.Vec, epsilon float64) bool {
    16  	if cmpeq(p1.X, p2.X, epsilon) &&
    17  		cmpeq(p1.Y, p2.Y, epsilon) &&
    18  		cmpeq(p1.Z, p2.Z, epsilon) {
    19  		return true
    20  	}
    21  	return false
    22  }
    23  
    24  func newPxPyPzE(p4 PxPyPzE) P4 {
    25  	return &p4
    26  }
    27  
    28  func newEEtaPhiM(p4 PxPyPzE) P4 {
    29  	var pp EEtaPhiM
    30  	pp.Set(&p4)
    31  	return &pp
    32  }
    33  
    34  func newEtEtaPhiM(p4 PxPyPzE) P4 {
    35  	var pp EtEtaPhiM
    36  	pp.Set(&p4)
    37  	return &pp
    38  }
    39  
    40  func newPtEtaPhiM(p4 PxPyPzE) P4 {
    41  	var pp PtEtaPhiM
    42  	pp.Set(&p4)
    43  	return &pp
    44  }
    45  
    46  func newIPtCotThPhiM(p4 PxPyPzE) P4 {
    47  	var pp IPtCotThPhiM
    48  	pp.Set(&p4)
    49  	return &pp
    50  }
    51  
    52  func deepEqual(p1, p2 P4) bool {
    53  	return Equal(p1, p2)
    54  }
    55  
    56  func TestAdd(t *testing.T) {
    57  	for _, tc := range []struct {
    58  		p1   P4
    59  		p2   P4
    60  		want P4
    61  	}{
    62  		{
    63  			p1:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
    64  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
    65  			want: newPxPyPzE(NewPxPyPzE(20, 20, 20, 40)),
    66  		},
    67  		{
    68  			p1:   newEEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
    69  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
    70  			want: newEEtaPhiM(NewPxPyPzE(20, 20, 20, 40)),
    71  		},
    72  		{
    73  			p1:   newEtEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
    74  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
    75  			want: newEtEtaPhiM(NewPxPyPzE(20, 20, 20, 40)),
    76  		},
    77  		{
    78  			p1:   newPtEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
    79  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
    80  			want: newPtEtaPhiM(NewPxPyPzE(20, 20, 20, 40)),
    81  		},
    82  		{
    83  			p1:   newIPtCotThPhiM(NewPxPyPzE(10, 10, 10, 20)),
    84  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
    85  			want: newIPtCotThPhiM(NewPxPyPzE(20, 20, 20, 40)),
    86  		},
    87  	} {
    88  		p1 := tc.p1.Clone()
    89  		p2 := tc.p2.Clone()
    90  
    91  		got := Add(p1, p2)
    92  
    93  		if !deepEqual(got, tc.want) {
    94  			t.Fatalf("got= %#v\nwant=%#v", got, tc.want)
    95  		}
    96  		if !reflect.DeepEqual(p1, tc.p1) {
    97  			t.Fatalf("add modified p1:\ngot= %#v\nwant=%#v", p1, tc.p1)
    98  		}
    99  		if !reflect.DeepEqual(p2, tc.p2) {
   100  			t.Fatalf("add modified p2:\ngot= %#v\nwant=%#v", p2, tc.p2)
   101  		}
   102  	}
   103  }
   104  
   105  func TestIAdd(t *testing.T) {
   106  	for _, tc := range []struct {
   107  		p1   P4
   108  		p2   P4
   109  		want P4
   110  	}{
   111  		{
   112  			p1:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   113  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   114  			want: newPxPyPzE(NewPxPyPzE(20, 20, 20, 40)),
   115  		},
   116  		{
   117  			p1:   newEEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
   118  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   119  			want: newEEtaPhiM(NewPxPyPzE(20, 20, 20, 40)),
   120  		},
   121  		{
   122  			p1:   newEtEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
   123  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   124  			want: newEtEtaPhiM(NewPxPyPzE(20, 20, 20, 40)),
   125  		},
   126  		{
   127  			p1:   newPtEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
   128  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   129  			want: newPtEtaPhiM(NewPxPyPzE(20, 20, 20, 40)),
   130  		},
   131  		{
   132  			p1:   newIPtCotThPhiM(NewPxPyPzE(10, 10, 10, 20)),
   133  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   134  			want: newIPtCotThPhiM(NewPxPyPzE(20, 20, 20, 40)),
   135  		},
   136  	} {
   137  		p1 := tc.p1.Clone()
   138  		p2 := tc.p2.Clone()
   139  
   140  		got := IAdd(p1, p2)
   141  
   142  		if !deepEqual(got, tc.want) {
   143  			t.Fatalf("got= %#v\nwant=%#v", got, tc.want)
   144  		}
   145  
   146  		if !reflect.DeepEqual(got, p1) {
   147  			t.Fatalf("fmom.IAdd did not modify p1 in-place:\ngot= %#v\nwant=%#v", got, p1)
   148  		}
   149  		if !reflect.DeepEqual(p2, tc.p2) {
   150  			t.Fatalf("fmom.IAdd modified p2:\ngot= %#v\nwant=%#v", p2, tc.p2)
   151  		}
   152  	}
   153  }
   154  
   155  func TestEqual(t *testing.T) {
   156  	for _, tc := range []struct {
   157  		p1   P4
   158  		p2   P4
   159  		want bool
   160  	}{
   161  		{
   162  			p1:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   163  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   164  			want: true,
   165  		},
   166  		{
   167  			p1:   newEEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
   168  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   169  			want: true,
   170  		},
   171  		{
   172  			p1:   newEtEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
   173  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   174  			want: true,
   175  		},
   176  		{
   177  			p1:   newPtEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
   178  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   179  			want: true,
   180  		},
   181  		{
   182  			p1:   newIPtCotThPhiM(NewPxPyPzE(10, 10, 10, 20)),
   183  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   184  			want: true,
   185  		},
   186  
   187  		{
   188  			p1:   newPxPyPzE(NewPxPyPzE(10+1e-14, 10, 10, 20)),
   189  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   190  			want: false,
   191  		},
   192  		{
   193  			p1:   newPxPyPzE(NewPxPyPzE(10, 10+1e-14, 10, 20)),
   194  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   195  			want: false,
   196  		},
   197  		{
   198  			p1:   newPxPyPzE(NewPxPyPzE(10, 10, 10+1e-14, 20)),
   199  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   200  			want: false,
   201  		},
   202  		{
   203  			p1:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20+1e-14)),
   204  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   205  			want: false,
   206  		},
   207  	} {
   208  		{
   209  			got := deepEqual(tc.p1, tc.p2)
   210  			if got != tc.want {
   211  				t.Fatalf("got= %#v\nwant=%#v\np1=%#v\np2=%#v\n", got, tc.want, tc.p1, tc.p2)
   212  			}
   213  		}
   214  		got := Equal(tc.p1, tc.p2)
   215  		if got != tc.want {
   216  			t.Fatalf("got= %#v\nwant=%#v\np1=%#v\np2=%#v\n", got, tc.want, tc.p1, tc.p2)
   217  		}
   218  	}
   219  }
   220  
   221  func TestScale(t *testing.T) {
   222  	for _, tc := range []struct {
   223  		p    P4
   224  		a    float64
   225  		want P4
   226  	}{
   227  		{
   228  			p:    newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   229  			a:    1,
   230  			want: newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   231  		},
   232  
   233  		{
   234  			p:    newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   235  			a:    0,
   236  			want: newPxPyPzE(NewPxPyPzE(0, 0, 0, 0)),
   237  		},
   238  
   239  		{
   240  			p:    newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   241  			a:    -1,
   242  			want: newPxPyPzE(NewPxPyPzE(-10, -10, -10, -20)),
   243  		},
   244  
   245  		{
   246  			p:    newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   247  			a:    2,
   248  			want: newPxPyPzE(NewPxPyPzE(20, 20, 20, 40)),
   249  		},
   250  
   251  		{
   252  			p:    newEEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
   253  			a:    2,
   254  			want: newEEtaPhiM(NewPxPyPzE(20, 20, 20, 40)),
   255  		},
   256  		{
   257  			p:    newEtEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
   258  			a:    2,
   259  			want: newEtEtaPhiM(NewPxPyPzE(20, 20, 20, 40)),
   260  		},
   261  		{
   262  			p:    newPtEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
   263  			a:    2,
   264  			want: newPtEtaPhiM(NewPxPyPzE(20, 20, 20, 40)),
   265  		},
   266  		{
   267  			p:    newIPtCotThPhiM(NewPxPyPzE(10, 10, 10, 20)),
   268  			a:    2,
   269  			want: newIPtCotThPhiM(NewPxPyPzE(20, 20, 20, 40)),
   270  		},
   271  	} {
   272  		p := tc.p.Clone()
   273  
   274  		got := Scale(tc.a, p)
   275  
   276  		if !deepEqual(got, tc.want) {
   277  			t.Fatalf("got= %#v\nwant=%#v", got, tc.want)
   278  		}
   279  		if !reflect.DeepEqual(p, tc.p) {
   280  			t.Fatalf("add modified p:\np=%#v (ref)\np=%#v (new)", tc.p, p)
   281  		}
   282  	}
   283  }
   284  
   285  func TestInvMass(t *testing.T) {
   286  	for _, tc := range []struct {
   287  		p1   P4
   288  		p2   P4
   289  		want float64
   290  	}{
   291  		{
   292  			p1:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   293  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   294  			want: newPxPyPzE(NewPxPyPzE(20, 20, 20, 40)).M(),
   295  		},
   296  		{
   297  			p1:   newEEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
   298  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   299  			want: newEEtaPhiM(NewPxPyPzE(20, 20, 20, 40)).M(),
   300  		},
   301  		{
   302  			p1:   newEtEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
   303  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   304  			want: newEtEtaPhiM(NewPxPyPzE(20, 20, 20, 40)).M(),
   305  		},
   306  		{
   307  			p1:   newPtEtaPhiM(NewPxPyPzE(10, 10, 10, 20)),
   308  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   309  			want: newPtEtaPhiM(NewPxPyPzE(20, 20, 20, 40)).M(),
   310  		},
   311  		{
   312  			p1:   newIPtCotThPhiM(NewPxPyPzE(10, 10, 10, 20)),
   313  			p2:   newPxPyPzE(NewPxPyPzE(10, 10, 10, 20)),
   314  			want: newIPtCotThPhiM(NewPxPyPzE(20, 20, 20, 40)).M(),
   315  		},
   316  	} {
   317  		p1 := tc.p1.Clone()
   318  		p2 := tc.p2.Clone()
   319  
   320  		got := InvMass(p1, p2)
   321  
   322  		if !scalar.EqualWithinULP(got, tc.want, 2) {
   323  			t.Fatalf("got= %#v\nwant=%#v", got, tc.want)
   324  		}
   325  
   326  		if !reflect.DeepEqual(tc.p1, p1) {
   327  			t.Fatalf("fmom.InvMass modified p1 in-place:\ngot: %#v\nwant:%#v", p1, tc.p1)
   328  		}
   329  		if !reflect.DeepEqual(tc.p2, p2) {
   330  			t.Fatalf("fmom.InvMass modified p2 in-place:\ngot: %#v\nwant:%#v", p2, tc.p2)
   331  		}
   332  	}
   333  }
   334  
   335  func TestBoost(t *testing.T) {
   336  	var (
   337  		p1      = NewPxPyPzE(1, 2, 3, 4)
   338  		boost   = BoostOf(&p1)
   339  		p1RF    = Boost(&p1, r3.Vec{X: -boost.X, Y: -boost.Y, Z: -boost.Z})
   340  		boostRF = BoostOf(p1RF)
   341  		zero    r3.Vec
   342  	)
   343  
   344  	if !p3equal(boostRF, zero, 1e-14) {
   345  		t.Fatalf("invalid boost: got=%v, want=%v", boostRF, zero)
   346  	}
   347  
   348  	if got, want := Boost(&p1, r3.Vec{}), &p1; !reflect.DeepEqual(got, want) {
   349  		t.Fatalf("invalid zero-boost: got=%v, want=%v", got, want)
   350  	}
   351  }
   352  
   353  func TestBoostOf(t *testing.T) {
   354  	for _, tc := range []struct {
   355  		p      P4
   356  		v      r3.Vec
   357  		panics string
   358  	}{
   359  		{
   360  			p: newPxPyPzE(NewPxPyPzE(1, 2, 4, 10)),
   361  			v: r3.Vec{X: 0.1, Y: 0.2, Z: 0.4},
   362  		},
   363  		{
   364  			p: newPxPyPzE(NewPxPyPzE(1, 2, 4, -10)),
   365  			v: r3.Vec{X: -0.1, Y: -0.2, Z: -0.4},
   366  		},
   367  		{
   368  			p:      newPxPyPzE(NewPxPyPzE(1, 2, 3, 1)),
   369  			v:      r3.Vec{},
   370  			panics: "fmom: non-timelike four-vector",
   371  		},
   372  		{
   373  			p:      newPxPyPzE(NewPxPyPzE(1, 2, 3, 0)),
   374  			v:      r3.Vec{},
   375  			panics: "fmom: zero-energy four-vector",
   376  		},
   377  		{
   378  			p: newPxPyPzE(NewPxPyPzE(0, 0, 0, 0)),
   379  			v: r3.Vec{},
   380  		},
   381  	} {
   382  		t.Run("", func(t *testing.T) {
   383  			if tc.panics != "" {
   384  				defer func() {
   385  					e := recover()
   386  					if e == nil {
   387  						t.Fatalf("expected a panic: got=%v, want=%v", e, tc.panics)
   388  					}
   389  					if got, want := e.(string), tc.panics; got != want {
   390  						t.Fatalf("invalid panic message:\ngot= %v\nwant=%v", got, want)
   391  					}
   392  				}()
   393  			}
   394  
   395  			got := BoostOf(tc.p)
   396  			if got, want := got, tc.v; got != want {
   397  				t.Fatalf("invalid boost vector:\ngot= %v\nwant=%v", got, want)
   398  			}
   399  		})
   400  	}
   401  }
   402  
   403  func TestVecOf(t *testing.T) {
   404  	for _, tc := range []struct {
   405  		p    P4
   406  		want r3.Vec
   407  	}{
   408  		{
   409  			p:    newPxPyPzE(NewPxPyPzE(0, 10, 20, 30)),
   410  			want: r3.Vec{X: 0, Y: 10, Z: 20},
   411  		},
   412  
   413  		{
   414  			p:    newPxPyPzE(NewPxPyPzE(10, 0, 20, 30)),
   415  			want: r3.Vec{X: 10, Y: 0, Z: 20},
   416  		},
   417  
   418  		{
   419  			p:    newPxPyPzE(NewPxPyPzE(10, 20, 0, 30)),
   420  			want: r3.Vec{X: 10, Y: 20, Z: 0},
   421  		},
   422  	} {
   423  		got := VecOf(tc.p)
   424  
   425  		if got != tc.want {
   426  			t.Fatalf("invalid spatial components for %#v: got= %#v\nwant=%#v",
   427  				tc.p, got, tc.want)
   428  		}
   429  	}
   430  }