LArSoft  v08_35_01
Liquid Argon Software toolkit - http://larsoft.org/
EMShower_module.cc
Go to the documentation of this file.
1 // Class: EMShower
3 // Module Type: producer
4 // File: EMShower_module.cc
5 // Author: Mike Wallbank (m.wallbank@sheffield.ac.uk), September 2015
6 //
7 // Module to make EM showers.
8 // Takes the output from cluster finding and track finding and combines
9 // information to make complete 3D shower.
10 //
11 // See DUNE-DocDB 1369 (public) for a detailed description.
13 
14 // Framework includes:
17 #include "fhiclcpp/ParameterSet.h"
25 
26 // LArSoft includes
40 
41 // ROOT includes
42 #include "RtypesCore.h"
43 #include "TVector3.h"
44 
45 // C++ STL includes
46 #include <algorithm>
47 #include <array>
48 #include <float.h>
49 #include <iostream>
50 #include <map>
51 #include <math.h>
52 #include <memory>
53 #include <stddef.h>
54 #include <string>
55 #include <vector>
56 
57 
58 namespace shower {
59  class EMShower;
60 }
61 
63 public:
64 
65  EMShower(fhicl::ParameterSet const& pset);
66 
67 private:
68  void produce(art::Event& evt);
69 
70 
79 
81  detinfo::DetectorProperties const* fDetProp = lar::providerFrom<detinfo::DetectorPropertiesService>();
82 
83  int fShower;
84  int fPlane;
85 
86  int fDebug;
87 
88 };
89 
91  EDProducer{pset},
92  fEMShowerAlg(pset.get<fhicl::ParameterSet>("EMShowerAlg"))
93 {
94  fHitsModuleLabel = pset.get<art::InputTag>("HitsModuleLabel");
95  fClusterModuleLabel = pset.get<art::InputTag>("ClusterModuleLabel");
96  fTrackModuleLabel = pset.get<art::InputTag>("TrackModuleLabel");
97  fPFParticleModuleLabel = pset.get<art::InputTag>("PFParticleModuleLabel","");
98  fVertexModuleLabel = pset.get<art::InputTag>("VertexModuleLabel","");
99  fCNNEMModuleLabel = pset.get<art::InputTag>("CNNEMModuleLabel","");
100  fFindBadPlanes = pset.get<bool> ("FindBadPlanes");
101  fSaveNonCompleteShowers = pset.get<bool> ("SaveNonCompleteShowers");
102  fMakeSpacePoints = pset.get<bool> ("MakeSpacePoints");
103  fUseCNNtoIDEMPFP = pset.get<bool> ("UseCNNtoIDEMPFP");
104  fUseCNNtoIDEMHit = pset.get<bool> ("UseCNNtoIDEMHit");
105  fMinTrackLikeScore = pset.get<double> ("MinTrackLikeScore");
106  fShower = pset.get<int>("Shower",-1);
107  fPlane = pset.get<int>("Plane",-1);
108  fDebug = pset.get<int>("Debug",0);
110 
111  produces<std::vector<recob::Shower> >();
112  produces<std::vector<recob::SpacePoint> >();
113  produces<art::Assns<recob::Shower, recob::Hit> >();
114  produces<art::Assns<recob::Shower, recob::Cluster> >();
115  produces<art::Assns<recob::Shower, recob::Track> >();
116  produces<art::Assns<recob::Shower, recob::SpacePoint> >();
117  produces<art::Assns<recob::SpacePoint, recob::Hit> >();
118 }
119 
121 
122  // Output -- showers and associations with hits and clusters
123  std::unique_ptr<std::vector<recob::Shower> > showers(new std::vector<recob::Shower>);
124  std::unique_ptr<std::vector<recob::SpacePoint> > spacePoints(new std::vector<recob::SpacePoint>);
125  std::unique_ptr<art::Assns<recob::Shower, recob::Cluster> > clusterAssociations(new art::Assns<recob::Shower, recob::Cluster>);
126  std::unique_ptr<art::Assns<recob::Shower, recob::Hit> > hitShowerAssociations(new art::Assns<recob::Shower, recob::Hit>);
127  std::unique_ptr<art::Assns<recob::Shower, recob::Track> > trackAssociations(new art::Assns<recob::Shower, recob::Track>);
128  std::unique_ptr<art::Assns<recob::Shower, recob::SpacePoint> > spShowerAssociations(new art::Assns<recob::Shower, recob::SpacePoint>);
129  std::unique_ptr<art::Assns<recob::SpacePoint, recob::Hit> > hitSpAssociations(new art::Assns<recob::SpacePoint, recob::Hit>);
130 
131  // Event has hits, tracks and clusters found already
132 
133  // Hits
135  std::vector<art::Ptr<recob::Hit> > hits;
136  if (evt.getByLabel(fHitsModuleLabel,hitHandle))
137  art::fill_ptr_vector(hits, hitHandle);
138 
139  // Tracks
141  std::vector<art::Ptr<recob::Track> > tracks;
142  if (evt.getByLabel(fTrackModuleLabel,trackHandle))
143  art::fill_ptr_vector(tracks, trackHandle);
144 
145  // Clusters
147  std::vector<art::Ptr<recob::Cluster> > clusters;
148  if (evt.getByLabel(fClusterModuleLabel,clusterHandle))
149  art::fill_ptr_vector(clusters, clusterHandle);
150 
151  // PFParticles
153  std::vector<art::Ptr<recob::PFParticle> > pfps;
154  if (evt.getByLabel(fPFParticleModuleLabel, pfpHandle))
155  art::fill_ptr_vector(pfps, pfpHandle);
156 
157  // PFParticles
159  std::vector<art::Ptr<recob::Vertex> > vertices;
160  if (evt.getByLabel(fVertexModuleLabel, vtxHandle))
161  art::fill_ptr_vector(vertices, vtxHandle);
162 
163  // Associations
164  art::FindManyP<recob::Hit> fmh(clusterHandle, evt, fClusterModuleLabel);
165  art::FindManyP<recob::Track> fmt(hitHandle, evt, fTrackModuleLabel);
168 
169  // Make showers
170  std::vector<std::vector<int> > newShowers;
171  std::vector<unsigned int> pfParticles;
172 
173  std::map<int,std::vector<int> > clusterToTracks;
174  std::map<int,std::vector<int> > trackToClusters;
175 
176  if (!pfpHandle.isValid()) {
177 
178  // Map between tracks and clusters
179  fEMShowerAlg.AssociateClustersAndTracks(clusters, fmh, fmt, clusterToTracks, trackToClusters);
180 
181  // Make initial showers
182  std::vector<std::vector<int> > initialShowers = fEMShowerAlg.FindShowers(trackToClusters);
183 
184  // Deal with views in which 2D reconstruction failed
185  std::vector<int> clustersToIgnore;
186  if (fFindBadPlanes)
187  clustersToIgnore = fEMShowerAlg.CheckShowerPlanes(initialShowers, clusters, fmh);
188  if (clustersToIgnore.size() > 0) {
189  clusterToTracks.clear();
190  trackToClusters.clear();
191  fEMShowerAlg.AssociateClustersAndTracks(clusters, fmh, fmt, clustersToIgnore, clusterToTracks, trackToClusters);
192  newShowers = fEMShowerAlg.FindShowers(trackToClusters);
193  }
194  else
195  newShowers = initialShowers;
196 
197  }
198 
199  else {
200 
201  // Use pfparticle information
203  for (size_t ipfp = 0; ipfp<pfps.size(); ++ipfp){
204  art::Ptr<recob::PFParticle> pfp = pfps[ipfp];
205  if (fCNNEMModuleLabel!="" && fUseCNNtoIDEMPFP){//use CNN to identify EM pfparticle
207  if (!hitResults){
208  throw cet::exception("EMShower") <<"Cannot get MVA results from "<<fCNNEMModuleLabel;
209  }
210  int trkLikeIdx = hitResults->getIndex("track");
211  int emLikeIdx = hitResults->getIndex("em");
212  if ((trkLikeIdx < 0) || (emLikeIdx < 0)){
213  throw cet::exception("EMShower") << "No em/track labeled columns in MVA data products.";
214  }
215  if (fmcp.isValid()){//find clusters
216  std::vector<art::Ptr<recob::Hit>> pfphits;
217  std::vector<art::Ptr<recob::Cluster> > clus = fmcp.at(ipfp);
218  for (size_t iclu = 0; iclu<clus.size(); ++iclu){
219  std::vector<art::Ptr<recob::Hit> > ClusterHits = fmh.at(clus[iclu].key());
220  pfphits.insert(pfphits.end(), ClusterHits.begin(), ClusterHits.end());
221  }
222  if (pfphits.size()){//find hits
223  auto vout = hitResults->getOutput(pfphits);
224  double trk_like = -1, trk_or_em = vout[trkLikeIdx] + vout[emLikeIdx];
225  if (trk_or_em > 0){
226  trk_like = vout[trkLikeIdx] / trk_or_em;
227  if (trk_like<fMinTrackLikeScore){ //EM like
228  std::vector<int> clusters;
229  for (size_t iclu = 0; iclu<clus.size(); ++iclu){
230  clusters.push_back(clus[iclu].key());
231  }
232  if (clusters.size()){
233  newShowers.push_back(clusters);
234  pfParticles.push_back(ipfp);
235  }
236  }
237  }
238  }
239  }
240  else{
241  throw cet::exception("EMShower") <<"Cannot get associated cluster for PFParticle "<<fPFParticleModuleLabel.encode()<<"["<<ipfp<<"]";
242  }
243  }
244  else if (pfp->PdgCode()==11){ //shower particle
245  if (fmcp.isValid()){
246  std::vector<int> clusters;
247  std::vector<art::Ptr<recob::Cluster> > clus = fmcp.at(ipfp);
248  for (size_t iclu = 0; iclu<clus.size(); ++iclu){
249  clusters.push_back(clus[iclu].key());
250  }
251  if (clusters.size()){
252  newShowers.push_back(clusters);
253  pfParticles.push_back(ipfp);
254  }
255  }
256  }
257  }
258  }
259 
260  // Make output larsoft products
261  int showerNum = 0;
262  for (std::vector<std::vector<int> >::iterator newShower = newShowers.begin(); newShower != newShowers.end(); ++newShower, ++showerNum) {
263 
264  if (showerNum != fShower and fShower != -1) continue;
265 
266  // New shower
267  if (fDebug > 0)
268  std::cout << std::endl << std::endl << "Start shower " << showerNum << std::endl;
269 
270  // New associations
271  art::PtrVector<recob::Hit> showerHits;
272  art::PtrVector<recob::Cluster> showerClusters;
273  art::PtrVector<recob::Track> showerTracks;
274  art::PtrVector<recob::SpacePoint> showerSpacePoints_p;
275 
276  std::vector<int> associatedTracks;
277 
278  // Make showers and associations
279  for (std::vector<int>::iterator showerCluster = (*newShower).begin(); showerCluster != (*newShower).end(); ++showerCluster) {
280 
281  // Clusters
282  art::Ptr<recob::Cluster> cluster = clusters.at(*showerCluster);
283  showerClusters.push_back(cluster);
284 
285  // Hits
286  std::vector<art::Ptr<recob::Hit> > showerClusterHits = fmh.at(cluster.key());
287  if (fCNNEMModuleLabel!="" && fUseCNNtoIDEMHit){//use CNN to identify EM hits
289  if (!hitResults){
290  throw cet::exception("EMShower") <<"Cannot get MVA results from "<<fCNNEMModuleLabel.encode();
291  }
292  int trkLikeIdx = hitResults->getIndex("track");
293  int emLikeIdx = hitResults->getIndex("em");
294  if ((trkLikeIdx < 0) || (emLikeIdx < 0)){
295  throw cet::exception("EMShower") << "No em/track labeled columns in MVA data products.";
296  }
297  for (auto & showerHit : showerClusterHits){
298  auto vout = hitResults->getOutput(showerHit);
299  double trk_like = -1, trk_or_em = vout[trkLikeIdx] + vout[emLikeIdx];
300  if (trk_or_em > 0){
301  trk_like = vout[trkLikeIdx] / trk_or_em;
302  if (trk_like<fMinTrackLikeScore){ //EM like
303  showerHits.push_back(showerHit);
304  }
305  }
306  }
307  }
308  else{
309  for (std::vector<art::Ptr<recob::Hit> >::iterator showerClusterHit = showerClusterHits.begin(); showerClusterHit != showerClusterHits.end(); ++showerClusterHit)
310  showerHits.push_back(*showerClusterHit);
311  }
312  // Tracks
313  if (!pfpHandle.isValid()) { // Only do this for non-pfparticle mode
314  std::vector<int> clusterTracks = clusterToTracks.at(*showerCluster);
315  for (std::vector<int>::iterator clusterTracksIt = clusterTracks.begin(); clusterTracksIt != clusterTracks.end(); ++clusterTracksIt)
316  if (std::find(associatedTracks.begin(), associatedTracks.end(), *clusterTracksIt) == associatedTracks.end())
317  associatedTracks.push_back(*clusterTracksIt);
318  }
319  }
320 
321  if (!pfpHandle.isValid()) { // For non-pfparticles, get space points from tracks
322  // Tracks and space points
323  for (std::vector<int>::iterator associatedTracksIt = associatedTracks.begin(); associatedTracksIt != associatedTracks.end(); ++associatedTracksIt) {
324  art::Ptr<recob::Track> showerTrack = tracks.at(*associatedTracksIt);
325  showerTracks.push_back(showerTrack);
326  }
327  }
328 
329  else { // For pfparticles, get space points from hits
331  for (size_t ihit = 0; ihit<showerHits.size(); ++ihit){
332  if (fmspp.isValid()){
333  std::vector<art::Ptr<recob::SpacePoint> > spacePoints_pfp = fmspp.at(ihit);
334  for (std::vector<art::Ptr<recob::SpacePoint> >::iterator spacePointsIt = spacePoints_pfp.begin(); spacePointsIt != spacePoints_pfp.end(); ++spacePointsIt)
335  showerSpacePoints_p.push_back(*spacePointsIt);
336  }
337  }
338  }
339 
340  if (!pfpHandle.isValid()) {
341 
342  // First, order the hits into the correct shower order in each plane
343  if (fDebug > 1)
344  std::cout << " ------------------ Ordering shower hits -------------------- " << std::endl;
345  std::map<int,std::vector<art::Ptr<recob::Hit> > > showerHitsMap = fEMShowerAlg.OrderShowerHits(showerHits, fPlane);
346  if (fDebug > 1)
347  std::cout << " ------------------ End ordering shower hits -------------------- " << std::endl;
348 
349  // Find the track at the start of the shower
350  std::unique_ptr<recob::Track> initialTrack;
351  std::map<int,std::vector<art::Ptr<recob::Hit> > > initialTrackHits;
352  fEMShowerAlg.FindInitialTrack(showerHitsMap, initialTrack, initialTrackHits, fPlane);
353 
354  // Make space points
355  std::vector<std::vector<art::Ptr<recob::Hit> > > hitAssns;
356  std::vector<recob::SpacePoint> showerSpacePoints;
357  if (fMakeSpacePoints)
358  showerSpacePoints = fEMShowerAlg.MakeSpacePoints(showerHitsMap, hitAssns);
359  else {
360  for (art::PtrVector<recob::Track>::const_iterator trackIt = showerTracks.begin(); trackIt != showerTracks.end(); ++trackIt) {
361  const std::vector<art::Ptr<recob::SpacePoint> > trackSpacePoints = fmsp.at(trackIt->key());
362  for (std::vector<art::Ptr<recob::SpacePoint> >::const_iterator trackSpIt = trackSpacePoints.begin(); trackSpIt != trackSpacePoints.end(); ++trackSpIt) {
363  showerSpacePoints.push_back(*(*trackSpIt));
364  hitAssns.push_back(std::vector<art::Ptr<recob::Hit> >());
365  }
366  }
367  }
368 
369  // Save space points
370  int firstSpacePoint = spacePoints->size(), nSpacePoint = 0;
371  for (std::vector<recob::SpacePoint>::const_iterator sspIt = showerSpacePoints.begin(); sspIt != showerSpacePoints.end(); ++sspIt, ++nSpacePoint) {
372  spacePoints->emplace_back(sspIt->XYZ(), sspIt->ErrXYZ(), sspIt->Chisq(), spacePoints->size());
373  util::CreateAssn(*this, evt, *(spacePoints.get()), hitAssns.at(nSpacePoint), *(hitSpAssociations.get()));
374  }
375  int lastSpacePoint = spacePoints->size();
376 
377  // Make shower object and associations
378  recob::Shower shower = fEMShowerAlg.MakeShower(showerHits, initialTrack, initialTrackHits);
379  shower.set_id(showerNum);
380  if ( fSaveNonCompleteShowers or (!fSaveNonCompleteShowers and shower.ShowerStart() != TVector3(0,0,0)) ) {
381  showers->push_back(shower);
382  util::CreateAssn(*this, evt, *(showers.get()), showerHits, *(hitShowerAssociations.get()));
383  util::CreateAssn(*this, evt, *(showers.get()), showerClusters, *(clusterAssociations.get()));
384  util::CreateAssn(*this, evt, *(showers.get()), showerTracks, *(trackAssociations.get()));
385  util::CreateAssn(*this, evt, *(showers.get()), *(spacePoints.get()), *(spShowerAssociations.get()), firstSpacePoint, lastSpacePoint);
386  }
387  else
388  mf::LogInfo("EMShower") << "Discarding shower " << showerNum << " due to incompleteness (SaveNonCompleteShowers == false)";
389  }
390 
391  else { // pfParticle
392 
393  if (vertices.size()) {
394  //found the most upstream vertex
395  TVector3 nuvtx(0,0,DBL_MAX);
396  for (auto & vtx: vertices){
397  double xyz[3];
398  vtx->XYZ(xyz);
399  if (xyz[2]<nuvtx.Z()){
400  nuvtx.SetXYZ(xyz[0], xyz[1], xyz[2]);
401  }
402  }
403 
404  TVector3 shwvtx(0,0,0);
405  double mindis = DBL_MAX;
406  for (auto &sp : showerSpacePoints_p){
407  double dis = sqrt(pow(nuvtx.X()-sp->XYZ()[0],2)+pow(nuvtx.Y()-sp->XYZ()[1],2)+pow(nuvtx.Z()-sp->XYZ()[2],2));
408  if (dis<mindis){
409  mindis = dis;
410  shwvtx.SetXYZ(sp->XYZ()[0], sp->XYZ()[1], sp->XYZ()[2]);
411  }
412  }
413 
414  art::Ptr<recob::Vertex> bestvtx;
415  mindis = DBL_MAX;
416  for (auto & vtx: vertices){
417  double xyz[3];
418  vtx->XYZ(xyz);
419  double dis = sqrt(pow(xyz[0]-shwvtx.X(),2)+pow(xyz[1]-shwvtx.Y(),2)+pow(xyz[2]-shwvtx.Z(),2));
420  if (dis<mindis){
421  mindis = dis;
422  bestvtx = vtx;
423  }
424  }
425 
426  int iok = 0;
427 
428  recob::Shower shower = fEMShowerAlg.MakeShower(showerHits, bestvtx, iok);
429  //shower.set_id(showerNum);
430  if (iok==0) {
431  showers->push_back(shower);
432  showers->back().set_id(showers->size()-1);
433  util::CreateAssn(*this, evt, *(showers.get()), showerHits, *(hitShowerAssociations.get()));
434  util::CreateAssn(*this, evt, *(showers.get()), showerClusters, *(clusterAssociations.get()));
435  util::CreateAssn(*this, evt, *(showers.get()), showerTracks, *(trackAssociations.get()));
436  util::CreateAssn(*this, evt, *(showers.get()), showerSpacePoints_p, *(spShowerAssociations.get()));
437  }
438  }
439 
440  }
441 
442  }
443 
444  // Put in event
445  evt.put(std::move(showers));
446  evt.put(std::move(spacePoints));
447  evt.put(std::move(hitShowerAssociations));
448  evt.put(std::move(clusterAssociations));
449  evt.put(std::move(trackAssociations));
450  evt.put(std::move(spShowerAssociations));
451  evt.put(std::move(hitSpAssociations));
452 
453 }
454 
void XYZ(double *xyz) const
Legacy method to access vertex position, preserved to avoid breaking code. Please try to use Vertex::...
Definition: Vertex.cxx:36
intermediate_table::iterator iterator
std::vector< int > CheckShowerPlanes(std::vector< std::vector< int > > const &initialShowers, std::vector< art::Ptr< recob::Cluster > > const &clusters, art::FindManyP< recob::Hit > const &fmh) const
Takes the initial showers found and tries to resolve issues where one bad view ruins the event...
Provides recob::Track data product.
std::vector< std::vector< int > > FindShowers(std::map< int, std::vector< int > > const &trackToClusters) const
Makes showers given a map between tracks and all clusters associated with them.
const TVector3 & ShowerStart() const
Definition: Shower.h:192
Utilities related to art service access.
art::InputTag fClusterModuleLabel
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
iterator begin()
Definition: PtrVector.h:217
Declaration of signal hit object.
EDProducer()=default
EMShower(fhicl::ParameterSet const &pset)
art::InputTag fVertexModuleLabel
int PdgCode() const
Return the type of particle as a PDG ID.
Definition: PFParticle.h:83
intermediate_table::const_iterator const_iterator
Cluster finding and building.
art::InputTag fTrackModuleLabel
std::string encode() const
Definition: InputTag.cc:95
void set_id(const int id)
Definition: Shower.h:128
bool isValid() const
Definition: Handle.h:183
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
bool getByLabel(std::string const &label, std::string const &instance, Handle< PROD > &result) const
Definition: DataViewImpl.h:437
void hits()
Definition: readHits.C:15
typename data_t::const_iterator const_iterator
Definition: PtrVector.h:55
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:56
void push_back(Ptr< U > const &p)
Definition: PtrVector.h:435
recob::Shower MakeShower(art::PtrVector< recob::Hit > const &hits, std::unique_ptr< recob::Track > const &initialTrack, std::map< int, std::vector< art::Ptr< recob::Hit > > > const &initialTrackHits) const
Makes a recob::Shower object given the hits in the shower and the initial track-like part...
iterator end()
Definition: PtrVector.h:231
std::vector< recob::SpacePoint > MakeSpacePoints(std::map< int, std::vector< art::Ptr< recob::Hit > > > hits, std::vector< std::vector< art::Ptr< recob::Hit > > > &hitAssns) const
Makes space points from the shower hits in each plane.
key_type key() const
Definition: Ptr.h:238
art::InputTag fHitsModuleLabel
art::InputTag fCNNEMModuleLabel
Declaration of cluster object.
art::InputTag fPFParticleModuleLabel
void produce(art::Event &evt)
size_type size() const
Definition: PtrVector.h:302
std::map< int, std::vector< art::Ptr< recob::Hit > > > OrderShowerHits(const art::PtrVector< recob::Hit > &shower, int plane) const
Takes the hits associated with a shower and orders them so they follow the direction of the shower...
Utility object to perform functions of association.
bool CreateAssn(PRODUCER const &prod, art::Event &evt, std::vector< T > const &a, art::Ptr< U > const &b, art::Assns< U, T > &assn, std::string a_instance, size_t index=UINT_MAX)
Creates a single one-to-one association.
void AssociateClustersAndTracks(std::vector< art::Ptr< recob::Cluster > > const &clusters, art::FindManyP< recob::Hit > const &fmh, art::FindManyP< recob::Track > const &fmt, std::map< int, std::vector< int > > &clusterToTracks, std::map< int, std::vector< int > > &trackToClusters) const
Map associated tracks and clusters together given their associated hits.
Definition: EMShowerAlg.cxx:59
void FindInitialTrack(const std::map< int, std::vector< art::Ptr< recob::Hit > > > &hits, std::unique_ptr< recob::Track > &initialTrack, std::map< int, std::vector< art::Ptr< recob::Hit > > > &initialTrackHits, int plane) const
Finds the initial track-like part of the shower and the hits in all views associated with it...
EMShowerAlg fEMShowerAlg
TCEvent evt
Definition: DataStructs.cxx:7
void fill_ptr_vector(std::vector< Ptr< T >> &ptrs, H const &h)
Definition: Ptr.h:293
art::ServiceHandle< geo::Geometry const > fGeom
ProductID put(std::unique_ptr< PROD > &&edp, FullSemantic< Level::Run > const semantic)
Definition: DataViewImpl.h:692
detinfo::DetectorProperties const * fDetProp
static std::unique_ptr< MVAReader > create(const art::Event &evt, const art::InputTag &tag)
Definition: MVAReader.h:110
art framework interface to geometry description
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33