View Javadoc

1   package org.lcsim.util;
2   
3   import hep.physics.particle.properties.ParticleType;
4   import hep.physics.vec.BasicHep3Vector;
5   import hep.physics.vec.BasicHepLorentzVector;
6   import hep.physics.vec.Hep3Vector;
7   import hep.physics.vec.HepLorentzVector;
8   
9   import java.util.ArrayList;
10  import java.util.Collection;
11  import java.util.HashMap;
12  import java.util.List;
13  import java.util.Map;
14  
15  import org.lcsim.detector.IDetectorElement;
16  import org.lcsim.event.EventHeader;
17  import org.lcsim.event.MCParticle;
18  import org.lcsim.event.SimCalorimeterHit;
19  import org.lcsim.event.SimTrackerHit;
20  import org.lcsim.event.EventHeader.LCMetaData;
21  import org.lcsim.event.base.BaseMCParticle;
22  import org.lcsim.event.base.BaseSimCalorimeterHit;
23  import org.lcsim.event.base.BaseSimTrackerHit;
24  import org.lcsim.lcio.LCIOConstants;
25  import org.lcsim.lcio.LCIOUtil;
26  
27  
28  /**
29   * Class with helper methods for event merging.
30   * 
31   * @author <a href="mailto:christian.grefe@cern.ch">Christian Grefe</a>
32   */
33  public class MergeEventTools {
34  
35  	/**
36  	 * Merge two events. The first event will contain both events afterwards.
37  	 * @param event the event which have the other event added
38  	 * @param mergeEvent the event to be merged with the first event
39  	 */
40  	static public void mergeEvents(EventHeader event, EventHeader mergeEvent) {
41  		mergeEvents(event, mergeEvent, new ArrayList<String>(), new HashMap<String, Map<Long,SimCalorimeterHit>>());
42  	}
43  	
44  	/**
45  	 * Merge two events. The first event will contain both events afterwards. Collections can be ignored.
46  	 * @param event the event which have the other event added
47  	 * @param mergeEvent the event to be merged with the first event
48  	 * @param ignoreCollections list of collection names that will not be merged
49  	 */
50  	static public void mergeEvents(EventHeader event, EventHeader mergeEvent, Collection<String> ignoreCollections) {
51  		mergeEvents(event, mergeEvent, ignoreCollections, new HashMap<String, Map<Long,SimCalorimeterHit>>());
52  	}
53  	
54  	/**
55  	 * Merge two events. The first event will contain both events afterwards. Collections can be ignored. A map holding maps of cell IDs to sim calorimeter hits is used to avoid
56  	 * that this information has to be obtained again if this method is called multiple times on the same event.
57  	 * @param event the event which have the other event added
58  	 * @param mergeEvent the event to be merged with the first event
59  	 * @param ignoreCollections list of collection names that will not be merged
60  	 * @param caloHitMaps map of collection names to maps of cell IDs to SimCalorimeterHits used to identify which cells require merging
61  	 */
62  	static public void mergeEvents(EventHeader event, EventHeader mergeEvent, Collection<String> ignoreCollections, Map<String, Map<Long, SimCalorimeterHit>> caloHitMaps) {
63  		Collection<LCMetaData> mergeMetaDataList = mergeEvent.getMetaData();
64  		// we need to have a single map of mc particles to their copies to ensure that all MCParticle references point to the same instance
65  		Map<MCParticle, MCParticle> mcParticleMap = new HashMap<MCParticle, MCParticle>();
66  		for (LCMetaData mergeMetaData : mergeMetaDataList) {
67  			String collectionName = mergeMetaData.getName();
68  			if (ignoreCollections.contains(collectionName)) {
69  				continue;
70  			}
71  			Class collectionType = mergeMetaData.getType();
72  			if (mergeEvent.get(collectionType, collectionName).isEmpty()) {
73  				continue;
74  			}
75  			LCMetaData metaData = event.getMetaData((List) event.get(collectionName));
76  			if (collectionType.isAssignableFrom(MCParticle.class)) {
77  				mergeMCParticleCollections(metaData, mergeMetaData, mcParticleMap);
78  			} else if (collectionType.isAssignableFrom(SimTrackerHit.class)) {
79  				mergeSimTrackerHitCollections(metaData, mergeMetaData, mcParticleMap);
80  			} else if (collectionType.isAssignableFrom(SimCalorimeterHit.class)) {
81  				if (!caloHitMaps.containsKey(collectionName)) {
82  					Map<Long, SimCalorimeterHit> hitMap = new HashMap<Long, SimCalorimeterHit>();
83  					for (SimCalorimeterHit hit : event.get(SimCalorimeterHit.class, collectionName)) {
84  						hitMap.put(hit.getCellID(), hit);
85  					}
86  					caloHitMaps.put(metaData.getName(), hitMap);
87  				}
88  				mergeSimCalorimeterHitCollections(metaData, mergeMetaData, mcParticleMap, caloHitMaps.get(collectionName));
89  			}
90  		}
91  	}
92  	
93  	/**
94  	 * Merge two MCParticle collections. The first one will contain both collections afterwards. All MCParticles are deep copied
95  	 * to allow for garbage collection.
96  	 * @param metaData meta data of the first collection
97  	 * @param mergeMetaData meta data of the second collection
98  	 * @param mcParticleMap map to store relation between the original MCParticles and their copies required to ensure consistency in relations to that MCParticle
99  	 */
100 	static public void mergeMCParticleCollections(LCMetaData metaData, LCMetaData mergeMetaData, Map<MCParticle, MCParticle> mcParticleMap) {
101 		String collectionName = mergeMetaData.getName();
102 		List<MCParticle> mcParticles = metaData.getEvent().get(MCParticle.class, collectionName);
103 		List<MCParticle> mergeMcParticles = mergeMetaData.getEvent().get(MCParticle.class, collectionName);
104 		for (MCParticle mcParticle : mergeMcParticles) {
105 			mcParticles.add(getMcParticleCopy(mcParticle, mcParticleMap));
106 		}
107 	}
108 	
109 	/**
110 	 * Merge two SimTrackerHit collections. The first one will contain both collections afterwards. All SimTrackerHits are deep copied
111 	 * to allow for garbage collection. Similarly, the MCParticles referenced by the SimTrackerHits are copied.
112 	 * @param metaData meta data of the first collection
113 	 * @param mergeMetaData meta data of the second collection
114 	 * @param mcParticleMap map to store relation between the original MCParticles and their copies required to ensure consistency in relations to that MCParticle
115 	 */
116 	static public void mergeSimTrackerHitCollections(LCMetaData metaData, LCMetaData mergeMetaData, Map<MCParticle, MCParticle> mcParticleMap) {
117 		String collectionName = mergeMetaData.getName();
118 		List<SimTrackerHit> trackerHits = metaData.getEvent().get(SimTrackerHit.class, collectionName);
119 		List<SimTrackerHit> mergeTrackerHits = mergeMetaData.getEvent().get(SimTrackerHit.class, collectionName);
120 		for (SimTrackerHit hit : mergeTrackerHits) {
121 			trackerHits.add(copySimTrackerHit(hit, metaData, mcParticleMap));
122 		}
123 	}
124 	
125 	/**
126 	 * Merge two SimCalorimeterHit collections. The first one will contain both collections afterwards. All SimCalorimeterHits are deep copied
127 	 * to allow for garbage collection. Similarly, the MCParticles referenced by the SimCalorimeterHits are copied.
128 	 * @param metaData meta data of the first collection
129 	 * @param mergeMetaData meta data of the second collection
130 	 * @param mcParticleMap map to store relation between the original MCParticles and their copies required to ensure consistency in relations to that MCParticle
131 	 */
132 	static public void mergeSimCalorimeterHitCollections(LCMetaData metaData, LCMetaData mergeMetaData, Map<MCParticle, MCParticle> mcParticleMap, Map<Long, SimCalorimeterHit> caloHitMap) {
133 		String collectionName = mergeMetaData.getName();
134 		List<SimCalorimeterHit> caloHits = metaData.getEvent().get(SimCalorimeterHit.class, collectionName);
135 		List<SimCalorimeterHit> mergeCaloHits = mergeMetaData.getEvent().get(SimCalorimeterHit.class, collectionName);
136 		for (SimCalorimeterHit caloHit : mergeCaloHits) {
137 			long cellID = caloHit.getCellID();
138 			if (caloHitMap.containsKey(cellID)) {
139 				SimCalorimeterHit hit = caloHitMap.get(cellID);
140 				caloHit = copySimCalorimeterHit(caloHit, metaData, mcParticleMap);
141 				SimCalorimeterHit mergedHit = mergeSimCalorimeterHits(hit, caloHit);
142 				caloHits.remove(hit);
143 				caloHitMap.put(cellID, mergedHit);
144 				caloHits.add(mergedHit);
145 			} else {
146 				SimCalorimeterHit caloHitCopy = copySimCalorimeterHit(caloHit, metaData, mcParticleMap); 
147 				caloHitMap.put(cellID, caloHitCopy);
148 				caloHits.add(caloHitCopy);
149 			}
150 		}
151 	}
152 
153 	/**
154 	 * Creates a deep copy of an MCParticle.
155 	 * @param mcParticle the particle to be copied
156 	 * @return the copied MCParticle
157 	 */
158 	static public MCParticle copyMcParticle(MCParticle mcParticle) {
159 		Hep3Vector origin = new BasicHep3Vector(mcParticle.getOriginX(), mcParticle.getOriginY(), mcParticle.getOriginZ());
160 		HepLorentzVector p = new BasicHepLorentzVector(mcParticle.getEnergy(), new double[] {mcParticle.getPX(), mcParticle.getPY(), mcParticle.getPZ()});
161 		ParticleType ptype = mcParticle.getType().getParticlePropertyProvider().get(mcParticle.getPDGID());
162 		int status = mcParticle.getGeneratorStatus();
163 		double time = mcParticle.getProductionTime();
164 		BaseMCParticle copyMcP = new BaseMCParticle(origin, p, ptype, status, time);
165 		// override the mass and charge from the particle type to prevent unknown particle exceptions
166 		copyMcP.setMass(mcParticle.getMass());
167 		copyMcP.setCharge(mcParticle.getCharge());
168 		copyMcP.setSimulatorStatus(mcParticle.getSimulatorStatus().getValue());
169 		return copyMcP;
170 	}
171 	
172 	/**
173 	 * Helper method to make a proper deep copy of an MCParticle and all its ancestors. A map between the original
174 	 * particle and can be used to ensure that all references to the original particle use the same copy.
175 	 * @param mcParticle the particle to be cpoied
176 	 * @param mcParticleMap map to store relation between the original MCParticles and their copies required to ensure consistency in relations to that MCParticle
177 	 * @return the copied MCParticle
178 	 */
179 	static public MCParticle getMcParticleCopy(MCParticle mcParticle, Map<MCParticle, MCParticle> mcParticleMap) {
180 		if (!mcParticleMap.containsKey(mcParticle)) {
181 			MCParticle mcParticleCopy = copyMcParticle(mcParticle);
182 			mcParticleMap.put(mcParticle, mcParticleCopy);
183 			for (MCParticle parent : mcParticle.getParents()) {
184 				MCParticle parentCopy = getMcParticleCopy(parent, mcParticleMap);
185 				((BaseMCParticle) parentCopy).addDaughter(mcParticleCopy);
186 			}
187 		}
188 		return mcParticleMap.get(mcParticle);
189 	}
190 	
191 	/**
192 	 * Creates a deep copy of a SimTrackerHit and assigns the given meta data. The corresponding MCParticles are also copied if they are not present in the map,
193 	 * otherwise the reference is updated to use the copy provided by the map.
194 	 * @param hit the original hit
195 	 * @param metaData the new meta data
196 	 * @param mcParticleMap map to store relation between the original MCParticles and their copies required to ensure consistency in relations to that MCParticle
197 	 * @return the copied hit
198 	 */
199 	static public SimTrackerHit copySimTrackerHit(SimTrackerHit hit, LCMetaData metaData, Map<MCParticle, MCParticle> mcParticleMap) {
200     	double[] position = new double[3];
201     	double[] momentum = new double[3];
202     	double[] hitp = hit.getPosition();
203     	double[] hitm = hit.getMomentum();
204     	for (int i = 0; i != 3; i++ ) {
205     		position[i] = hitp[i];
206     		momentum[i] = hitm[i];
207     	}
208     	double dEdx = hit.getdEdx();
209     	double pathLength = hit.getPathLength();
210     	double time = hit.getTime();
211     	int cellID = hit.getCellID();
212     	MCParticle mcParticle = getMcParticleCopy(hit.getMCParticle(), mcParticleMap);
213     	IDetectorElement de = hit.getDetectorElement();
214     	
215 		return new BaseSimTrackerHit(position, dEdx, momentum, pathLength, time, cellID, mcParticle, metaData, de);
216 	}
217 	
218 	/**
219 	 * Creates a deep copy of a SimCalorimeterHit and assigns the given meta data. The corresponding MCParticles are also copied if they are not present in the map,
220 	 * otherwise the reference is updated to use the copy provided by the map.
221 	 * @param hit the original hit
222 	 * @param metaData the new meta data
223 	 * @param mcParticleMap map to store relation between the original MCParticles and their copies required to ensure consistency in relations to that MCParticle
224 	 * @return the copied hit
225 	 */
226 	static public SimCalorimeterHit copySimCalorimeterHit(SimCalorimeterHit hit, LCMetaData metaData, Map<MCParticle, MCParticle> mcParticleMap) {
227 		long id = hit.getCellID();
228 		double rawEnergy = hit.getRawEnergy();
229 		double time = hit.getTime();
230 		int nMCP = hit.getMCParticleCount();
231 		Object[] mcparts = new Object[nMCP];
232 		float[] energies = new float[nMCP];
233 		float[] times = new float[nMCP];
234 		int[] pdgs = null;
235 		List<float[]> steps = new ArrayList<float[]>();
236 		boolean hasPDG = LCIOUtil.bitTest(metaData.getFlags(),LCIOConstants.CHBIT_PDG);
237 		if (hasPDG) {
238 			pdgs = new int[nMCP];
239 		}
240 		// fill arrays with values from hit
241 		for (int i = 0; i != nMCP; i++) {
242 			mcparts[i] = getMcParticleCopy(hit.getMCParticle(i), mcParticleMap);
243 			energies[i] = (float)hit.getContributedEnergy(i);
244 			times[i] = (float)hit.getContributedTime(i);
245 			if (hasPDG){
246 				pdgs[i] = hit.getPDG(i);
247 				steps.add(hit.getStepPosition(i));
248 			}
249 		}
250 		
251 		BaseSimCalorimeterHit copyHit = new BaseSimCalorimeterHit(id, rawEnergy, time, mcparts, energies, times, pdgs, steps, metaData);
252 		//copyHit.setDetectorElement(hit.getDetectorElement());
253 		copyHit.setMetaData(metaData);
254 		
255 		return copyHit;
256 	}
257 	
258 	/**
259 	 * Merges two SimCalorimeterHits that occupy the same cell
260 	 * @param hit the first hit to be merged
261 	 * @param mergeHit the second hit to be merged
262 	 * @return the combined calorimeter hit
263 	 */
264 	static public SimCalorimeterHit mergeSimCalorimeterHits(SimCalorimeterHit hit, SimCalorimeterHit mergeHit) {
265 		int nMcpHit = hit.getMCParticleCount();
266 		int nMcpMergeHit = mergeHit.getMCParticleCount();
267 		int nMcp = nMcpHit + nMcpMergeHit;
268 		// arrays of mc particle contributions to the hit
269 		Object[] mcpList = new Object[nMcp];
270 		float[] eneList = new float[nMcp];
271 		float[] timeList = new float[nMcp];
272 		int[] pdgList = null;
273 		LCMetaData metaData = hit.getMetaData();
274 		boolean hasPDG = LCIOUtil.bitTest(metaData.getFlags(),LCIOConstants.CHBIT_PDG);
275 		if (hasPDG) {
276 			pdgList = new int[nMcp];
277 		}
278 		List<float[]> steps = new ArrayList<float[]>();
279 		double rawEnergy = 0.;
280 		// fill arrays with values from hit
281 		for (int i = 0; i != nMcpHit; i++) {
282 			mcpList[i] = hit.getMCParticle(i);
283 			eneList[i] = (float) hit.getContributedEnergy(i);
284 			timeList[i] = (float) hit.getContributedTime(i);
285 			if (hasPDG) {
286 				pdgList[i] = hit.getPDG(i);
287 				steps.add(hit.getStepPosition(i));
288 			}
289 			rawEnergy += eneList[i];
290 		}
291 		// add values of overlay hit
292 		for (int i = 0; i != nMcpMergeHit; i++) {
293 			int j = nMcpHit + i;
294 	    	mcpList[j] = mergeHit.getMCParticle(i);
295 			eneList[j] = (float) mergeHit.getContributedEnergy(i);
296 			timeList[j] = (float) mergeHit.getContributedTime(i);
297 			if (hasPDG) {
298 				pdgList[j] = mergeHit.getPDG(i);
299 				steps.add(mergeHit.getStepPosition(i));
300 			}
301 			rawEnergy += eneList[j];
302 		}
303 		// need to set time to 0 so it is recalculated from the timeList
304 		SimCalorimeterHit mergedHit = new BaseSimCalorimeterHit(hit.getCellID(),
305 				rawEnergy, 0., mcpList, eneList, timeList, pdgList, steps, metaData);
306 		return mergedHit;
307 	}
308 	
309 }