LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
Solver.cxx
Go to the documentation of this file.
1 #include "Solver.h"
2 
3 #include <algorithm>
4 #include <cmath>
5 #include <cstdlib>
6 #include <set>
7 #include <iostream>
8 
9 template<class T> T sqr(T x){return x*x;}
10 
11 // ---------------------------------------------------------------------------
13  : fChannel(chan), fCharge(q), fPred(0)
14 {
15 }
16 
17 // ---------------------------------------------------------------------------
18 Neighbour::Neighbour(SpaceCharge* sc, double coupling)
19  : fSC(sc), fCoupling(coupling)
20 {
21 }
22 
23 // ---------------------------------------------------------------------------
24 SpaceCharge::SpaceCharge(double x, double y, double z,
25  CollectionWireHit* cwire,
26  InductionWireHit* wire1, InductionWireHit* wire2)
27  : fX(x), fY(y), fZ(z),
28  fCWire(cwire), fWire1(wire1), fWire2(wire2),
29  fPred(0),
30  fNeiPotential(0)
31 {
32 }
33 
34 // ---------------------------------------------------------------------------
35 void SpaceCharge::AddCharge(double dq)
36 {
37  fPred += dq;
38 
39  for(Neighbour& nei: fNeighbours)
40  nei.fSC->fNeiPotential += dq * nei.fCoupling;
41 
42  if(fWire1) fWire1->fPred += dq;
43  if(fWire2) fWire2->fPred += dq;
44 }
45 
46 // ---------------------------------------------------------------------------
48  const std::vector<SpaceCharge*>& cross)
49  : fChannel(chan), fCharge(q), fCrossings(cross)
50 {
51  const double p = q/cross.size();
52 
53  for(SpaceCharge* iwires: cross){
54  iwires->fPred += p;
55  if(iwires->fWire1) iwires->fWire1->fPred += p;
56  if(iwires->fWire2) iwires->fWire2->fPred += p;
57  }
58 }
59 
60 // ---------------------------------------------------------------------------
62 {
63  // Each SpaceCharge can only be in one collection wire, so we own them. But
64  // not SpaceCharge does not clean up its induction wires since they're
65  // shared.
66  for(SpaceCharge* sc: fCrossings) delete sc;
67 }
68 
69 // ---------------------------------------------------------------------------
70 double Metric(double q, double p)
71 {
72  return sqr(q-p);
73 }
74 
75 // ---------------------------------------------------------------------------
76 QuadExpr Metric(double q, QuadExpr p)
77 {
78  return sqr(q-p);
79 }
80 
81 // ---------------------------------------------------------------------------
82 double Metric(const std::vector<SpaceCharge*>& scs, double alpha)
83 {
84  double ret = 0;
85 
86  std::set<InductionWireHit*> iwires;
87  for(const SpaceCharge* sc: scs){
88  if(sc->fWire1) iwires.insert(sc->fWire1);
89  if(sc->fWire2) iwires.insert(sc->fWire2);
90 
91  if(alpha != 0){
92  ret -= alpha*sqr(sc->fPred);
93  // "Double-counting" of the two ends of the connection is
94  // intentional. Otherwise we'd have a half in the line above.
95  ret -= alpha * sc->fPred * sc->fNeiPotential;
96  }
97  }
98 
99  for(const InductionWireHit* iwire: iwires){
100  ret += Metric(iwire->fCharge, iwire->fPred);
101  }
102 
103  return ret;
104 }
105 
106 // ---------------------------------------------------------------------------
107 double Metric(const std::vector<CollectionWireHit*>& cwires, double alpha)
108 {
109  std::vector<SpaceCharge*> scs;
110  for(CollectionWireHit* cwire: cwires)
111  scs.insert(scs.end(), cwire->fCrossings.begin(), cwire->fCrossings.end());
112  return Metric(scs, alpha);
113 }
114 
115 // ---------------------------------------------------------------------------
116 QuadExpr Metric(const SpaceCharge* sci, const SpaceCharge* scj, double alpha)
117 {
118  QuadExpr ret = 0;
119 
120  // How much charge moves from scj to sci
121  QuadExpr x = QuadExpr::X();
122 
123  if(alpha != 0){
124  const double scip = sci->fPred;
125  const double scjp = scj->fPred;
126 
127  // Self energy. SpaceCharges are never the same object
128  ret -= alpha*sqr(scip + x);
129  ret -= alpha*sqr(scjp - x);
130 
131  // Interaction. We're only seeing one end of the double-ended connection
132  // here, so multiply by two.
133  ret -= 2 * alpha * (scip + x) * sci->fNeiPotential;
134  ret -= 2 * alpha * (scjp - x) * scj->fNeiPotential;
135 
136  // This miscounts if i and j are neighbours of each other
137  for(const Neighbour& n: sci->fNeighbours){
138  if(n.fSC == scj){
139  // If we detect that case, remove the erroneous terms
140  ret += 2 * alpha * (scip + x) * scjp * n.fCoupling;
141  ret += 2 * alpha * (scjp - x) * scip * n.fCoupling;
142 
143  // And replace with the correct interaction terms
144  ret -= 2 * alpha * (scip + x) * (scjp - x) * n.fCoupling;
145  break;
146  }
147  }
148  }
149 
150 
151  const InductionWireHit* iwire1 = sci->fWire1;
152  const InductionWireHit* jwire1 = scj->fWire1;
153  if(iwire1 && jwire1){
154  const double qi1 = iwire1->fCharge;
155  const double pi1 = iwire1->fPred;
156 
157  const double qj1 = jwire1->fCharge;
158  const double pj1 = jwire1->fPred;
159 
160  if(iwire1 == jwire1){
161  ret += Metric(qi1, pi1);
162  }
163  else{
164  ret += Metric(qi1, pi1 + x);
165  ret += Metric(qj1, pj1 - x);
166  }
167  }
168 
169  const InductionWireHit* iwire2 = sci->fWire2;
170  const InductionWireHit* jwire2 = scj->fWire2;
171  if(iwire2 && jwire2){
172  const double qi2 = iwire2->fCharge;
173  const double pi2 = iwire2->fPred;
174 
175  const double qj2 = jwire2->fCharge;
176  const double pj2 = jwire2->fPred;
177 
178  if(iwire2 == jwire2){
179  ret += Metric(qi2, pi2);
180  }
181  else{
182  ret += Metric(qi2, pi2 + x);
183  ret += Metric(qj2, pj2 - x);
184  }
185  }
186 
187  return ret;
188 }
189 
190 // ---------------------------------------------------------------------------
192  SpaceCharge* sci, SpaceCharge* scj,
193  double alpha)
194 {
195  const QuadExpr chisq = Metric(sci, scj, alpha);
196  const double chisq0 = chisq.Eval(0);
197 
198  // Find the minimum of a quadratic expression
199  double x = -chisq.Linear()/(2*chisq.Quadratic());
200 
201  // Don't allow either SpaceCharge to go negative
202  const double xmin = -sci->fPred;
203  const double xmax = scj->fPred;
204 
205  // Clamp to allowed range
206  x = std::min(xmax, x);
207  x = std::max(xmin, x);
208 
209  const double chisq_new = chisq.Eval(x);
210 
211  // Should try these too, because the function might be convex not concave, so
212  // d/dx=0 gives the max not the min, and the true min is at one extreme of
213  // the range.
214  const double chisq_p = chisq.Eval(xmax);
215  const double chisq_n = chisq.Eval(xmin);
216 
217  if(std::min(std::min(chisq_p, chisq_n), chisq_new) > chisq0+1){
218  for(double x = xmin; x < xmax; x += .01*(xmax-xmin)){
219  std::cout << x << " " << chisq.Eval(x) << std::endl;
220  }
221 
222  std::cout << chisq_new << " " << chisq0 << " " << chisq_p << " " << chisq_n << std::endl;
223  abort();
224  }
225 
226  if(std::min(chisq_n, chisq_p) < chisq_new){
227  if(chisq_n < chisq_p) return xmin;
228  return xmax;
229  }
230 
231  return x;
232 }
233 
234 // ---------------------------------------------------------------------------
235 void Iterate(CollectionWireHit* cwire, double alpha)
236 {
237  // Consider all pairs of crossings
238  const unsigned int N = cwire->fCrossings.size();
239 
240  for(unsigned int i = 0; i+1 < N; ++i){
241  SpaceCharge* sci = cwire->fCrossings[i];
242 
243  InductionWireHit* iwire1 = sci->fWire1;
244  InductionWireHit* iwire2 = sci->fWire2;
245 
246  for(unsigned int j = i+1; j < N; ++j){
247  SpaceCharge* scj = cwire->fCrossings[j];
248 
249  InductionWireHit* jwire1 = scj->fWire1;
250  InductionWireHit* jwire2 = scj->fWire2;
251 
252  // There can't be any cross-overs of U and V views like this
253  if(iwire1 == jwire2 || iwire2 == jwire1) abort();
254 
255  // Driving all the same wires, no move can have any effect
256  if(iwire1 == jwire1 && iwire2 == jwire2) continue;
257 
258  const double x = SolvePair(cwire, sci, scj, alpha);
259 
260  if(x == 0) continue;
261 
262  // Actually make the update
263  sci->AddCharge(+x);
264  scj->AddCharge(-x);
265  } // end for j
266  } // end for i
267 }
268 
269 // ---------------------------------------------------------------------------
270 void Iterate(const std::vector<CollectionWireHit*>& cwires,
271  double alpha)
272 {
273  for(CollectionWireHit* cwire: cwires) Iterate(cwire, alpha);
274 }
Float_t x
Definition: compare.C:6
void AddCharge(double dq)
Definition: Solver.cxx:35
double fCharge
Definition: Solver.h:25
double fPred
Definition: Solver.h:27
double Linear() const
Definition: QuadExpr.h:16
Float_t y
Definition: compare.C:6
Double_t z
Definition: plot.C:279
double fCoupling
Definition: Solver.h:37
std::vector< Neighbour > fNeighbours
Definition: Solver.h:56
SpaceCharge(double x, double y, double z, CollectionWireHit *cwire, InductionWireHit *wire1, InductionWireHit *wire2)
Definition: Solver.cxx:24
double fNeiPotential
Neighbour-induced potential.
Definition: Solver.h:59
Int_t max
Definition: plot.C:27
void Iterate(CollectionWireHit *cwire, double alpha)
Definition: Solver.cxx:235
std::vector< SpaceCharge * > fCrossings
Definition: Solver.h:73
double SolvePair(CollectionWireHit *cwire, SpaceCharge *sci, SpaceCharge *scj, double alpha)
Definition: Solver.cxx:191
InductionWireHit * fWire2
Definition: Solver.h:54
static QuadExpr X()
Definition: QuadExpr.cxx:6
InductionWireHit * fWire1
Definition: Solver.h:54
double Metric(double q, double p)
Definition: Solver.cxx:70
Neighbour(SpaceCharge *sc, double coupling)
Definition: Solver.cxx:18
InductionWireHit(int chan, double q)
Definition: Solver.cxx:12
T sqr(T x)
Definition: Solver.cxx:9
Int_t min
Definition: plot.C:26
double Eval(double x) const
Definition: QuadExpr.cxx:67
Char_t n[5]
Vector cross(Vector const &a, Vector const &b)
Return cross product of two vectors.
CollectionWireHit(int chan, double q, const std::vector< SpaceCharge * > &cross)
Definition: Solver.cxx:47
double fPred
Definition: Solver.h:58
double Quadratic() const
Definition: QuadExpr.h:15
SpaceCharge * fSC
Definition: Solver.h:36