Page MenuHomeHEPForge

No OneTemporary

diff --git a/config_generator/include/HEJC/HEJC.hh b/config_generator/include/HEJC/HEJC.hh
index db67d32..7e91b4e 100644
--- a/config_generator/include/HEJC/HEJC.hh
+++ b/config_generator/include/HEJC/HEJC.hh
@@ -1,116 +1,137 @@
/**
* \authors The HEJ collaboration (see AUTHORS for details)
* \date 2023
* \copyright GPLv2 or later
*
* Contains the main class representing a run of the HEJC program.
*/
#pragma once
#include <cstdint>
#include <filesystem>
#include <iterator>
#include <memory>
+#include <random>
#include <type_traits>
#include <yaml-cpp/yaml.h>
#include "HEJC/Config.hh"
#include "HEJC/Process.hh"
namespace HEJC {
namespace detail {
template<class Iterator>
YAML::Node combine_yaml_files(Iterator begin, Iterator end) {
YAML::Node res;
for(auto it = begin; it != end; ++it) {
const std::filesystem::path file{*it};
YAML::Node yaml = YAML::LoadFile(file);
if(!yaml.IsMap()) {
throw std::invalid_argument{
file.string() + " does not contain a YAML map"
};
}
for(auto && entry: std::move(yaml)) {
res[std::move(entry.first)] = std::move(entry.second);
}
}
return res;
}
}
//! Main class for writing HEJ and fixed-order generator configurations
class HEJC {
public:
//! Construct with the settings in `config_file`
explicit HEJC(std::filesystem::path const & config_file);
//! Construct with the combined settings from a range of input files
template<class Iterator,
// ensure that Iterator points to something that can be converted to a path
std::enable_if_t<
std::is_constructible_v<
std::filesystem::path,
typename std::iterator_traits<Iterator>::value_type
>,
bool
> = true
>
HEJC(Iterator begin, Iterator end):
HEJC{detail::combine_yaml_files(begin, end)}
{}
//! Construct with the given YAML settings
explicit HEJC(YAML::Node && config);
//! Construct with the given settings
explicit HEJC(Config const & config);
//! Write configuration files to the directory `target_dir`
void write_config(std::filesystem::path const & target_dir) const;
private:
- void write_HEJ(std::filesystem::path const & target_dir) const;
- void write_HEJ_default(std::filesystem::path const & target_dir) const;
- void write_HEJ_lowpt(std::filesystem::path const & target_dir) const;
- void write_HEJ_with_HEJFOG(std::filesystem::path const & target_dir) const;
+ using Rng = std::ranlux48;
+
+ void write_HEJ(
+ std::filesystem::path const & target_dir,
+ Rng & rng
+ ) const;
+ void write_HEJ_default(
+ std::filesystem::path const & target_dir,
+ Rng & rng
+ ) const;
+ void write_HEJ_lowpt(
+ std::filesystem::path const & target_dir,
+ Rng & rng
+ ) const;
+ void write_HEJ_with_HEJFOG(
+ std::filesystem::path const & target_dir,
+ Rng & rng
+ ) const;
void write_HEJ_NLO(
std::filesystem::path const & target_dir,
- std::uint32_t njets
+ std::uint32_t njets,
+ Rng & rng
) const;
void write_HEJ_NLO_lowpt(
std::filesystem::path const & target_dir,
- std::uint32_t njets
+ std::uint32_t njets,
+ Rng & rng
) const;
void write_FO_config(
GeneratorConfig const & conf,
- std::filesystem::path const & target_dir
+ std::filesystem::path const & target_dir,
+ Rng & rng
) const;
void write_HEJFOG(
Range<std::uint32_t> multiplicities,
YAML::Node const & config,
- std::filesystem::path const & target_dir
+ std::filesystem::path const & target_dir,
+ Rng & rng
) const;
void write_Sherpa2(
Range<std::uint32_t> multiplicities,
YAML::Node const & config,
- std::filesystem::path const & target_dir
+ std::filesystem::path const & target_dir,
+ Rng & rng
) const;
void write_Sherpa2NLO(
Range<std::uint32_t> multiplicities,
YAML::Node const & config,
- std::filesystem::path const & target_dir
+ std::filesystem::path const & target_dir,
+ Rng & rng
) const;
YAML::Node gen_Sherpa2_config(YAML::Node const & base) const;
std::string gen_Sherpa2_jet_def(
std::string njets,
double min_jet_pt
) const;
std::uint64_t runs_;
Process process_;
std::string pdf_;
Beam beam_;
YAML::Node hej_config_;
std::vector<GeneratorConfig> fo_configs_;
};
}
diff --git a/config_generator/src/HEJC.cc b/config_generator/src/HEJC.cc
index a707bbc..75170a4 100644
--- a/config_generator/src/HEJC.cc
+++ b/config_generator/src/HEJC.cc
@@ -1,558 +1,575 @@
#include "HEJC/HEJC.hh"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <filesystem>
#include <fstream>
+#include <functional>
#include <limits>
#include <random>
#include <string>
#include "HEJ/exceptions.hh"
#include "HEJ/Config.hh"
#include "HEJ/PDG_codes.hh"
#include "HEJ/YAMLreader.hh"
#include "yaml-cpp/yaml.h"
#include "HEJC/Config.hh"
#include "HEJC/Generators.hh"
#include "HEJC/Utility.hh"
namespace fs = std::filesystem;
namespace HEJC {
namespace {
+ using Rng = std::ranlux48;
constexpr double low_pt_frac = 0.8;
- void update_HEJ_seed(YAML::Node & node) {
- std::random_device ran;
+ void update_HEJ_seed(
+ YAML::Node & node,
+ Rng & rng
+ ) {
std::uniform_int_distribution<std::uint32_t> dist{
0,
std::numeric_limits<std::uint32_t>::max()
};
if(node["random generator"]["name"].as<std::string>() == "mixmax") {
- node["random generator"]["seed"] = dist(ran);
+ node["random generator"]["seed"] = dist(rng);
}
}
void update_HEJ_jets(YAML::Node & node) {
node["jets"].remove("max rapidity");
node["resummation jets"] = node["jets"];
node["fixed order jets"] = clone(node["jets"]);
node.remove("jets");
}
- void update_HEJ(YAML::Node & node) {
- update_HEJ_seed(node);
+ void update_HEJ(YAML::Node & node, Rng & rng) {
+ update_HEJ_seed(node, rng);
update_HEJ_jets(node);
}
// HEJC generates a number of entries in the HEJ config
// if they already exist this is a user mistake
void assert_no_unused_entries(const YAML::Node & yaml) {
for(auto entry: {
"fixed order jets",
"resummation jets",
"NLO truncation",
"require low pt jet"
}) {
if(yaml[entry]) {
throw HEJ::unknown_option{entry};
}
}
}
void validate_HEJ_config(YAML::Node && yaml) {
assert_no_unused_entries(yaml);
// check it's a valid HEJ config after adding the entries
// we will generate when writing to a file
if(!yaml["jets"]) {
throw HEJ::missing_option{"jets"};
}
if(!yaml["jets"]["max rapidity"]) {
throw HEJ::missing_option{"jets: max rapidity"};
}
- update_HEJ(yaml);
+ update_HEJ_jets(yaml);
yaml.remove("pdf");
yaml.remove("beams");
yaml["NLO truncation"]["enabled"] = false;
yaml["require low pt jet"] = false;
HEJ::to_Config(yaml);
}
void validate_HEJFOG_config(YAML::Node const & yaml) {
// TODO: would be nice to check against HEJFOG's `to_Config`,
// but that is not public
// TODO: add decays?
for(auto opt: {
"jets",
"process",
"vev",
"particle properties",
"Higgs coupling",
"pdf",
"beam"
}) {
if(yaml[opt]) {
throw HEJ::unknown_option{
"'" + std::string{opt} + "' in HEJFOG configuration"
};
}
}
}
void validate_Sherpa2_config(YAML::Node const & yaml) {
if(yaml["processes"]) {
throw HEJ::unknown_option{
"'processes' in Sherpa2 configuration"
};
}
for(auto opt: {
"PDF_LIBRARY",
"PDF_SET",
"USE_PDF_ALPHAS",
"BEAM_1",
"BEAM_2",
"FRAGMENTATION",
"YFS_MODE",
"MI_HANDLER",
"CSS_MAXEM",
"BEAM_REMNANTS",
"MASSIVE[4]",
"MASS[4]",
"MASSIVE[5]",
"MASS[5]",
"MASS[23]",
"MASS[23]",
"WIDTH[23]",
"MASS[24]",
"WIDTH[24]",
"MASS[25]",
"WIDTH[25]"
}) {
if(yaml["run"][opt]) {
throw HEJ::unknown_option{
"'" + std::string{opt} + "' in Sherpa2 configuration"
};
}
}
}
void validate_Sherpa2NLO_config(YAML::Node const & yaml) {
validate_Sherpa2_config(yaml);
}
void validate_FO_config(GeneratorConfig const & config) {
switch(config.generator) {
case HEJFOG:
return validate_HEJFOG_config(config.config);
case Sherpa2:
return validate_Sherpa2_config(config.config);
case Sherpa2NLO:
return validate_Sherpa2NLO_config(config.config);
}
}
void validate_config(Config const & config) {
validate_HEJ_config(clone(config.hej_config));
if(config.fo_configs.empty()) {
throw HEJ::missing_option{
"Need to specify at least one fixed-order generator"
};
}
for(auto const & config: config.fo_configs) {
validate_FO_config(config);
}
}
struct Sherpa2Format {
YAML::Node const & conf;
};
std::ostream & operator<<(std::ostream & out, Sherpa2Format f) {
// we need to convert all YAML nodes to `std::string`
// to avoid bad formatting of multiline strings
out << "(run){\n";
for(auto const & entry: f.conf["run"]) {
out << " " << entry.first.as<std::string>()
<< ' ' << entry.second.as<std::string>() << ";\n";
}
out << "}(run);\n\n"
"(processes){\n"
" Process " << f.conf["processes"].as<std::string>() << ";\n"
" End process\n"
"}(processes);\n\n"
"(selector){\n";
for(auto const & entry: f.conf["selector"]) {
out << " " << entry.as<std::string>() << ";\n";
}
out << "}(selector);\n";
if(f.conf["analysis"]) {
out << "\n(analysis){\n";
for(auto const & entry: f.conf["analysis"]) {
out << " " << entry.as<std::string>() << ";\n";
}
out << "}(analysis);\n";
}
return out;
}
std::string to_Sherpa_str(HEJ::ParticleID id) {
if(id == HEJ::pid::proton || id == HEJ::pid::antiproton) {
return "93";
}
return std::to_string(id);
}
}
HEJC::HEJC(fs::path const & config_file):
HEJC{YAML::LoadFile(config_file)}
{}
HEJC::HEJC(YAML::Node && config):
HEJC{into_Config(std::move(config))}
{}
HEJC::HEJC(Config const & config):
runs_{config.runs},
process_{config.process},
pdf_{config.pdf},
beam_{config.beam},
hej_config_{config.hej_config},
fo_configs_{config.fo_configs}
{
validate_config(config);
}
void HEJC::write_config(fs::path const & base_dir) const {
// from this point on the only possible errors should be I/O failures,
// we don't want to produce half-broken output if we can avoid it
+ Rng rng{};
+ rng.seed(1);
fs::create_directories(base_dir);
for(std::uint64_t run = 0; run < runs_; ++run) {
const fs::path subdir = "run_" + std::to_string(run);
const fs::path target_dir = base_dir/subdir;
fs::create_directories(target_dir);
- write_HEJ(target_dir);
+ write_HEJ(target_dir, rng);
for(auto const & fo_config: fo_configs_) {
- write_FO_config(fo_config, target_dir);
+ write_FO_config(fo_config, target_dir, rng);
}
}
}
- void HEJC::write_HEJ(fs::path const & target_dir) const {
+ void HEJC::write_HEJ(fs::path const & target_dir, Rng & rng) const {
const bool use_HEJFOG = std::any_of(
fo_configs_.begin(), fo_configs_.end(),
[](auto const & gen) { return gen.generator == Generator::HEJFOG; }
);
const bool need_lowpt = std::any_of(
fo_configs_.begin(), fo_configs_.end(),
[](auto const & gen) { return gen.generator != Generator::HEJFOG; }
);
if(use_HEJFOG) {
- write_HEJ_with_HEJFOG(target_dir);
+ write_HEJ_with_HEJFOG(target_dir, rng);
}
if(need_lowpt) {
- write_HEJ_default(target_dir);
- write_HEJ_lowpt(target_dir);
+ write_HEJ_default(target_dir, rng);
+ write_HEJ_lowpt(target_dir, rng);
}
std::vector<Range<std::uint32_t>> NLO_multiplicities;
for(auto const & fo_config: fo_configs_) {
if(is_NLO(fo_config.generator)) {
NLO_multiplicities.emplace_back(fo_config.multiplicities);
}
}
for(const auto multiplicities: NLO_multiplicities) {
for(const std::uint32_t njets: multiplicities) {
- write_HEJ_NLO(target_dir, njets);
- write_HEJ_NLO_lowpt(target_dir, njets);
+ write_HEJ_NLO(target_dir, njets, rng);
+ write_HEJ_NLO_lowpt(target_dir, njets, rng);
}
}
}
- void HEJC::write_HEJ_default(fs::path const & target_dir) const {
+ void HEJC::write_HEJ_default(fs::path const & target_dir, Rng & rng) const {
YAML::Node conf = clone(hej_config_);
- update_HEJ(conf);
+ update_HEJ(conf, rng);
conf["NLO truncation"]["enabled"] = false;
conf["require low pt jet"] = false;
std::ofstream out{target_dir/"config_HEJ.yml"};
out << conf;
}
- void HEJC::write_HEJ_lowpt(fs::path const & target_dir) const {
+ void HEJC::write_HEJ_lowpt(fs::path const & target_dir, Rng & rng) const {
YAML::Node conf = clone(hej_config_);
const auto min_jet_pt = conf["jets"]["min pt"].as<double>();
- update_HEJ(conf);
+ update_HEJ(conf, rng);
conf["fixed order jets"]["min pt"] = low_pt_frac * min_jet_pt;
conf["require low pt jet"] = true;
conf["NLO truncation"]["enabled"] = false;
if(conf["event treatment"]["non-resummable"].as<std::string>() == "keep") {
conf["event treatment"]["non-resummable"] = "discard";
}
std::ofstream out{target_dir/"config_HEJ_lowpt.yml"};
out << conf;
}
- void HEJC::write_HEJ_with_HEJFOG(fs::path const & target_dir) const {
+ void HEJC::write_HEJ_with_HEJFOG(fs::path const & target_dir, Rng & rng) const {
YAML::Node conf = clone(hej_config_);
const auto min_jet_pt = conf["jets"]["min pt"].as<double>();
- update_HEJ(conf);
+ update_HEJ(conf, rng);
conf["fixed order jets"]["min pt"] = low_pt_frac * min_jet_pt;
conf["require low pt jet"] = false;
conf["NLO truncation"]["enabled"] = false;
std::ofstream out{target_dir/"config_HEJ_with_HEJFOG.yml"};
out << conf;
}
void HEJC::write_HEJ_NLO(
fs::path const & target_dir,
- const std::uint32_t njets
+ const std::uint32_t njets,
+ Rng & rng
) const {
YAML::Node conf = clone(hej_config_);
- update_HEJ(conf);
+ update_HEJ(conf, rng);
conf["NLO truncation"]["enabled"] = true;
conf["NLO truncation"]["nlo order"] = njets;
conf["require low pt jet"] = false;
const std::string filename =
"config_HEJ_NLO_" + std::to_string(njets) + "j.yml";
std::ofstream out{target_dir/filename};
out << conf;
}
void HEJC::write_HEJ_NLO_lowpt(
fs::path const & target_dir,
- const std::uint32_t njets
+ const std::uint32_t njets,
+ Rng & rng
) const {
YAML::Node conf = clone(hej_config_);
const auto min_jet_pt = conf["jets"]["min pt"].as<double>();
- update_HEJ(conf);
+ update_HEJ(conf, rng);
conf["fixed order jets"]["min pt"] = low_pt_frac * min_jet_pt;
conf["require low pt jet"] = true;
conf["NLO truncation"]["enabled"] = true;
conf["NLO truncation"]["nlo order"] = njets;
const std::string filename =
"config_HEJ_NLO_" + std::to_string(njets) + "j_lowpt.yml";
std::ofstream out{target_dir/filename};
out << conf;
}
void HEJC::write_FO_config(
GeneratorConfig const & conf,
- fs::path const & target_dir
+ fs::path const & target_dir,
+ Rng & rng
) const {
switch(conf.generator) {
case HEJFOG:
return write_HEJFOG(
conf.multiplicities,
conf.config,
- target_dir
+ target_dir,
+ rng
);
case Sherpa2:
return write_Sherpa2(
conf.multiplicities,
conf.config,
- target_dir
+ target_dir,
+ rng
);
case Sherpa2NLO:
return write_Sherpa2NLO(
conf.multiplicities,
conf.config,
- target_dir
+ target_dir,
+ rng
);
}
}
void HEJC::write_HEJFOG(
Range<std::uint32_t> multiplicities,
YAML::Node const & c,
- std::filesystem::path const & target_dir
+ std::filesystem::path const & target_dir,
+ Rng & rng
) const {
YAML::Node config = clone(c);
config["pdf"] = pdf_;
config["beam"]["energy"] = beam_.energy;
config["beam"]["particles"][0] = name(beam_.particles[0]);
config["beam"]["particles"][1] = name(beam_.particles[1]);
const auto min_jet_pt = hej_config_["jets"]["min pt"].as<double>();
config["jets"] = hej_config_["jets"];
config["jets"]["min pt"] = low_pt_frac * min_jet_pt;
config["jets"]["peak pt"] = min_jet_pt;
config["vev"] = hej_config_["vev"];
config["particle properties"] = hej_config_["particle properties"];
if(hej_config_["Higgs coupling"]) {
config["Higgs coupling"] = hej_config_["Higgs coupling"];
}
std::string proc_str =
name(process_.incoming[0]) + ' ' + name(process_.incoming[1])
+ " =>";
for(HEJ::ParticleID out: process_.outgoing) {
proc_str += ' ' + name(out);
}
proc_str += ' ';
for(const std::uint32_t njets: multiplicities) {
const std::string njet_str = std::to_string(njets);
config["process"] = proc_str + njet_str + 'j';
- update_HEJ_seed(config);
+ update_HEJ_seed(config, rng);
const std::string filename = "config_HEJFOG_" + njet_str + "j.yml";
std::ofstream out{target_dir/filename};
out << config;
}
}
- void update_Sherpa2_seed(YAML::Node & node) {
- std::random_device ran;
+ void update_Sherpa2_seed(
+ YAML::Node & node,
+ Rng & rng
+ ) {
std::uniform_int_distribution<std::uint32_t> dist{
0,
std::numeric_limits<std::uint32_t>::max()
};
- node["run"]["RANDOM_SEED"] = dist(ran);
+ node["run"]["RANDOM_SEED"] = dist(rng);
}
YAML::Node HEJC::gen_Sherpa2_config(YAML::Node const & base) const {
YAML::Node config = clone(base);
config["run"]["FRAGMENTATION"] = "Off";
config["run"]["YFS_MODE"] = 0;
config["run"]["MI_HANDLER"] = "None";
config["run"]["CSS_MAXEM"] = 0;
config["run"]["BEAM_REMNANTS"] = 0;
config["run"]["PDF_LIBRARY"] = "LHAPDFSherpa";
config["run"]["USE_PDF_ALPHAS"] = 1;
config["run"]["PDF_SET"] = pdf_;
config["run"]["MASSIVE[4]"] = 0;
config["run"]["MASS[4]"] = 0.;
config["run"]["MASSIVE[5]"] = 0;
config["run"]["MASS[5]"] = 0.;
const std::string E_beam = std::to_string(beam_.energy);
static constexpr std::array<std::string_view, 3> ew_boson_names = {
"Z", "W", "Higgs"
};
for(int id = 23; id <= 25; ++id) {
const std::string id_str = std::to_string(id);
const std::string mass_str = "MASS[" + id_str + "]";
const std::string width_str = "WIDTH[" + id_str + "]";
const auto name = std::string{ew_boson_names[id - 23]};
config["run"][mass_str] =
hej_config_["particle properties"][name]["mass"];
config["run"][width_str] =
hej_config_["particle properties"][name]["width"];
}
config["run"]["BEAM_1"] = std::to_string(beam_.particles[0]) + ' ' + E_beam;
config["run"]["BEAM_2"] = std::to_string(beam_.particles[1]) + ' ' + E_beam;
if(!config["run"]["ME_SIGNAL_GENERATOR"]) {
config["run"]["ME_SIGNAL_GENERATOR"] = "Comix,Amegic";
}
return config;
}
std::string HEJC::gen_Sherpa2_jet_def(
std::string njets,
const double min_jet_pt
) const {
return "FastjetFinder "
+ hej_config_["jets"]["algorithm"].as<std::string>()
+ ' ' + njets
+ ' ' + std::to_string(min_jet_pt)
+ " 0 " + hej_config_["jets"]["R"].as<std::string>()
+ " 0.75 " + hej_config_["jets"]["max rapidity"].as<std::string>();
}
void HEJC::write_Sherpa2(
Range<std::uint32_t> multiplicities,
YAML::Node const & c,
- std::filesystem::path const & target_dir
+ std::filesystem::path const & target_dir,
+ Rng & rng
) const {
YAML::Node config = gen_Sherpa2_config(c);
std::string proc_str =
to_Sherpa_str(process_.incoming[0]) + ' ' + to_Sherpa_str(process_.incoming[1])
+ " ->";
for(HEJ::ParticleID out: process_.outgoing) {
proc_str += ' ' + std::to_string(out);
}
for(const std::uint32_t njets: multiplicities) {
std::string proc_nj = proc_str;
for(std::uint32_t i = 0; i < njets; ++i) {
proc_nj += " 93";
}
config["processes"] = proc_nj;
const YAML::Node old_selector = clone(config["selector"]);
const std::string njet_str = std::to_string(njets);
const std::string jet_def = gen_Sherpa2_jet_def(
njet_str, hej_config_["jets"]["min pt"].as<double>()
);
config["selector"].push_back(jet_def);
{
const std::string filename = "Run_" + njet_str + "j.dat";
std::ofstream out{target_dir/filename};
- update_Sherpa2_seed(config);
+ update_Sherpa2_seed(config, rng);
out << Sherpa2Format{config};
}
config["selector"] = old_selector;
const std::string low_pt_jet_def = gen_Sherpa2_jet_def(
njet_str, low_pt_frac * hej_config_["jets"]["min pt"].as<double>()
);
config["selector"].push_back(low_pt_jet_def);
const std::string filename = "Run_" + njet_str + "j_lowpt.dat";
std::ofstream out{target_dir/filename};
- update_Sherpa2_seed(config);
+ update_Sherpa2_seed(config, rng);
out << Sherpa2Format{config};
config["selector"] = old_selector;
}
}
void HEJC::write_Sherpa2NLO(
Range<std::uint32_t> multiplicities,
YAML::Node const & c,
- std::filesystem::path const & target_dir
+ std::filesystem::path const & target_dir,
+ Rng & rng
) const {
// TODO: code duplication with `write_Sherpa2`
YAML::Node config = gen_Sherpa2_config(c);
assert(config["run"]["ME_SIGNAL_GENERATOR"]);
const auto me_gen = config["run"]["ME_SIGNAL_GENERATOR"].as<std::string>();
if(me_gen.find("OpenLoops") == me_gen.npos) {
config["run"]["ME_SIGNAL_GENERATOR"] = me_gen + ",OpenLoops";
}
std::string proc_str =
to_Sherpa_str(process_.incoming[0]) + ' ' + to_Sherpa_str(process_.incoming[1])
+ " ->";
for(HEJ::ParticleID out: process_.outgoing) {
proc_str += ' ' + std::to_string(out);
}
for(const std::uint32_t njets: multiplicities) {
std::string proc_nj = proc_str;
for(std::uint32_t i = 0; i < njets; ++i) {
proc_nj += " 93";
}
// evil hack: append mode & generator to process
config["processes"] = proc_nj + ";\n"
" NLO_QCD_Mode Fixed_Order;\n"
" Loop_Generator OpenLoops";
const YAML::Node old_selector = clone(config["selector"]);
const std::string njet_str = std::to_string(njets);
const std::string jet_def = gen_Sherpa2_jet_def(
njet_str, hej_config_["jets"]["min pt"].as<double>()
);
config["selector"].push_back(jet_def);
- update_Sherpa2_seed(config);
+ update_Sherpa2_seed(config, rng);
const std::string filename = "Run_NLO_" + njet_str + "j.dat";
std::ofstream out{target_dir/filename};
out << Sherpa2Format{config};
config["selector"] = old_selector;
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, May 3, 6:25 AM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4983017
Default Alt Text
(24 KB)

Event Timeline