From 3afaf28161e1e508ae0566dbe5dc7edb4f56cbeb Mon Sep 17 00:00:00 2001 From: Seth R Johnson Date: Mon, 13 May 2024 11:18:28 -0400 Subject: [PATCH 1/5] Allow null values for geometry/memspace input --- app/celer-geo/GeoInput.cc | 4 ++-- app/celer-geo/celer-geo.cc | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/celer-geo/GeoInput.cc b/app/celer-geo/GeoInput.cc index 062924f7e6..22214195c9 100644 --- a/app/celer-geo/GeoInput.cc +++ b/app/celer-geo/GeoInput.cc @@ -61,11 +61,11 @@ void from_json(nlohmann::json const& j, ModelSetup& v) void from_json(nlohmann::json const& j, TraceSetup& v) { - if (auto iter = j.find("geometry"); iter != j.end()) + if (auto iter = j.find("geometry"); iter != j.end() && !iter->is_null()) { v.geometry = to_geometry(iter->get()); } - if (auto iter = j.find("memspace"); iter != j.end()) + if (auto iter = j.find("memspace"); iter != j.end() && !iter->is_null()) { v.memspace = to_memspace(iter->get()); } diff --git a/app/celer-geo/celer-geo.cc b/app/celer-geo/celer-geo.cc index 5275043b4d..59b28bdf8d 100644 --- a/app/celer-geo/celer-geo.cc +++ b/app/celer-geo/celer-geo.cc @@ -204,7 +204,8 @@ void run(std::istream& is) { CELER_LOG(error) << "Invalid trace setup; expected structure written " - "to stdout"; + "to stdout (" + << e.what() << ")"; json temp = TraceSetup{}; temp["image"] = ImageInput{}; std::cout << json(temp).dump() << std::endl; From e6efdb4cb190336d8fea566d4a985f8c47f713c3 Mon Sep 17 00:00:00 2001 From: Seth R Johnson Date: Mon, 13 May 2024 11:18:58 -0400 Subject: [PATCH 2/5] Add gdml-to-dot script --- scripts/user/.gitignore | 2 + scripts/user/gdml-to-dot.py | 106 ++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 scripts/user/.gitignore create mode 100755 scripts/user/gdml-to-dot.py diff --git a/scripts/user/.gitignore b/scripts/user/.gitignore new file mode 100644 index 0000000000..b4a858a013 --- /dev/null +++ b/scripts/user/.gitignore @@ -0,0 +1,2 @@ +*.dot +*.pdf diff --git a/scripts/user/gdml-to-dot.py b/scripts/user/gdml-to-dot.py new file mode 100755 index 0000000000..0a1531f655 --- /dev/null +++ b/scripts/user/gdml-to-dot.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +# Copyright 2023 UT-Battelle, LLC, and other Celeritas developers. +# See the top-level COPYRIGHT file for details. +# SPDX-License-Identifier: (Apache-2.0 OR MIT) +""" +Generate a GraphViz DAG of GDML logical volume relationships. + +The resulting output file can be converted to a PDF file with, e.g.:: + + dot -Tpdf:quartz demo.gdml.dot -o demo.pdf + +""" + +import json +import re +import xml.etree.ElementTree as ET +import networkx as nx + +from collections import defaultdict +from pathlib import Path + +class PointerReplacer: + sub = re.compile(r'0x[0-9a-f]{4,}').sub + + def __init__(self): + self.addrs = {} + + def repl(self, match): + val = self.addrs.setdefault(match.group(0), len(self.addrs)) + return f"@{val:d}" + + def __call__(self, s): + return self.sub(self.repl, s) + +class Graph: + def __init__(self): + self.nodes = [] + self.edges = defaultdict(int) + self.replace_pointers = PointerReplacer() + + def add_volume(self, el): + edges = self.edges + + pname = self.replace_pointers(el.attrib["name"]) + self.nodes.append(pname) + for vrel in el.iter("volumeref"): + dname = self.replace_pointers(vrel.attrib["ref"]) + edges[(pname, dname)] += 1 + + def add_world(self, vrel): + pname = self.replace_pointers(vrel.attrib["ref"]) + self.nodes.append(pname) + + @property + def weighted_edges(self): + for ((u, v), weight) in self.edges.items(): + yield (u, v, weight) + + @property + def labeled_edges(self): + for ((u, v), weight) in self.edges.items(): + yield (u, v, ("" if weight == 1 else f"×{weight}")) + + @property + def pointer_addresses(self): + return self.replace_pointers.addrs + +def read_graph(filename): + tree = ET.parse(filename) + structure = next(tree.iter("structure")) + + g = Graph() + for el in structure: + if el.tag in ('volume', 'assembly'): + g.add_volume(el) + else: + raise ValueError(f"Unrecognized structure tag: {el!r}") + g.add_world(tree.findall("./setup/world")[0]) + + return g + +def write_graph(g, filename): + graph = nx.DiGraph() + graph.add_nodes_from(reversed(g.nodes)) + graph.add_weighted_edges_from(g.labeled_edges, weight='label') + graph.graph['graph']={'rankdir':'LR'} + nx.nx_pydot.write_dot(graph, filename) + + with open(filename, 'a') as f: + f.write("// Pointer mapping:\n") + addrs = g.pointer_addresses.items() + for (idx, addr) in sorted((v, k) for (k, v) in addrs): + f.write(f"// {idx:04d}: {addr}\n") + +def main(*args): + from argparse import ArgumentParser + parser = ArgumentParser(description=__doc__, prog="gdml-to-dot") + parser.add_argument('-o', '--output') + parser.add_argument('input') + ns = parser.parse_args(*args) + input = Path(ns.input) + g = read_graph(input) + write_graph(g, ns.output or (input.stem + ".dot")) + +if __name__ == "__main__": + main() From 2bd64bfc6a04796999de431d52d8d1c58b183adb Mon Sep 17 00:00:00 2001 From: Seth R Johnson Date: Mon, 13 May 2024 13:29:06 -0400 Subject: [PATCH 3/5] Catch failure when one window coordinate is degenerate --- src/geocel/rasterize/Image.cc | 6 ++---- test/orange/Orange.test.cc | 1 - test/orange/OrangeGeant.test.cc | 1 - test/orange/OrangeJson.test.cc | 1 - test/orange/OrangeShift.test.cc | 1 - 5 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/geocel/rasterize/Image.cc b/src/geocel/rasterize/Image.cc index 7558caf7b4..99c39a40a4 100644 --- a/src/geocel/rasterize/Image.cc +++ b/src/geocel/rasterize/Image.cc @@ -32,9 +32,6 @@ ImageParams::ImageParams(ImageInput const& inp) CELER_VALIDATE(ArraySoftUnit{real_type{0.001}}(inp.rightward), << "rightward axis " << repr(inp.rightward) << " is not a unit vector"); - CELER_VALIDATE(!(inp.lower_left == inp.upper_right), - << "lower left corner " << repr(inp.lower_left) - << " and upper right corner cannot be the same"); CELER_VALIDATE(inp.vertical_pixels > 0, << "number of pixels must be positive"); CELER_VALIDATE(inp.horizontal_divisor > 0, @@ -66,7 +63,8 @@ ImageParams::ImageParams(ImageInput const& inp) // Calculate length along each axis real_type width_x = dot_product(diagonal, scalars.right); real_type width_y = -dot_product(diagonal, scalars.down); - CELER_ASSERT(width_x > 0 && width_y > 0); + CELER_VALIDATE(width_x > 0 && width_y > 0, + << "window coordinates result in a degenerate window"); scalars.max_length = width_x; // Set number of pixels in each direction. diff --git a/test/orange/Orange.test.cc b/test/orange/Orange.test.cc index 76e6976830..398c1c5a08 100644 --- a/test/orange/Orange.test.cc +++ b/test/orange/Orange.test.cc @@ -17,7 +17,6 @@ #include "OrangeGeoTestBase.hh" #include "TestMacros.hh" -#include "celeritas_test.hh" using celeritas::constants::sqrt_two; diff --git a/test/orange/OrangeGeant.test.cc b/test/orange/OrangeGeant.test.cc index 9a6475f5da..8032699d87 100644 --- a/test/orange/OrangeGeant.test.cc +++ b/test/orange/OrangeGeant.test.cc @@ -13,7 +13,6 @@ #include "OrangeGeoTestBase.hh" #include "TestMacros.hh" -#include "celeritas_test.hh" namespace celeritas { diff --git a/test/orange/OrangeJson.test.cc b/test/orange/OrangeJson.test.cc index a24b267160..3d311c5273 100644 --- a/test/orange/OrangeJson.test.cc +++ b/test/orange/OrangeJson.test.cc @@ -20,7 +20,6 @@ #include "OrangeGeoTestBase.hh" #include "TestMacros.hh" -#include "celeritas_test.hh" namespace celeritas { diff --git a/test/orange/OrangeShift.test.cc b/test/orange/OrangeShift.test.cc index 7259390558..02018ad66c 100644 --- a/test/orange/OrangeShift.test.cc +++ b/test/orange/OrangeShift.test.cc @@ -19,7 +19,6 @@ #include "OrangeGeoTestBase.hh" #include "TestMacros.hh" -#include "celeritas_test.hh" namespace celeritas { From 734b099d202a9c1a2b86e7f607c483750105e99a Mon Sep 17 00:00:00 2001 From: Seth R Johnson Date: Fri, 24 May 2024 06:27:02 +0100 Subject: [PATCH 4/5] Fix csg-to-dot with output argument --- scripts/user/orange-csg-to-dot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/user/orange-csg-to-dot.py b/scripts/user/orange-csg-to-dot.py index 047537b3b0..84d8b0913b 100755 --- a/scripts/user/orange-csg-to-dot.py +++ b/scripts/user/orange-csg-to-dot.py @@ -74,7 +74,7 @@ def main(): if not args.output: outfile = sys.stdout else: - outfile = open(args.output) + outfile = open(args.output, 'w') run(infile, outfile) From c38bd8732b5169b1499d137a8a49940b3dcc5914 Mon Sep 17 00:00:00 2001 From: Seth R Johnson Date: Mon, 27 May 2024 09:35:58 -0400 Subject: [PATCH 5/5] fixup! Catch failure when one window coordinate is degenerate --- test/orange/Orange.test.cc | 1 + test/orange/OrangeGeant.test.cc | 1 + test/orange/OrangeJson.test.cc | 1 + test/orange/OrangeShift.test.cc | 1 + 4 files changed, 4 insertions(+) diff --git a/test/orange/Orange.test.cc b/test/orange/Orange.test.cc index 398c1c5a08..76e6976830 100644 --- a/test/orange/Orange.test.cc +++ b/test/orange/Orange.test.cc @@ -17,6 +17,7 @@ #include "OrangeGeoTestBase.hh" #include "TestMacros.hh" +#include "celeritas_test.hh" using celeritas::constants::sqrt_two; diff --git a/test/orange/OrangeGeant.test.cc b/test/orange/OrangeGeant.test.cc index 8032699d87..9a6475f5da 100644 --- a/test/orange/OrangeGeant.test.cc +++ b/test/orange/OrangeGeant.test.cc @@ -13,6 +13,7 @@ #include "OrangeGeoTestBase.hh" #include "TestMacros.hh" +#include "celeritas_test.hh" namespace celeritas { diff --git a/test/orange/OrangeJson.test.cc b/test/orange/OrangeJson.test.cc index 3d311c5273..a24b267160 100644 --- a/test/orange/OrangeJson.test.cc +++ b/test/orange/OrangeJson.test.cc @@ -20,6 +20,7 @@ #include "OrangeGeoTestBase.hh" #include "TestMacros.hh" +#include "celeritas_test.hh" namespace celeritas { diff --git a/test/orange/OrangeShift.test.cc b/test/orange/OrangeShift.test.cc index 02018ad66c..7259390558 100644 --- a/test/orange/OrangeShift.test.cc +++ b/test/orange/OrangeShift.test.cc @@ -19,6 +19,7 @@ #include "OrangeGeoTestBase.hh" #include "TestMacros.hh" +#include "celeritas_test.hh" namespace celeritas {