66static std::string
LabelName(
const TDF_Label& label) {
67 Handle(TDataStd_Name) nameAttr;
68 if (label.FindAttribute(TDataStd_Name::GetID(), nameAttr)) {
69 TCollection_ExtendedString ext = nameAttr->Get();
70 TCollection_AsciiString ascii(ext,
'?');
71 return std::string(ascii.ToCString());
78 const Handle(XCAFDoc_MaterialTool) & matTool) {
79 Handle(TCollection_HAsciiString) matName;
80 Handle(TCollection_HAsciiString) matDescription;
81 Standard_Real density{};
82 Handle(TCollection_HAsciiString) densityName;
83 Handle(TCollection_HAsciiString) densityValType;
84 if (matTool->GetMaterial(label, matName, matDescription, density, densityName, densityValType)) {
85 if (!matName.IsNull())
86 return std::string(matName->ToCString());
96static void CollectMaterials(
const TDF_Label& label,
const Handle(XCAFDoc_ShapeTool) & shapeTool,
97 const Handle(XCAFDoc_MaterialTool) & matTool,
98 std::set<std::string>& out) {
99 if (shapeTool->IsAssembly(label)) {
100 TDF_LabelSequence components;
101 shapeTool->GetComponents(label, components, Standard_False);
102 for (Standard_Integer i = 1; i <= components.Length(); ++i) {
104 if (shapeTool->GetReferredShape(components.Value(i), referred))
172 std::map<std::string, std::string> result;
173 std::ifstream in(path);
175 std::cerr <<
"step2dd4hep: warning: cannot open existing map '" << path <<
"' — ignored\n";
179 while (std::getline(in, line)) {
181 auto spos = line.find(
"step_name=\"");
182 auto mpos = line.find(
"dd4hep_material=\"");
183 if (spos == std::string::npos || mpos == std::string::npos)
186 auto send = line.find(
'"', spos);
188 auto mend = line.find(
'"', mpos);
189 if (send == std::string::npos || mend == std::string::npos)
191 std::string stepName =
XmlUnescape(line.substr(spos, send - spos));
192 std::string matName =
XmlUnescape(line.substr(mpos, mend - mpos));
193 if (!matName.empty() && matName !=
"TODO")
194 result[stepName] = matName;
216int main(
int argc,
char** argv) {
217 std::string stepPath;
218 std::string compactOut;
219 std::string materialOut;
220 std::string existingMapPath;
221 std::string detectorName;
225 for (
int i = 1; i < argc; ++i) {
226 std::string arg(argv[i]);
227 if (arg ==
"-h" || arg ==
"--help") {
230 }
else if (arg ==
"-o") {
232 std::cerr <<
"step2dd4hep: -o requires an argument\n";
235 compactOut = argv[i];
236 }
else if (arg ==
"-m") {
238 std::cerr <<
"step2dd4hep: -m requires an argument\n";
241 materialOut = argv[i];
242 }
else if (arg ==
"--existing-map") {
244 std::cerr <<
"step2dd4hep: --existing-map requires an argument\n";
247 existingMapPath = argv[i];
248 }
else if (arg ==
"--detector-id") {
250 std::cerr <<
"step2dd4hep: --detector-id requires an argument\n";
254 detectorId = std::stoi(argv[i]);
255 }
catch (
const std::exception& e) {
256 std::cerr <<
"step2dd4hep: --detector-id requires a valid integer, got '" << argv[i]
257 <<
"': " << e.what() <<
"\n";
260 }
else if (arg ==
"--detector-name") {
262 std::cerr <<
"step2dd4hep: --detector-name requires an argument\n";
265 detectorName = argv[i];
266 }
else if (arg[0] ==
'-') {
267 std::cerr <<
"step2dd4hep: unknown option '" << arg <<
"'\n";
271 if (!stepPath.empty()) {
272 std::cerr <<
"step2dd4hep: multiple STEP files specified; only one is supported\n";
279 if (stepPath.empty()) {
285 fs::path stepFsPath(stepPath);
286 std::string stem = stepFsPath.stem().string();
288 if (compactOut.empty())
289 compactOut = (fs::current_path() / (stem +
".xml")).string();
290 if (materialOut.empty())
291 materialOut = (fs::current_path() / (stem +
"_materials.xml")).string();
292 if (detectorName.empty())
296 Handle(TDocStd_Application) app =
new TDocStd_Application;
297 Handle(TDocStd_Document) doc;
298 app->NewDocument(
"MDTV-CAF", doc);
303 if (!fs::exists(stepFsPath)) {
304 std::cerr <<
"step2dd4hep: STEP file not found: '" << stepPath <<
"'\n";
308 STEPCAFControl_Reader reader;
309 reader.SetNameMode(Standard_True);
310 reader.SetMatMode(Standard_True);
312 IFSelect_ReturnStatus status = reader.ReadFile(stepPath.c_str());
313 if (status != IFSelect_RetDone) {
314 std::cerr <<
"step2dd4hep: failed to read STEP file '" << stepPath <<
"'\n";
317 if (!reader.Transfer(doc)) {
318 std::cerr <<
"step2dd4hep: STEP transfer to XDE document failed\n";
322 Handle(XCAFDoc_ShapeTool) shapeTool = XCAFDoc_DocumentTool::ShapeTool(doc->Main());
323 Handle(XCAFDoc_MaterialTool) matTool = XCAFDoc_DocumentTool::MaterialTool(doc->Main());
326 TDF_LabelSequence freeShapes;
327 shapeTool->GetFreeShapes(freeShapes);
329 std::set<std::string> stepNames;
330 for (Standard_Integer i = 1; i <= freeShapes.Length(); ++i)
333 if (stepNames.empty()) {
334 std::cerr <<
"step2dd4hep: warning: no material or label names found in '" << stepPath
335 <<
"'; the material map will be empty\n";
339 std::map<std::string, std::string> knownMappings;
340 if (!existingMapPath.empty())
345 std::ofstream out(materialOut);
346 if (!out.is_open()) {
347 std::cerr <<
"step2dd4hep: cannot write material map to '" << materialOut <<
"'\n";
350 out <<
"<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->\n"
351 <<
"<!-- Copyright (C) 2026 G4OCCT Contributors -->\n"
353 <<
" Material map for " << stem <<
"\n"
354 <<
" Generated by step2dd4hep from: " << stepPath <<
"\n"
356 <<
" For each <entry>:\n"
357 <<
" step_name — the name that appears in the STEP file\n"
358 <<
" dd4hep_material — the DD4hep / Geant4 NIST material name\n"
359 <<
" (e.g. \"G4_Al\", \"G4_Fe\", \"G4_AIR\", …)\n"
360 <<
" Replace every \"TODO\" placeholder with the correct material name.\n"
362 <<
"<material_map>\n";
363 for (
const auto& name : stepNames) {
364 auto it = knownMappings.find(name);
365 std::string mapped = (it != knownMappings.end()) ? it->second :
"TODO";
366 out <<
" <entry step_name=\"" <<
XmlAttr(name) <<
"\""
367 <<
" dd4hep_material=\"" <<
XmlAttr(mapped) <<
"\"/>\n";
369 out <<
"</material_map>\n";
371 std::cout <<
"step2dd4hep: wrote material map → " << materialOut <<
"\n";
375 fs::path absStep = fs::absolute(stepFsPath);
376 fs::path absMat = fs::absolute(fs::path(materialOut));
379 std::ofstream out(compactOut);
380 if (!out.is_open()) {
381 std::cerr <<
"step2dd4hep: cannot write compact XML to '" << compactOut <<
"'\n";
384 out <<
"<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->\n"
385 <<
"<!-- Copyright (C) 2026 G4OCCT Contributors -->\n"
387 <<
" DD4hep compact file for " << stem <<
"\n"
388 <<
" Generated by step2dd4hep from: " << stepPath <<
"\n"
390 <<
" Fill in the material map (" << absMat.filename().string() <<
")\n"
391 <<
" before running ddsim or npsim.\n"
394 <<
" <info name=\"" <<
XmlAttr(stem) <<
"\"\n"
395 <<
" title=\"" <<
XmlAttr(stem) <<
"\"\n"
398 <<
" status=\"development\"\n"
399 <<
" version=\"0.0\">\n"
400 <<
" <comment>Compact geometry generated by step2dd4hep.</comment>\n"
404 <<
" <constant name=\"world_side\" value=\"10000*mm\"/>\n"
405 <<
" <constant name=\"world_x\" value=\"world_side/2\"/>\n"
406 <<
" <constant name=\"world_y\" value=\"world_side/2\"/>\n"
407 <<
" <constant name=\"world_z\" value=\"world_side/2\"/>\n"
408 <<
" <constant name=\"tracker_region_rmax\" value=\"world_side/2\"/>\n"
409 <<
" <constant name=\"tracker_region_zmax\" value=\"world_side/2\"/>\n"
413 <<
" <detector id=\"" << detectorId <<
"\"\n"
414 <<
" name=\"" <<
XmlAttr(detectorName) <<
"\"\n"
415 <<
" type=\"G4OCCT_STEPAssembly\">\n"
416 <<
" <step_file path=\"" <<
XmlAttr(absStep.string()) <<
"\"/>\n"
417 <<
" <position x=\"0\" y=\"0\" z=\"0\"/>\n"
418 <<
" <material_map>\n"
420 <<
" Auto-generated from STEP material/label names.\n"
421 <<
" Replace TODO values in " << absMat.filename().string() <<
"\n"
422 <<
" and re-run step2dd4hep, or edit the entries below directly.\n"
425 for (
const auto& name : stepNames) {
426 auto it = knownMappings.find(name);
427 std::string mapped = (it != knownMappings.end()) ? it->second :
"TODO";
428 out <<
" <entry step_name=\"" <<
XmlAttr(name) <<
"\""
429 <<
" dd4hep_material=\"" <<
XmlAttr(mapped) <<
"\"/>\n";
432 out <<
" </material_map>\n"
438 std::cout <<
"step2dd4hep: wrote compact XML → " << compactOut <<
"\n";
440 if (!stepNames.empty()) {
441 long nTodo = std::count_if(stepNames.begin(), stepNames.end(), [&](
const std::string& n) {
442 return knownMappings.find(n) == knownMappings.end();
445 std::cout <<
"step2dd4hep: " << nTodo <<
" material(s) need mapping in "
446 << absMat.filename().string() <<
" (marked TODO)\n";
int main(int argc, char **argv)
static std::string MaterialName(const TDF_Label &label, const Handle(XCAFDoc_MaterialTool) &matTool)
Return the XDE material name for label, or empty string.
static std::string LabelName(const TDF_Label &label)
Return the TDataStd_Name value for label, or empty string.
static void CollectMaterials(const TDF_Label &label, const Handle(XCAFDoc_ShapeTool) &shapeTool, const Handle(XCAFDoc_MaterialTool) &matTool, std::set< std::string > &out)
static std::map< std::string, std::string > ReadExistingMap(const std::string &path)
static std::string XmlAttr(const std::string &s)
Escape characters that must be escaped inside an XML attribute value.
static void PrintUsage(const char *prog)
static std::string XmlUnescape(std::string s)