/* This file is part of MAUS: http:// micewww.pp.rl.ac.uk:8080/projects/maus * * MAUS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MAUS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MAUS. If not, see . */ /* Author: Peter Lane */ #include "src/common_cpp/Optics/TransferMapOpticsModel.hh" #include #include #include "TLorentzVector.h" #include "DataStructure/Primary.hh" #include "DataStructure/ThreeVector.hh" #include "DataStructure/Global/ReconEnums.hh" #include "JsonCppProcessors/PrimaryProcessor.hh" #include "src/common_cpp/Optics/CovarianceMatrix.hh" #include "src/common_cpp/Optics/PhaseSpaceVector.hh" #include "src/common_cpp/Optics/TransferMap.hh" #include "Recon/Global/DataStructureHelper.hh" #include "Recon/Global/Particle.hh" #include "Simulation/MAUSGeant4Manager.hh" #include "Simulation/MAUSPhysicsList.hh" #include "Simulation/VirtualPlanes.hh" #include "src/legacy/Interface/MiceMaterials.hh" #include "src/legacy/Interface/MICERun.hh" #include "src/legacy/Simulation/FillMaterials.hh" namespace MAUS { using DataStructure::Global::TrackPoint; using DataStructure::Global::PID; using recon::global::Particle; using recon::global::DataStructureHelper; // ############################## // TransferMapOpticsModel public // ############################## // ****************************** // Constructors // ****************************** TransferMapOpticsModel::TransferMapOpticsModel( Json::Value const * const configuration) : OpticsModel(configuration), built_(false) { // Reference Particle MAUSGeant4Manager * const simulator = MAUSGeant4Manager::GetInstance(); MAUS::MAUSPrimaryGeneratorAction::PGParticle reference_pgparticle = simulator->GetReferenceParticle(); reference_primary_ = reference_pgparticle.GetPrimary(); primary_plane_ = reference_pgparticle.z; ThreeVector position = reference_primary_.GetPosition(); ThreeVector momentum = reference_primary_.GetMomentum(); reference_trajectory_ = PhaseSpaceVector(reference_primary_.GetTime(), reference_primary_.GetEnergy(), position.x(), momentum.x(), position.y(), momentum.y()); // First plane particle coordinate deltas Json::Value delta_values = JsonWrapper::GetProperty( *configuration_, "TransferMapOpticsModel_Deltas", JsonWrapper::objectValue); deltas_ = PhaseSpaceVector( delta_values["t"].asDouble(), delta_values["E"].asDouble(), delta_values["x"].asDouble(), delta_values["Px"].asDouble(), delta_values["y"].asDouble(), delta_values["Py"].asDouble()); } TransferMapOpticsModel::~TransferMapOpticsModel() { std::map::iterator transfer_map; for (transfer_map = transfer_maps_.begin(); transfer_map != transfer_maps_.end(); ++transfer_map) { delete (*transfer_map).second; } } void TransferMapOpticsModel::Build() { // Create some test hits at the desired First plane const std::vector primaries = Primaries(); std::vector primary_vectors; Json::Value primaries_json; for (std::vector::const_iterator primary = primaries.begin(); primary < primaries.end(); ++primary) { // serialize primary to JSON PrimaryProcessor serializer; Json::Value * primary_json = serializer.CppToJson(*primary, ""); Json::Value object_value; object_value["primary"] = *primary_json; primaries_json.append(object_value); // generate a phase space vector for the primary ThreeVector position = primary->GetPosition(); ThreeVector momentum = primary->GetMomentum(); PhaseSpaceVector primary_vector(primary->GetTime(), primary->GetEnergy(), position.x(), momentum.x(), position.y(), momentum.y()); primary_vectors.push_back(primary_vector); } MAUSGeant4Manager * simulator = MAUSGeant4Manager::GetInstance(); // Force setting of stochastics simulator->GetPhysicsList()->BeginOfRunAction(); // Simulate on the primaries, generating virtual detector tracks for each Json::Value virtual_tracks; try { Squeak::mout(Squeak::debug) << "DEBUG TransferMapOpticsModel::Build: " << "Executing RunManyParticles with the following VirtualPlanes:" << std::endl; MAUS::VirtualPlaneManager * const virtual_plane_mgr = MAUSGeant4Manager::GetInstance()->GetVirtualPlanes(); for (size_t i = 0; i < virtual_plane_mgr->GetPlanes().size(); ++i) { Squeak::mout(Squeak::debug) << virtual_plane_mgr->GetPlanes()[i]->GetPosition() << std::endl; } virtual_tracks = MAUSGeant4Manager::GetInstance()->RunManyParticles(primaries_json); } catch (std::exception ex) { std::stringstream message; message << "Simulation failed." << std::endl << ex.what() << std::endl; throw(Exceptions::Exception(Exceptions::nonRecoverable, message.str(), "MAUS::TransferMapOpticsModel::Build()")); } if (virtual_tracks.size() == 0) { throw(Exceptions::Exception(Exceptions::nonRecoverable, "No events were generated during simulation.", "MAUS::TransferMapOpticsModel::Build()")); } // Map stations to hits in each virtual track std::map > station_hits_map; Squeak::mout(Squeak::debug) << "DEBUG TransferMapOpticsModel::Build: " << "RunManyParticles yielded the following tracks:" << std::endl; for (Json::Value::iterator virtual_track = virtual_tracks.begin(); virtual_track != virtual_tracks.end(); ++virtual_track) { const Json::Value hits = (*virtual_track)["virtual_hits"]; for (int hit_index = 0; hit_index < static_cast(hits.size()); ++hit_index) { Squeak::mout(Squeak::debug) << setprecision(12) << "(" << hits[hit_index]["position"]["x"].asDouble() << ", " << hits[hit_index]["position"]["y"].asDouble() << ", " << hits[hit_index]["position"]["z"].asDouble() << ") "; } Squeak::mout(Squeak::debug) << std::endl; MapStationsToHits(station_hits_map, *virtual_track); Squeak::mout(Squeak::debug) << "DEBUG TransferMapOpticsModel::Build: " << "station_hits_map size: " << station_hits_map.size() << std::endl; } // Calculate transfer maps from the primary plane to each station plane std::map >::iterator station_hits; for (station_hits = station_hits_map.begin(); station_hits != station_hits_map.end(); ++station_hits) { // calculate transfer map and index it by the station z-plane Squeak::mout(Squeak::debug) << "DEBUG TransferMapOpticsModel::Build: " << "Calculating transfer map for plane at z = " << station_hits->first << std::endl; transfer_maps_[station_hits->first] = CalculateTransferMap(primary_vectors, station_hits->second); } built_ = true; } const TransferMap * TransferMapOpticsModel::FindTransferMap( const double end_plane) const { // find the transfer map that transports a particle from the first plane // to the station that is nearest to the desired end_plane std::map::const_iterator transfer_map_entry; bool found_entry = false; for (transfer_map_entry = transfer_maps_.begin(); !found_entry && (transfer_map_entry != transfer_maps_.end()); ++transfer_map_entry) { // determine whether the station before or after end_plane is closest double station_plane = static_cast(transfer_map_entry->first); if (station_plane >= end_plane) { if ((transfer_map_entry == transfer_maps_.begin()) && (station_plane > end_plane)) { std::ostringstream message_buffer; message_buffer << "Mapping detectors are all positioned downstream " << "from one or more hits (Hit z: " << end_plane << ", First Station Plane: " << station_plane << ")."; throw(Exceptions::Exception(Exceptions::nonRecoverable, message_buffer.str(), "MAUS::TransferMapOpticsModel::GenerateTransferMap()")); } double after_delta = station_plane - end_plane; if (transfer_map_entry != transfer_maps_.begin()) { --transfer_map_entry; double before_delta = end_plane - static_cast(transfer_map_entry->first); if (after_delta < before_delta) { ++transfer_map_entry; } } // check for poorly configured mapping virtual detectors const double selection_error = ::abs(end_plane - static_cast(transfer_map_entry->first)); const double max_selection_error = 10.; // 1 cm if (selection_error > max_selection_error) { std::ostringstream message_buffer; message_buffer << "Difference in z-position between closest mapping " << setprecision(10) << "detector (z=" << transfer_map_entry->first << ") " << " and target detector " << "(z=" << end_plane << ") " << "exceeds tollerance for accurate mapping."; throw(Exceptions::Exception(Exceptions::nonRecoverable, message_buffer.str(), "MAUS::TransferMapOpticsModel::GenerateTransferMap()")); } found_entry = true; } } if (transfer_map_entry != transfer_maps_.begin()) { --transfer_map_entry; // compensate for the final increment in the loop } return transfer_map_entry->second; } const TransferMap * TransferMapOpticsModel::GenerateTransferMap( const double plane) const { if (!built_) { throw(Exceptions::Exception(Exceptions::nonRecoverable, "No transfer maps available since the optics model has not " "been built yet. Call Build() first.", "MAUS::TransferMapOpticsModel::GenerateTransferMap()")); } else if (transfer_maps_.size() == 0) { throw(Exceptions::Exception(Exceptions::nonRecoverable, "No transfer maps to choose from.", "MAUS::TransferMapOpticsModel::GenerateTransferMap()")); } return FindTransferMap(plane); } const std::vector TransferMapOpticsModel::Primaries() { std::vector primaries; primaries.push_back(reference_primary_); for (int coordinate_index = 0; coordinate_index < 6; ++coordinate_index) { Primary primary1 = reference_primary_; Primary primary2 = reference_primary_; switch (coordinate_index) { case 0: { primary1.SetTime(primary1.GetTime() + deltas_[coordinate_index]); primary2.SetTime(primary2.GetTime() - deltas_[coordinate_index]); break; } case 1: { primary1.SetEnergy(primary1.GetEnergy() + deltas_[coordinate_index]); primary2.SetEnergy(primary2.GetEnergy() - deltas_[coordinate_index]); break; } case 2: case 3: { ThreeVector position = primary1.GetPosition(); position[coordinate_index-2] += deltas_[coordinate_index]; primary1.SetPosition(position); position = primary2.GetPosition(); position[coordinate_index-2] -= deltas_[coordinate_index]; primary2.SetPosition(position); break; } case 4: case 5: { ThreeVector momentum = primary1.GetMomentum(); momentum[coordinate_index-4] += deltas_[coordinate_index]; primary1.SetMomentum(momentum); momentum = primary2.GetMomentum(); momentum[coordinate_index-4] -= deltas_[coordinate_index]; primary2.SetMomentum(momentum); break; } } primaries.push_back(primary1); primaries.push_back(primary2); } return primaries; } void TransferMapOpticsModel::MapStationsToHits( std::map > & station_hits, const Json::Value & event) { // Iterate through each event of the simulation const Json::Value hits = event["virtual_hits"]; std::cout << "DEBUG TransferMapOpticsModel::MapStationsToHits: " << "# virtual track hits: " << hits.size() << std::endl; for (size_t hit_index = 0; hit_index < hits.size(); ++hit_index) { const Json::Value hit = hits[Json::Value::ArrayIndex(hit_index)]; const PID particle_id = PID(hit["particle_id"].asInt()); const double mass = Particle::GetInstance().GetMass(particle_id); const double px = hit["momentum"]["x"].asDouble(); const double py = hit["momentum"]["y"].asDouble(); const double pz = hit["momentum"]["z"].asDouble(); const double momentum = ::sqrt(px*px + py*py + pz*pz); const double energy = ::sqrt(mass*mass + momentum*momentum); PhaseSpaceVector hit_vector( hit["time"].asDouble(), energy, hit["position"]["x"].asDouble(), px, hit["position"]["y"].asDouble(), py); double z = hit["position"]["z"].asDouble(); // Round to the nearest mm int64_t z_key = (z >= 0?static_cast(z+.5):static_cast(z-.5)); Squeak::mout(Squeak::debug) << "DEBUG TransferMapOpticsModel::MapStationsToHits: " << setprecision(8) << "z = " << z << "\tz_key = " << z_key << std::endl; station_hits[z_key].push_back(hit_vector); // Assuming the first hit is at the start plane, save the time offset // between what the reference primary specifies and what the simulation's // virtual detectors produce if (hit_index == 0) { const double start_plane_time = hit["time"].asDouble(); time_offset_ = start_plane_time - reference_primary_.GetTime(); } } } CovarianceMatrix TransferMapOpticsModel::Transport( const CovarianceMatrix & covariances, const double end_plane) const { TransferMap const * transfer_map = GenerateTransferMap(end_plane); if (transfer_map == NULL) { throw(Exceptions::Exception(Exceptions::nonRecoverable, "Got NULL transfer map.", "MAUS::TransferMapOpticsModel::Transport()")); } CovarianceMatrix transported_covariances = transfer_map->Transport(covariances); return transported_covariances; } CovarianceMatrix TransferMapOpticsModel::Transport( const CovarianceMatrix & covariances, const double start_plane, const double end_plane) const { const TransferMap * start_plane_map = FindTransferMap(start_plane); const TransferMap * end_plane_map = FindTransferMap(end_plane); const TransferMap * inverted_start_plane_map = start_plane_map->Inverse(); CovarianceMatrix transported_covariances = inverted_start_plane_map->Transport( end_plane_map->Transport(covariances)); delete inverted_start_plane_map; return transported_covariances; } PhaseSpaceVector TransferMapOpticsModel::Transport( const PhaseSpaceVector & vector, const double end_plane) const { TransferMap const * transfer_map = GenerateTransferMap(end_plane); if (transfer_map == NULL) { throw(Exceptions::Exception(Exceptions::nonRecoverable, "Got NULL transfer map.", "MAUS::TransferMapOpticsModel::Transport()")); } PhaseSpaceVector transported_vector = transfer_map->Transport(vector); return transported_vector; } PhaseSpaceVector TransferMapOpticsModel::Transport( const PhaseSpaceVector & vector, const double start_plane, const double end_plane) const { const TransferMap * start_plane_map = FindTransferMap(start_plane); const TransferMap * end_plane_map = FindTransferMap(end_plane); const TransferMap * inverted_start_plane_map = start_plane_map->Inverse(); PhaseSpaceVector transported_vector = inverted_start_plane_map->Transport( end_plane_map->Transport(vector)); delete inverted_start_plane_map; return transported_vector; } } // namespace MAUS