// VertexGen_ELLIE.cc // Contact person:Jeanne Wilson // See VertexGen_ELLIE.hh for more details //———————————————————————// // This file contains the bulk of code that used to be in Gen_ELLIE.cc - // split up so can now be used to generate ELLIE events // as a separate vertex. It is meant to be used with the PosGen_ELLIE position // generator with the same fibre_id argument for both vertex and position // to simulate ELLIE events. A separate vertex and position generator are // required as input to the coincidence generator. //////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace CLHEP; using namespace std; namespace RAT { VertexGen_ELLIE::VertexGen_ELLIE(const char *arg_dbname) : GLG4VertexGen(arg_dbname) { // Not yet initialised - call the Init function to setup on first event fInitDB = false; // Initialise the fibre to null - set either by state or as DB command fFibreID=""; } void VertexGen_ELLIE::Init() { // This function should be run once on the first event (once DB all set up) warn << "VertexGen_ELLIE::Init: Initialising simulation settings \n"; // Photon definition (assume /run/initialize already done) fPhotonDef = G4ParticleTable::GetParticleTable()->FindParticle("opticalphoton"); // Is there a photon thinning factor - this doesn't necessarily make sense for optical simulations double photon_thin = RAT::PhotonThinning::GetFactor(); if(photon_thin > 1.0){ // OUTPUT WARNING warn << "VertexGen_ELLIE::Init: !!!WARNING!!! - You are using photon thinning with factor " << photon_thin << "\n Are you sure you want to do this for an Optical Simulation (could result in loss of accuracy if low number of photons) \n"; } // Load database values DBLinkPtr lELLIE; try { lELLIE = DB::Get()->GetLink("ELLIE"); fPhotonsPerEvent = int(double(lELLIE->GetI("intensity"))/photon_thin); debug << "VertexGen_ELLIE::Init: intensity = " << fPhotonsPerEvent << "\n"; }catch (DBNotFoundError &e) { Log::Die("VertexGen_ELLIE::Init: can't find ratdb Intensity info."); }; // Optional setting try { string temp = lELLIE->GetS("pulse_mode"); if(temp == "poisson"){ fPoisson = true; }else if(temp == "fixed"){ fPoisson = false; }else{ Log::Die(dformat("VertexGen_ELLIE::Init: ELLIE.pulse_mode = \"%s\" is invalid.", temp.c_str())); } }catch (DBNotFoundError &e) { }; // Which fibre to simulate - identify by name: try { if(fFibreID=="") fFibreID = lELLIE->GetS("fibre_id"); // It hasn't been set as the state of the generator so load from DB }catch (DBNotFoundError &e) { Log::Die("VertexGen_ELLIE::Init: can't find ratdb fibre_id info."); }; // Now get the info specific for this fibre DBLinkPtr lfibre; try{ lfibre = DB::Get()->GetLink("FIBRE", fFibreID); info << "VertexGen_ELLIE::Init: Getting fibre info for " << fFibreID << "\n"; }catch(DBNotFoundError &e) { Log::Die("VertexGen_ELLIE::Init: can't find ratdb FIBRE info for " + fFibreID); } // ELLIE directions try { fDirX = lfibre->GetD("u"); fDirY = lfibre->GetD("v"); fDirZ = lfibre->GetD("w"); }catch (DBNotFoundError &e) { Log::Die("VertexGen_ELLIE::Init: No directions set for fibre " + fFibreID); } // wavelength info try{ fMono_wls = lELLIE->GetI("mono_wl"); }catch (DBNotFoundError &e) { }; if(!fMono_wls){ // chose wavelength either set by user (SMELLIE) or use default for that ELLIE fibre (AMELLIE/TELLIE) try{ fELLIEWavelength = lELLIE->GetS("wavelength_dist"); info << "VertexGen_ELLIE::Init: Setting wavelength distribution to " << fELLIEWavelength << "\n"; }catch (DBNotFoundError &e) { info << "VertexGen_ELLIE::Init: Using fixed wavelengths as set in ratdb FIBRE wavelength field\n"; fELLIEWavelength = "FIBREDEFAULT"; } if(fELLIEWavelength=="FIBREDEFAULT"){ try{ fELLIEWavelength = lfibre->GetS("wavelength_dist"); }catch (DBNotFoundError &e) { // wavelength not specified in either place Log::Die("VertexGen_ELLIE::Init: can't find wavelength specification for fibre " + fFibreID); } } if(fELLIEWavelength=="SMELLIESUPERK"){ // setable wavelength region only for specified wavelength distributions (SMELLIE) try { std::vector lwaveRange = lELLIE->GetDArray("wavelength_dist_range"); fELLIEWavelengthRange.push_back(lwaveRange.at(0)); // only a double method exists to import array but want floats fELLIEWavelengthRange.push_back(lwaveRange.at(1)); info << "VertexGen_ELLIE::Init: Limiting wavelength spectrum to within specified min, max: (" << fELLIEWavelengthRange.at(0) << "," << fELLIEWavelengthRange.at(1) << ")\n"; }catch (DBNotFoundError &e) { Log::Die("VertexGen_ELLIE::Init: can't find wavelength distribution range specification"); } } // Find the right database file with this index (need exception catching here) debug << "VertexGen_ELLIE::Init: Getting Wavelength info for " << fELLIEWavelength << "\n"; DBLinkPtr lELLIEwave; try{ lELLIEwave = DB::Get()->GetLink("ELLIEWAVE", fELLIEWavelength); }catch(DBNotFoundError &e) { Log::Die("VertexGen_ELLIE::Init: can't find ratdb wavelength distribution for " + fELLIEWavelength); } // get the wavelengths try { fwave = lELLIEwave->GetFArrayFromD("dist_wl"); }catch(DBNotFoundError &e) { Log::Die("VertexGen_ELLIE::Init: can't find ratdb wavelength data for " + fELLIEWavelength); } // make a check that the values in the dist_wl make sense and increase monotonically for(unsigned int iv=1;ivGetFArrayFromD("dist_wl_intensity"); }catch(DBNotFoundError &e) { Log::Die("VertexGen_ELLIE::Init: can't find ratdb intensity data for wavelength distribution for " + fELLIEWavelength); } // check the intensity vector is the same length as the wavelength vector if (fwave.size()!=fwaveInt.size()){ warn << "VertexGen_ELLIE::Init: wavelength vector size:" << fwave.size() << " wavelength Intensity vector size:" << fwaveInt.size() << "\n"; Log::Die("VertexGen_ELLIE::Init: Must have one intensity per wavelength. Please check wavelength spectrum values."); } // If wavelength range was specified, replace the wavelength spectrum with a subset of the spectrum (and only if wavelength distribution was specified - SMELLIE) if(fELLIEWavelength=="SMELLIESUPERK"){ if (!(fELLIEWavelengthRange.at(0)==0.0&&fELLIEWavelengthRange.at(1)==0.0)) { // tests if wavelength range was specified if (((fELLIEWavelengthRange.at(1)-fELLIEWavelengthRange.at(0))>=10)&&((fELLIEWavelengthRange.at(1)-fELLIEWavelengthRange.at(0))<=100)) { // tests max and minimum SuperK bandwidth (also tests if max wavelength > min wavelength) if (fELLIEWavelengthRange.at(0)>=400 && fELLIEWavelengthRange.at(1)<=700.0) { // tests maximum and minimum wavelengths int wavelengthRangeMinIndex = -1, wavelengthRangeMaxIndex = -1; for (unsigned long i=0; i=fELLIEWavelengthRange.at(0)) { // >= min value, inclusive wavelengthRangeMinIndex = i; break; } if (wavelengthRangeMinIndex<0) { warn << "VertexGen_ELLIE::Init: Minimum wavelength not found in laser wavelength spectrum!" << fwave[0] << " " << fELLIEWavelengthRange.at(0) << "\n"; Log::Die("VertexGen_ELLIE::Init: Minimum wavelength not found in laser wavelength spectrum. Please check values."); } for (unsigned long i=0; ifELLIEWavelengthRange.at(1)) { // > max value, exclusive wavelengthRangeMaxIndex = i; break; } if (wavelengthRangeMaxIndex<0) { warn << "VertexGen_ELLIE::Init: Maximum wavelength not found in laser wavelength spectrum!\n"; Log::Die("VertexGen_ELLIE::Init: Maximum wavelength not found in laser wavelength spectrum. Please check values."); } debug << "VertexGen_ELLIE::Init: Selected wavelength spectrum elements between indicies = " << wavelengthRangeMinIndex << "-" << wavelengthRangeMaxIndex << "\n"; std::vector subWave(&fwave[wavelengthRangeMinIndex],&fwave[wavelengthRangeMaxIndex]); std::vector subWaveInt(&fwaveInt[wavelengthRangeMinIndex],&fwaveInt[wavelengthRangeMaxIndex]); std::fill(fwave.begin(), fwave.end(), 0); // zero the old vectors to be sure they are empty std::fill(fwaveInt.begin(), fwaveInt.end(), 0); fwave.clear(); // reset the old vectors to have zero elements fwaveInt.clear(); for (unsigned int i=0; i=10nm and <=100nm. Specified wavelengths: (" << fELLIEWavelengthRange.at(0) << "," << fELLIEWavelengthRange.at(1) << ") BW: " << (fELLIEWavelengthRange.at(1)-fELLIEWavelengthRange.at(0)) << "\n"; Log::Die("VertexGen_ELLIE::Init: The specified SMELLIESUPERK laser bandwidth is beyond maximum or minimum. Must be >=10nm and <=100nm."); } }else { warn << "VertexGen_ELLIE::Init: SMELLIESUPERK laser set without specifing a wavelength range. Must select min & max wavelengths using: wavelength_dist_range [min,max].\n"; Log::Die("VertexGen_ELLIE::Init: SMELLIESUPERK laser set without specifing a wavelength range. Must select min & max wavelengths using: wavelength_dist_range [min,max]"); } detail << "VertexGen_ELLIE::Init: Wavelength distribution values: ["; for (unsigned int i=0; iGetS("time_dist"); info << "VertexGen_ELLIE::Init: Setting time distribution to " + fELLIETime + "\n"; }catch (DBNotFoundError &e) { info << "VertexGen_ELLIE::Init: Using fixed time distribution as set in ratdb FIBRE time field\n"; fELLIETime = "FIBREDEFAULT"; } if(fELLIETime=="mono"){ fMono_time = 1; info << "VertexGen_ELLIE::Init WARNING: Setting time distribution to mono \n"; }else if(fELLIETime=="FIBREDEFAULT"){ try{ fELLIETime = lfibre->GetS("time_dist"); }catch (DBNotFoundError &e){ // time distribution not specified - check for parameter definition try { fELLIEIPW = lfibre->GetD("ipw"); }catch (DBNotFoundError &sube) { Log::Die("VertexGen_ELLIE::Init: can't find time IPW for fibre " + fFibreID); } try { fELLIEPulserBoard = lfibre->GetS("pulser_board"); }catch (DBNotFoundError &sube) { Log::Die("VertexGen_ELLIE::Init: can't find time pulser-board for fibre " + fFibreID); } fELLIETime="FIBREPARAM"; info << "VertexGen_ELLIE::Init info: Using parameterised timing distribution for " << fFibreID << " with IPW = " << fELLIEIPW << ", pulserboard = " <GetLink("ELLIEPULSERBOARD",fELLIEPulserBoard); debug << "VertexGen_ELLIE::Init: Getting Pulserboard info for board " << fELLIEPulserBoard << "\n"; }catch (DBNotFoundError &e) { Log::Die("VertexGen_ELLIE::Init: can't find pulser board info for board " + fELLIEPulserBoard); } ////FIXME!!!!//// // Pull the parameters out of the PulserBoard table here // Then do something with them (ideally make a set of vectors using SampleRandom) // - for now put in a dummy distribution. ftime.push_back(0.2); ftime.push_back(0.8); ftime.push_back(1.0); ftimeInt.push_back(1.0); ftimeInt.push_back(0.5); ftimeInt.push_back(0.0); float timemagsum = 0; ftimeCumMag.clear(); ftimeCumMag.push_back(0.0); detail << " Cumulative time dummy dist"; for(unsigned int i=1;iGetLink("ELLIETIME", fELLIETime); debug << "VertexGen_ELLIE::Init: Getting Time info for " << fELLIETime << "\n"; }catch(DBNotFoundError &e) { Log::Die("VertexGen_ELLIE::Init: can't find ratdb time distribution for " + fELLIETime); } // firstly get the times and store endpoints ftime = lELLIEtime->GetFArrayFromD("dist_time"); fMaxTime = ftime.back(); fMinTime = ftime.front(); // make a check that the values in the dist_time make sense and increase monotonically for(unsigned int iv=1;ivGetFArrayFromD("dist_time_intensity"); // now create a cumulative magnitude vector float timemagsum = 0; ftimeCumMag.clear(); ftimeCumMag.push_back(0.0); detail << " Cumulative time "; for(unsigned int i=1;iGetS("angle_dist"); info << "VertexGen_ELLIE::Init: Setting angular distribution to " + fELLIEAngle + "\n"; }catch (DBNotFoundError &e) { info << "VertexGen_ELLIE::Init: Using fixed angular distribution as set in ratdb FIBRE time field\n"; fELLIEAngle="FIBREDEFAULT"; } fIso_angle = 0; fZero_angle = 0; if(fELLIEAngle=="iso"){ fIso_angle = 1; info << "VertexGen_ELLIE::Init: Setting angular distribution to isotropic \n"; }else if(fELLIEAngle=="zero"){ fZero_angle = 1; info << "VertexGen_ELLIE::Init: Setting angular distribution to zero (ie no dispersion) \n"; if(fIso_angle&&fZero_angle)Log::Die("VertexGen_ELLIE: Beam is set to be both isotropic and Zero dispersion!"); }else{ if(fELLIEAngle=="FIBREDEFAULT"){ try{ fELLIEAngle = lfibre->GetS("angle_dist"); }catch (DBNotFoundError &e){ // angular distribution not specified in either place Log::Die("VertexGen_ELLIE::Init: can't find angular information for fibre " + fFibreID); } } // Find the right database file with this index (need exception catching here) DBLinkPtr lELLIEangle; try{ lELLIEangle = DB::Get()->GetLink("ELLIEANGLE", fELLIEAngle); debug << "VertexGen_ELLIE::Init: Getting Angular info for " << fELLIEAngle << "\n"; }catch(DBNotFoundError &e) { Log::Die("VertexGen_ELLIE::Init: can't find ratdb angular distribution for " + fELLIEAngle); } string angle_units = lELLIEangle->GetS("dist_angle_option"); if(angle_units == "degrees" ){ fConvertAngle = twopi/360.; }else if(angle_units == "radians"){ fConvertAngle = 1; }else{ fConvertAngle = 1; warn << "VertexGen_ELLIE::Init: Unknown angular unit, assuming radians\n"; } // firstly get the angles and store endpoints fang = lELLIEangle->GetFArrayFromD("dist_angle"); fMaxAngle = fang.back(); fMinAngle = fang.front(); // make a check that the values in the dist_wl make sense and increase monotonically for(unsigned int iv=1;ivGetFArrayFromD("dist_angle_intensity"); // now create a cumulative magnitude vector and account for solid angle weighting float angmagsum = 0; fangCumMag.clear(); fangCumMag.push_back(0.0); fangInt[0] =fangInt[0]*sin(fang[0]*fConvertAngle); detail << " Cumulative angle "; for(unsigned int i=1;ishoot() * (fMaxTime - fMinTime) + fMinTime); } // Record in extra G4Event information EventInfo *exinfo = dynamic_cast(event->GetUserInformation()); DS::Calib *calib = new DS::Calib(); calib->SetSourceName(fFibreID); // set name as fibre ID now calib->SetID(1); // This number is non zero if there is a source, but what ID to assign??? calib->SetMode((int) wavelength); // sampled wavelength distribution calib->SetIntensity(nphot); calib->SetDir( TVector3(normal.x(), normal.y(), normal.z()) ); calib->SetPos( TVector3(dx.x(), dx.y(), dx.z()) ); calib->SetTime(time); exinfo->SetCalib(calib); // Add vertices for each photon for (int i=0; i < nphot; i++) { // photon time double t1 = 0; // stays as 0 for Mono_time if(!fMono_time){ t1 = SampleRandom(ftime, ftimeInt, ftimeCumMag); } if(!fMono_wls){ // set above for Mono_wls wavelength = SampleRandom(fwave, fwaveInt, fwaveCumMag); } float energy = hbarc * twopi / (wavelength * nm); float momentum = energy; // GEANT uses momentum in same units as energy // photon direction double theta; if (fIso_angle){ theta = acos(0.9999 * (2.0*G4UniformRand()-1.0)); }else if(fZero_angle){ theta = 0; }else{ theta = fConvertAngle*SampleRandom(fang, fangInt, fangCumMag); } double phi = twopi * G4UniformRand(); // Calc momentum G4ThreeVector mom(normal); mom.rotate(theta, perp); // Rotate away from ELLIE direction (polar angle) mom.rotate(phi, normal); // Rotate around ELLIE direction (phi) mom.setMag(momentum); // Scale to right magnitude G4PrimaryVertex *vertex = new G4PrimaryVertex(dx, dt+t1); G4PrimaryParticle *particle = new G4PrimaryParticle(fPhotonDef, mom.x(), mom.y(), mom.z()); // Generate random polarization phi = (G4UniformRand()*2.0-1.0)*pi; G4ThreeVector e1 = mom.orthogonal().unit(); G4ThreeVector e2 = mom.unit().cross(e1); G4ThreeVector pol = e1*cos(phi)+e2*sin(phi); particle->SetPolarization(pol.x(), pol.y(), pol.z()); particle->SetMass(0.0); // Seems odd, but used in GLG4VertexGen_Gun vertex->SetPrimary(particle); event->AddPrimaryVertex(vertex); } } float VertexGen_ELLIE::SampleRandom(std::vector mydistx, std::vector mydistraw, std::vector mydistcum){ // Return value sampled from distribution with cumulative distribution mydistcum, raw distribution mydistraw for x points mydistx // (assume linear interpolation between specified points in spectrum) float start = mydistcum.front(); float stop = mydistcum.back(); // now throw a random number between these limits float random = start+G4UniformRand()*(stop-start); // now loop through cumulative distribution to choose energy int istep = 0; while(mydistcum[istep+1]