Commit 61d93f01 authored by Simon Spannagel's avatar Simon Spannagel
Browse files

Merge branch 'fix_laser' into 'master'

DepositionLaser: smarter use of the lookup table

Closes #262

See merge request allpix-squared/allpix-squared!959
parents f204c141 93bce0f5
Loading
Loading
Loading
Loading
+61 −16
Original line number Diff line number Diff line
@@ -85,41 +85,86 @@ DepositionLaserModule::DepositionLaserModule(Configuration& config, Messenger* m
        throw InvalidValueError(config_, "pulse_duration_", "Pulse should be a positive value");
    }

    // FIXME less hardcoded values
    // Select user optics or silicon absorption lookup:
    is_user_optics_ = (config_.count({"absorption_length", "refractive_index"}) == 2);

    if(config_.count({"absorption_length", "refractive_index", "wavelength"}) == 3) {
        throw InvalidCombinationError(config_,
                                      {"absorption_length", "refractive_index", "wavelength"},
                                      "User definition for optical parameters and wavelength are mutually exclusive!");
    }

    if(is_user_optics_) {
        absorption_length_ = config_.get<double>("absorption_length");
        refractive_index_ = config_.get<double>("refractive_index");
        LOG(DEBUG) << "Setting user-defined optical properties for sensor material";
    } else {
        wavelength_ = config_.get<double>("wavelength");
        if(Units::convert(wavelength_, "nm") < 250 || Units::convert(wavelength_, "nm") > 1450) {
            throw InvalidValueError(config_, "wavelength", "Currently supported wavelengths are 250 -- 1450 nm");
        }

        // Register lookup path for data files:
        if(config_.has("data_path")) {
            auto path = config_.getPath("data_path", true);
            if(!std::filesystem::is_directory(path)) {
                throw InvalidValueError(config_, "data_path", "path does not point to a directory");
            }
            LOG(TRACE) << "Registered absorption data path from configuration: " << path;
        } else {
            if(std::filesystem::is_directory(ALLPIX_LASER_DATA_DIRECTORY)) {
                config_.set<std::string>("data_path", ALLPIX_LASER_DATA_DIRECTORY);
                LOG(TRACE) << "Registered absorption data path from system: " << ALLPIX_LASER_DATA_DIRECTORY;
            } else {
                const char* data_dirs_env = std::getenv("XDG_DATA_DIRS");
                if(data_dirs_env == nullptr || strlen(data_dirs_env) == 0) {
                    data_dirs_env = "/usr/local/share/:/usr/share/:";
                }

                auto data_dirs = split<std::filesystem::path>(data_dirs_env, ":");
                for(auto data_dir : data_dirs) {
                    data_dir /= std::filesystem::path(ALLPIX_PROJECT_NAME) / "data";
                    if(std::filesystem::is_directory(data_dir)) {
                        config_.set<std::string>("data_path", data_dir);
                        LOG(TRACE) << "Registered absorption data path from XDG_DATA_DIRS: " << data_dir;
                    } else {
                        throw ModuleError(
                            "Cannot find absorption data files, provide them in the configuration, via XDG_DATA_DIRS or "
                            "in system directory " +
                            std::string(ALLPIX_LASER_DATA_DIRECTORY));
                    }
                }
            }
        }
    }

    config_.setDefault<bool>("output_plots", false);
    output_plots_ = config.get<bool>("output_plots");
}

void DepositionLaserModule::initialize() {
    // Check if there are user-specified optical properties for materials
    is_user_optics_ = (config_.count({"absorption_length", "refractive_index"}) == 2);
    if(is_user_optics_) {
        absorption_length_ = config_.get<double>("absorption_length");
        refractive_index_ = config_.get<double>("refractive_index");
        LOG(DEBUG) << "Setting user-defined optical properties for sensor material";
    } else {
        // Load data
        std::string laser_data_path = ALLPIX_LASER_DATA_DIRECTORY;
        std::ifstream f(std::filesystem::path(laser_data_path) / "silicon_photoabsorption.data");
    if(!is_user_optics_) {
        // wavelength: {absorption_length, refractive_index}
        std::map<double, std::pair<double, double>> optics_lut;
        double wl = 0;
        double abs_length = 0;
        double refr_ind = 0;

        // Load data
        auto file_path = config_.getPath("data_path", true) / "silicon_photoabsorption.data";
        std::ifstream f(file_path);
        LOG(DEBUG) << "Loading optical properties for sensor material from LUT: " << std::endl << file_path.string();

        if(!f || !std::filesystem::is_regular_file(file_path)) {
            throw ModuleError("Could not open optical properties reference file at \"" + file_path.string() + "\"");
        }

        while(f >> wl >> abs_length >> refr_ind) {
            optics_lut[Units::get(wl, "nm")] = {abs_length, refr_ind};
        }

        LOG(DEBUG) << "Loading optical properties for sensor material from LUT: " << laser_data_path;

        // Find or interpolate absorption depth for given wavelength

        if(optics_lut.count(wavelength_) != 0) {
            absorption_length_ = optics_lut[wavelength_].first;
            refractive_index_ = optics_lut[wavelength_].second;
+1 −1
Original line number Diff line number Diff line
@@ -129,7 +129,7 @@ namespace allpix {
        double focal_distance_;

        size_t number_of_photons_;
        double wavelength_;
        double wavelength_{0.};
        double absorption_length_{0.};
        double refractive_index_{0.};
        double pulse_duration_;
+4 −2
Original line number Diff line number Diff line
@@ -55,7 +55,9 @@ and temporal distribution.

* `number_of_photons`: number of incident photons, generated in *one* event. Defaults to 10000. The total deposited charge
  will also depend on wavelength and geometry.
* `wavelength` of the laser. Supported values are 250 -- 1450 nm.
* `wavelength` of the laser. If specified, it is used to retrieve sensor optical properties from the lookup table (data is available for the range of 250 -- 1450 nm). The only supported material is silicon.
* `data_path`: Directory to read the tabulated input data for the absorption on silicon. By default, this is the standard installation path of the data files shipped with the framework.
* `absorption_length` and `refractive_index`: if both are specified, given values are used instead of the lookup table. This also allows use of sensor materials other than silicon.
* `pulse_duration`: gaussian width of pulse temporal profile. Defaults to 0.5 ns.
* `source_position`: a 3D position vector.
* `beam_direction`: a 3D direction vector.
@@ -66,7 +68,7 @@ and temporal distribution.
  source.
* `beam_convergence_angle`: max angle between tracks and `beam_direction`. Needs to be specified for a `converging` beam.
* `output_plots`: if set `true`, this module will produce histograms to monitor beam shape and also 3D distributions of charges, deposited in each detector. Histograms would look sensible even for one-event runs. Defaults to `false`.
* `absorption_length` and `refractive_index`: if both are specified, they override corresponding values from the lookup table. This also allows use of sensor materials other than silicon.


## Usage
A simulation pipeline to build an analog detector response would include `DepositionLaser`, `TransientPropagation` and
+1 −2
Original line number Diff line number Diff line
@@ -14,10 +14,9 @@ beam_geometry = "cylindrical"
number_of_photons = 1
source_position = 0 0 0
beam_direction = 0 0 1
wavelength = 1064nm
absorption_length = 1mm
refractive_index = 2.5


#PASS Wavelength = 1064nm, absorption length: 1mm, refractive index: 2.5
#PASS Wavelength = 0nm, absorption length: 1mm, refractive index: 2.5
#FAIL Detector d1 has unsupported material and will be ignored