Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* [Work environment](tutorial/02-work-environment.md)
* [Factory](tutorial/03-factory.md)
* [Contributing](tutorial/04-contributing.md)
* [Factory Help Commands](tutorial/05-factory-help-commands.md)
* Misc
* [References](misc/references.md)
* Flags
Expand Down
125 changes: 125 additions & 0 deletions docs/tutorial/05-factory-help-commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# EICrecon Factory Help Commands

This document describes the new factory introspection commands added to the `eicrecon` CLI.

## New Commands

### `--list-available-factories <plugin>`

Lists all factories provided by a specific plugin.

**Usage:**
```bash
eicrecon --list-available-factories EEMC
eicrecon --list-available-factories BEMC
```

**Output:**
- Shows all factories provided by the specified plugin
- Displays factory object names and tags in a table format
- If plugin not found, shows list of available plugins with factories
- Includes a summary count of factories provided by the plugin

**Example:**
```
$ eicrecon --list-available-factories EEMC

Factories provided by plugin 'EEMC':

| Object name | Tag | Description |
|------------------------------|------------------------------------------|---------------------------------------------------------------|
| CalorimeterHitDigi | EcalEndcapNRawHits | Produces: CalorimeterHitDigi (tag: EcalEndcapNRawHits) |
| CalorimeterHitReco | EcalEndcapNRecHits | Produces: CalorimeterHitReco (tag: EcalEndcapNRecHits) |
| CalorimeterClusterRecoCoG | EcalEndcapNTruthClustersWithoutShapes | Produces: CalorimeterClusterRecoCoG (tag: EcalEndcapN...) |

Summary: Plugin 'EEMC' provides 15 factories.
```

### `--print-factory-info`

Shows detailed information about all factories in the system.

**Usage:**
```bash
eicrecon --print-factory-info
```

**Output:**
- Complete table of all factories with plugin, object name, tag, and type info
- Factory summary statistics including total count
- Breakdown of factories by plugin
- Collection naming pattern analysis (heuristic)
- Guidance on how to get more detailed input/output information

**Example:**
```
$ eicrecon --print-factory-info

Detailed factory information:

| Plugin | Object name | Tag | Type Info |
|--------|--------------------------|----------------------------|-------------------------------------|
| EEMC | CalorimeterHitDigi | EcalEndcapNRawHits | CalorimeterHitDigi [EcalEndcapN...] |
| BEMC | CalorimeterHitDigi | EcalBarrelRawHits | CalorimeterHitDigi [EcalBarrel...] |
| ... | ... | ... | ... |

Factory Summary:
Total factories: 157
Factories by plugin:
EEMC: 15 factories
BEMC: 12 factories
tracking: 8 factories
...

Collection naming patterns (heuristic analysis):
Ecal*: 45 collections (EcalEndcapNRawHits, EcalBarrelRawHits, EcalEndcapNRecHits, ...)
Hcal*: 23 collections (HcalEndcapNRawHits, HcalBarrelRawHits, ...)
...

For detailed input/output collection information:
1. Use --list-available-factories <plugin> to see factories by plugin
2. Inspect factory source code for precise input/output definitions
3. Look at plugin registration code (e.g., EEMC.cc) for I/O specifications
```

## Integration with Existing Commands

These new commands work alongside existing EICrecon CLI commands:

- `--list-available-plugins` - Lists all available plugins
- `--list-factories` (`-L`) - Lists all factories (basic format)
- `--list-default-plugins` - Lists default plugins

## Technical Notes

### Factory Input/Output Collections

While these commands provide comprehensive factory listings, detailed input/output collection information requires examination of:

1. **Factory source code**: Look at the `PodioInput<>` and `PodioOutput<>` declarations in factory headers
2. **Plugin registration**: Check the plugin's `.cc` file (e.g., `EEMC.cc`) for `JOmniFactoryGeneratorT` calls that specify input and output collections
3. **Factory documentation**: Some factories may have additional documentation in their source files

### Example Factory Registration

In plugin registration code, you'll find patterns like:
```cpp
app->Add(new JOmniFactoryGeneratorT<CalorimeterHitDigi_factory>(
"EcalEndcapNRawHits", // Factory tag
{"EventHeader", "EcalEndcapNHits"}, // Input collections
{"EcalEndcapNRawHits", "EcalEndcapNRawHitAssociations"}, // Output collections
config_object
));
```

This provides the precise input/output relationships that are not currently accessible through the runtime CLI introspection.

## Future Enhancements

These commands provide a foundation for factory introspection. Potential future enhancements could include:

- Runtime access to input/output collection information through extended JANA APIs
- Factory dependency graph visualization
- Collection type information
- Factory parameter inspection
- Performance metrics integration
198 changes: 179 additions & 19 deletions src/utilities/eicrecon/eicrecon_cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <cstring>
#include <filesystem>
#include <iostream>
#include <map>
#include <memory>
#include <set>
#include <sstream>
Expand Down Expand Up @@ -49,6 +50,9 @@ void PrintUsageOptions() {
std::cout << " -b --benchmark Run in benchmark mode" << std::endl;
std::cout << " -L --list-factories List all the factories without running"
<< std::endl;
std::cout << " --list-available-factories <plugin> List factories for a specific plugin"
<< std::endl;
std::cout << " --print-factory-info Print detailed factory information" << std::endl;
std::cout << " -Pkey=value Specify a configuration parameter" << std::endl;
std::cout << " -Pplugin:param=value Specify a parameter value for a plugin"
<< std::endl;
Expand All @@ -72,7 +76,9 @@ void PrintUsageExample() {
std::cout << "Example:" << std::endl;
std::cout << " eicrecon -Pplugins=plugin1,plugin2,plugin3 -Pnthreads=8 infile.root"
<< std::endl;
std::cout << " eicrecon -Ppodio:print_type_table=1 infile.root" << std::endl << std::endl;
std::cout << " eicrecon -Ppodio:print_type_table=1 infile.root" << std::endl;
std::cout << " eicrecon --list-available-factories EEMC" << std::endl;
std::cout << " eicrecon --print-factory-info" << std::endl << std::endl;
std::cout << std::endl << std::endl;
}

Expand Down Expand Up @@ -321,6 +327,61 @@ void PrintFactories(JApplication* app) {
std::cout << std::endl;
}

void PrintPluginFactories(JApplication* app, const std::string& plugin_name) {
std::cout << std::endl
<< "Factories provided by plugin '" << plugin_name << "':" << std::endl
<< std::endl;

auto cs = app->GetComponentSummary();
JTablePrinter factory_table;
factory_table.AddColumn("Object name");
factory_table.AddColumn("Tag");
factory_table.AddColumn("Description");

bool found_any = false;
int factory_count = 0;

for (const auto& factory : cs.factories) {
if (factory.plugin_name == plugin_name) {
std::string description = "Produces: " + factory.object_name;
if (!factory.factory_tag.empty()) {
description += " (tag: " + factory.factory_tag + ")";
}
factory_table | factory.object_name | factory.factory_tag | description;
found_any = true;
factory_count++;
}
}

if (!found_any) {
std::cout << "No factories found for plugin '" << plugin_name << "'" << std::endl;
std::cout << std::endl << "Available plugins with factories:" << std::endl;

// Show available plugins that have factories
std::set<std::string> available_plugins;
for (const auto& factory : cs.factories) {
available_plugins.insert(factory.plugin_name);
}

JTablePrinter plugin_table;
plugin_table.AddColumn("Plugin name");
for (const auto& plugin : available_plugins) {
plugin_table | plugin;
}

std::ostringstream plugin_ss;
plugin_table.Render(plugin_ss);
std::cout << plugin_ss.str() << std::endl;
} else {
std::ostringstream ss;
factory_table.Render(ss);
std::cout << ss.str() << std::endl;
std::cout << "Summary: Plugin '" << plugin_name << "' provides " << factory_count
<< " factories." << std::endl;
}
std::cout << std::endl;
}

void PrintPodioCollections(JApplication* app) {
if (app->GetJParameterManager()->Exists("PODIO:PRINT_TYPE_TABLE")) {
bool print_type_table = app->GetParameterValue<bool>("podio:print_type_table");
Expand All @@ -339,6 +400,81 @@ void PrintPodioCollections(JApplication* app) {
}
}

void PrintFactoryInfo(JApplication* app) {
std::cout << std::endl << "Detailed factory information:" << std::endl << std::endl;

auto cs = app->GetComponentSummary();
JTablePrinter factory_table;
factory_table.AddColumn("Plugin");
factory_table.AddColumn("Object name");
factory_table.AddColumn("Tag");
factory_table.AddColumn("Type Info");

for (const auto& factory : cs.factories) {
std::string type_info = factory.object_name;
if (!factory.factory_tag.empty()) {
type_info += " [" + factory.factory_tag + "]";
}
factory_table | factory.plugin_name | factory.object_name | factory.factory_tag | type_info;
}

std::ostringstream ss;
factory_table.Render(ss);
std::cout << ss.str() << std::endl;

std::cout << "Factory Summary:" << std::endl;
std::cout << " Total factories: " << cs.factories.size() << std::endl;

// Count factories by plugin
std::map<std::string, int> plugin_counts;
for (const auto& factory : cs.factories) {
plugin_counts[factory.plugin_name]++;
}

std::cout << " Factories by plugin:" << std::endl;
for (const auto& pair : plugin_counts) {
std::cout << " " << pair.first << ": " << pair.second << " factories" << std::endl;
}

// Look for potential collection patterns (basic heuristic)
std::cout << std::endl << " Collection naming patterns (heuristic analysis):" << std::endl;
std::map<std::string, std::vector<std::string>> patterns;

for (const auto& factory : cs.factories) {
// Extract potential collection base names by looking at factory tags
std::string tag = factory.factory_tag.empty() ? factory.object_name : factory.factory_tag;

// Simple pattern matching - look for common prefixes
if (tag.length() > 4) {
std::string prefix = tag.substr(0, 4);
patterns[prefix].push_back(tag);
}
}

for (const auto& pattern : patterns) {
if (pattern.second.size() > 1) { // Only show patterns with multiple factories
std::cout << " " << pattern.first << "*: " << pattern.second.size() << " collections (";
for (size_t i = 0; i < std::min(pattern.second.size(), size_t(3)); ++i) {
if (i > 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ readability-braces-around-statements ⚠️
statement should be inside braces

Suggested change
if (i > 0)
if (i > 0) {

std::cout << ", ";
std::cout << pattern.second[i];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ readability-braces-around-statements ⚠️
statement should be inside braces

Suggested change
std::cout << pattern.second[i];
}
std::cout << pattern.second[i];

}
if (pattern.second.size() > 3)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ readability-braces-around-statements ⚠️
statement should be inside braces

Suggested change
if (pattern.second.size() > 3)
if (pattern.second.size() > 3) {

std::cout << ", ...";
std::cout << ")" << std::endl;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ readability-braces-around-statements ⚠️
statement should be inside braces

Suggested change
std::cout << ")" << std::endl;
}
std::cout << ")" << std::endl;

}
}

std::cout << std::endl;
std::cout << "For detailed input/output collection information:" << std::endl;
std::cout << " 1. Use --list-available-factories <plugin> to see factories by plugin"
<< std::endl;
std::cout << " 2. Inspect factory source code for precise input/output definitions" << std::endl;
std::cout << " 3. Look at plugin registration code (e.g., EEMC.cc) for I/O specifications"
<< std::endl;
std::cout << std::endl;
}

void PrintConfigParameters(JApplication* app) {
/// Print a table of the currently defined configuration parameters.
/// n.b. this mostly duplicates a call to app->GetJParameterManager()->PrintParameters()
Expand Down Expand Up @@ -419,6 +555,12 @@ int Execute(JApplication* app, UserOptions& options) {

// TODO: more elegant processing here
PrintPodioCollections(app);
} else if (options.flags[ListPluginFactories]) {
app->Initialize();
PrintPluginFactories(app, options.plugin_name);
} else if (options.flags[PrintFactoryInfo]) {
app->Initialize();
PrintFactoryInfo(app);
} else {
if ((JVersion::GetMajorNumber() == 2) && (JVersion::GetMinorNumber() == 3) &&
(JVersion::GetPatchNumber() <= 1)) {
Expand Down Expand Up @@ -452,24 +594,26 @@ UserOptions GetCliOptions(int nargs, char* argv[], bool expect_extra) {
UserOptions options;

std::map<std::string, Flag> tokenizer;
tokenizer["-h"] = ShowUsage;
tokenizer["--help"] = ShowUsage;
tokenizer["-v"] = ShowVersion;
tokenizer["--version"] = ShowVersion;
tokenizer["-j"] = ShowJANAVersion;
tokenizer["--janaversion"] = ShowJANAVersion;
tokenizer["-c"] = ShowConfigs;
tokenizer["--configs"] = ShowConfigs;
tokenizer["-l"] = LoadConfigs;
tokenizer["--loadconfigs"] = LoadConfigs;
tokenizer["-d"] = DumpConfigs;
tokenizer["--dumpconfigs"] = DumpConfigs;
tokenizer["-b"] = Benchmark;
tokenizer["--benchmark"] = Benchmark;
tokenizer["-L"] = ListFactories;
tokenizer["--list-factories"] = ListFactories;
tokenizer["--list-default-plugins"] = ShowDefaultPlugins;
tokenizer["--list-available-plugins"] = ShowAvailablePlugins;
tokenizer["-h"] = ShowUsage;
tokenizer["--help"] = ShowUsage;
tokenizer["-v"] = ShowVersion;
tokenizer["--version"] = ShowVersion;
tokenizer["-j"] = ShowJANAVersion;
tokenizer["--janaversion"] = ShowJANAVersion;
tokenizer["-c"] = ShowConfigs;
tokenizer["--configs"] = ShowConfigs;
tokenizer["-l"] = LoadConfigs;
tokenizer["--loadconfigs"] = LoadConfigs;
tokenizer["-d"] = DumpConfigs;
tokenizer["--dumpconfigs"] = DumpConfigs;
tokenizer["-b"] = Benchmark;
tokenizer["--benchmark"] = Benchmark;
tokenizer["-L"] = ListFactories;
tokenizer["--list-factories"] = ListFactories;
tokenizer["--list-available-factories"] = ListPluginFactories;
tokenizer["--print-factory-info"] = PrintFactoryInfo;
tokenizer["--list-default-plugins"] = ShowDefaultPlugins;
tokenizer["--list-available-plugins"] = ShowAvailablePlugins;

// `eicrecon` has the same effect with `eicrecon -h`
if (nargs == 1) {
Expand Down Expand Up @@ -532,6 +676,22 @@ UserOptions GetCliOptions(int nargs, char* argv[], bool expect_extra) {
options.flags[ListFactories] = true;
break;

case ListPluginFactories:
options.flags[ListPluginFactories] = true;
if (i + 1 < nargs && argv[i + 1][0] != '-') {
options.plugin_name = argv[i + 1];
i += 1;
} else {
std::cout << "Error: --list-available-factories requires a plugin name argument"
<< std::endl;
options.flags[ShowUsage] = true;
}
break;

case PrintFactoryInfo:
options.flags[PrintFactoryInfo] = true;
break;

case ShowDefaultPlugins:
options.flags[ShowDefaultPlugins] = true;
break;
Expand Down
Loading
Loading