// // couples_gen.C - load four already-combined, per-orientation hmu topographs // (as produced by combine_half_target_scans() / fill_from_complementary_scan() // in combiner.py) into a Couples object, using setmap() to bypass the // file-based read/cleanup/transform pipeline that Couples::getmap() would // otherwise perform. Applies Center() to each map beforehand so the // diamond silhouette is centered in the frame (no automatic rotation -- // squaring the edges to the x/y axes is done interactively via lineup(), // using the R/L/U/D/P/N/I/O/S/T keys). // // This file is meant to be the permanent record of exactly which scan pairs // were assigned to which physical orientation slot for each sample, so that // a couples.root file can always be regenerated from the original results.root // files without depending on anything typed at an interactive prompt. // // author: richard.t.jones at uconn.edu // version: july 29, 2026 // // usage: root [0] .L Map2D.cc+O // root [1] .L Couples.C+O // root [2] .L couples_gen.C+O // root [3] redo_couples_2023(); // or root [3] process_sample("JD80-211"); // #include #include #include #include #include #include "Map2D.h" #include "Couples.h" TString xroot = "root://nod25.phys.uconn.edu//Gluex/beamline/diamonds"; TString xpath = "%s/%s/results/%s_%s_results.root"; // Common frame settings applied to all four Couples slots. Edit these to // match the actual physical extent of the combined, centered topographs. Double_t xrange_mm = 7.5; Double_t yrange_mm = 7.5; Int_t xpixels_common = 1500; Int_t ypixels_common = 1500; TString period_2023 = "cls-6-2023"; // Permanent record of the orientation-to-scan-pair assignment for each // sample from run period cls-6-2023, keyed by sample name. Each value is // a 4-vector of scantag strings in slot order (\X, /X, X/, X\). // // TODO: fill in or adjust the actual scantag values below for each sample. std::map > orientation_scantags_2023 = { {"JD80-211", {"011,012", "021,020", "030,031,011,012", "041,040"}}, {"JD80-212", {"011,010,032,033", "020,021,040,041", "032,033,011,010", "040,041"}}, {"JD80-213", {"011,010,032,031", "021,020", "032,031,011,010", "041,042"}}, }; TString resultsfile(const char *period, const char *sample, const char *scantag) { // Build the xrootd path to a combined-topograph results file, given the // scan-number tag string used in its filename (e.g. "011,012" for a // combine_half_target_scans() output, or "030,031,011,012" for a // fill_from_complementary_scan() output). return TString::Format(xpath.Data(), xroot.Data(), period, sample, scantag); } Map2D *load_hmu(const char *rfile, const char *keyname="hmu") { // Open a combined-topograph results file (as written by // combine_half_target_scans() or fill_from_complementary_scan() in // combiner.py) and return a fresh Map2D copy of the named key // (default "hmu"). TFile *fin = TFile::Open(rfile); if (fin == 0 || fin->IsZombie()) { printf("Error in load_hmu - could not open %s\n", rfile); return 0; } TH2D *h = (TH2D*)fin->Get(keyname); if (h == 0) { printf("Error in load_hmu - no object named '%s' found in %s\n", keyname, rfile); return 0; } return new Map2D(*h); } Couples *build_couples(const char *sample, const char *period, const char *scantag0, const char *scantag1, const char *scantag2, const char *scantag3) { // Build a Couples object for the given sample/period from four already- // combined per-orientation hmu topograph files, one per slot: // slot 0 = \X, slot 1 = /X, slot 2 = X/, slot 3 = X\ // Each scantag argument is the scan-number tag string (e.g. "011,012") // used in the corresponding combined-topograph filename. Each input file // is expected to already be a cleaned, aligned, physically scaled // topograph (as produced upstream by combiner.py), containing a "hmu" // key. Returns the populated Couples object. const char *scantags[4] = {scantag0, scantag1, scantag2, scantag3}; Map2D *maps[4]; for (int p=0; p<4; ++p) { TString rfile = resultsfile(period, sample, scantags[p]); maps[p] = load_hmu(rfile.Data()); if (maps[p] == 0) { printf("Error in build_couples - failed to load slot %d " "for sample %s, cannot continue.\n", p, sample); return 0; } maps[p]->Center(); } TString title = TString::Format("%s from %s", sample, period); Couples *coup = new Couples("coup", title.Data()); coup->setxrange(xrange_mm, -xrange_mm); coup->setyrange(yrange_mm, -yrange_mm); coup->setresol(xpixels_common, ypixels_common); for (int p=0; p<4; ++p) { coup->setmap(p, maps[p], "hmu"); delete maps[p]; } return coup; } TString run_couples(Couples *coup, const char *sample, const char *name="hmu") { // Open _couples.root as the active output file, run fitandsave() // on the given Couples object (which internally calls surface(), // building and saving surface10/surface32/surfacediff/diff02/diff13 and // all their PNG/profile companions), write the Couples object itself, // and close the file. Call this only after the two orientation pairs // (slots 0,1 and slots 2,3) have already been visually aligned via // lineup(0,1) and lineup(2,3). TString outname = TString::Format("%s_couples.root", sample); TFile *fout = new TFile(outname.Data(), "RECREATE"); coup->fitandsave(name); coup->Write("coup"); fout->Close(); return outname; } Couples *process_sample(const char *sample, const char *period=0, const char *name="hmu") { // Full permanent driver for one sample: builds the Couples object from // the orientation_scantags_2023 mapping, draws the four current images, // invokes the interactive lineup() alignment for both orientation pairs // (slots 0,1 and slots 2,3), then runs fitandsave() and writes the // result to _couples.root. This is the single entry point meant // to be called for each sample; nothing about the sequence of steps // needs to be remembered or re-typed at the interactive prompt. // // Returns the populated Couples object, after the output file has // already been written to disk. if (period == 0) period = period_2023.Data(); auto it = orientation_scantags_2023.find(sample); if (it == orientation_scantags_2023.end() || it->second.size() != 4) { printf("Error in process_sample - orientation_scantags_2023[%s] " "is missing or incomplete, please fill in all four " "scantag values.\n", sample); return 0; } const std::vector &scantags = it->second; Couples *coup = build_couples(sample, period, scantags[0].Data(), scantags[1].Data(), scantags[2].Data(), scantags[3].Data()); if (coup == 0) return 0; coup->draw(name); coup->lineup(0, 1); coup->lineup(2, 3); run_couples(coup, sample, name); return coup; } std::map redo_couples_2023() { // Regenerate the Couples object for every sample in run period // cls-6-2023, using the permanent orientation_scantags_2023 mapping // above. Returns a map of Couples objects keyed by sample name. std::map result; for (auto &entry : orientation_scantags_2023) { const std::string &sample = entry.first; const std::vector &scantags = entry.second; if (scantags.size() != 4) { printf("Error in redo_couples_2023 - orientation_scantags_2023[%s] " "is incomplete, please fill in all four scantag values.\n", sample.c_str()); continue; } Couples *coup = build_couples(sample.c_str(), period_2023.Data(), scantags[0].Data(), scantags[1].Data(), scantags[2].Data(), scantags[3].Data()); if (coup) result[sample] = coup; } return result; }