Skip to content

Commit c447118

Browse files
committed
Update LightUp option parsing for when multi-materials / regions have same material name
Previously, we only allowed the light up options to require unique material names. However, we didn't have any such restrictions in the actual material blocks. I've now updated this such that we can use a single light up block for material blocks with all of the same material name. The assumption here is that there might be slight differences or none at all if the material names are the same in our material blocks. However, the light up blocks would all have the same parameters / name. We might change this in the future but for now things are good as we'll duplicate the blocks and allow such cases as our file output makes unique file names for everything. Outside of the above, I also hardened the input parsing aspect of things as the TOML parser is a bit brittle and might parse numbers as ints and then crash on us if we want a double. So, I now parse for both cases and internally convert to doubles for everyone which solves the issues for the HKL and sample directions for everything.
1 parent a573b38 commit c447118

3 files changed

Lines changed: 214 additions & 97 deletions

File tree

src/options/option_parser_v2.cpp

Lines changed: 143 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,76 @@ bool ExaOptions::validate() {
453453
}
454454
}
455455

456+
// Build material-to-regions map for efficiency
457+
std::unordered_multimap<std::string, int> material_to_regions;
458+
for (const auto& region : materials) {
459+
material_to_regions.emplace(region.material_name, region.region_id);
460+
}
461+
462+
// Track which light-up configurations already exist to avoid duplicates
463+
std::set<std::pair<std::string, int>> existing_configs;
464+
465+
// First pass: collect existing configurations
466+
for (const auto& lightup : post_processing.light_up_configs) {
467+
existing_configs.insert(std::make_pair(lightup.material_name, lightup.region_id.value()));
468+
}
469+
470+
// Create a temporary vector to hold new LightUpOptions
471+
std::vector<LightUpOptions> additional_lightup_options;
472+
473+
// Second pass: process each light-up option
474+
for (auto& lightup : post_processing.light_up_configs) {
475+
// Mark original options as user-generated
476+
lightup.is_auto_generated = false;
477+
478+
// Find all regions with this material
479+
auto range = material_to_regions.equal_range(lightup.material_name);
480+
std::vector<int> regions_with_material;
481+
482+
for (auto it = range.first; it != range.second; ++it) {
483+
regions_with_material.push_back(it->second);
484+
}
485+
486+
if (regions_with_material.empty()) {
487+
std::ostringstream info;
488+
info << "Error: PostProcessing.light_up material '" << lightup.material_name << "' not found in any region";
489+
WARNING_0_OPT(info.str());
490+
return false;
491+
}
492+
493+
// Sort for consistent ordering
494+
std::sort(regions_with_material.begin(), regions_with_material.end());
495+
496+
// Update the original lightup with the first region ID
497+
lightup.region_id = regions_with_material[0];
498+
499+
// Create duplicates for remaining regions if they don't already exist
500+
for (size_t i = 1; i < regions_with_material.size(); ++i) {
501+
int target_region_id = regions_with_material[i];
502+
503+
// Check if this configuration already exists
504+
auto config_key = std::make_pair(lightup.material_name, target_region_id);
505+
if (existing_configs.find(config_key) == existing_configs.end()) {
506+
// Create duplicate
507+
LightUpOptions duplicate = lightup;
508+
duplicate.region_id = target_region_id;
509+
duplicate.is_auto_generated = true; // Mark as auto-generated
510+
511+
additional_lightup_options.push_back(duplicate);
512+
existing_configs.insert(config_key); // Track that we've added this
513+
}
514+
}
515+
}
516+
517+
// Append the duplicated options to the main vector
518+
if (!additional_lightup_options.empty()) {
519+
post_processing.light_up_configs.insert(
520+
post_processing.light_up_configs.end(),
521+
additional_lightup_options.begin(),
522+
additional_lightup_options.end()
523+
);
524+
}
525+
456526
// Validate post-processing after region resolution
457527
if (!post_processing.validate()) return false;
458528

@@ -1009,76 +1079,87 @@ void ExaOptions::print_post_processing_options() const {
10091079
if (light_configs.empty()) {
10101080
std::cout << " Light-up analysis: Disabled\n";
10111081
} else {
1012-
std::cout << " Light-up analysis: " << light_configs.size() << " configuration(s)\n";
1082+
std::cout << "\n=== LightUp Configuration Summary ===\n";
10131083

1014-
for (size_t i = 0; i < light_configs.size(); ++i) {
1015-
const auto& light = light_configs[i];
1016-
std::cout << " Configuration " << (i + 1) << ":\n";
1017-
std::cout << " Material: " << light.material_name;
1018-
if (light.region_id.has_value()) {
1019-
std::cout << " (region " << light.region_id.value() << ")";
1020-
}
1021-
std::cout << "\n";
1022-
std::cout << " Enabled: " << (light.enabled ? "Yes" : "No") << "\n";
1023-
1024-
if (light.enabled) {
1025-
std::cout << " Laue Group: ";
1026-
switch (light.lattice_type) {
1027-
case LatticeType::CUBIC: {
1028-
std::cout << "cubic\n";
1029-
break;
1030-
}
1031-
case LatticeType::HEXAGONAL: {
1032-
std::cout << "hexagonal\n";
1033-
break;
1034-
}
1035-
case LatticeType::TRIGONAL: {
1036-
std::cout << "trigonal\n";
1037-
break;
1038-
}
1039-
case LatticeType::RHOMBOHEDRAL: {
1040-
std::cout << "rhombohedral\n";
1041-
break;
1042-
}
1043-
case LatticeType::TETRAGONAL: {
1044-
std::cout << "tetragonal\n";
1045-
break;
1046-
}
1047-
case LatticeType::ORTHORHOMBIC: {
1048-
std::cout << "orthorhombic\n";
1049-
break;
1050-
}
1051-
case LatticeType::MONOCLINIC: {
1052-
std::cout << "monoclinic\n";
1053-
break;
1054-
}
1055-
case LatticeType::TRICLINIC: {
1056-
std::cout << "triclinic\n";
1057-
break;
1058-
}
1059-
default: {
1060-
std::cout << "unknown\n";
1061-
}
1084+
// Group by material for cleaner output
1085+
std::map<std::string, std::vector<const LightUpOptions*>> by_material;
1086+
for (const auto& lightup : light_configs) {
1087+
by_material[lightup.material_name].push_back(&lightup);
1088+
}
1089+
1090+
for (const auto& [material, options] : by_material) {
1091+
std::cout << " Material: " << material << "\n";
1092+
for (const auto* opt : options) {
1093+
auto& light = *opt;
1094+
std::cout << " Region ";
1095+
if (light.region_id.has_value()) {
1096+
std::cout << light.region_id.value();
1097+
} else {
1098+
std::cout << "(unassigned)";
10621099
}
1100+
std::cout << " (" << (light.is_auto_generated ? "auto" : "user") << ")\n";
1101+
std::cout << "\n";
1102+
std::cout << " Enabled: " << (light.enabled ? "Yes" : "No") << "\n";
1103+
if (light.enabled) {
1104+
std::cout << " Laue Group: ";
1105+
switch (light.lattice_type) {
1106+
case LatticeType::CUBIC: {
1107+
std::cout << "cubic\n";
1108+
break;
1109+
}
1110+
case LatticeType::HEXAGONAL: {
1111+
std::cout << "hexagonal\n";
1112+
break;
1113+
}
1114+
case LatticeType::TRIGONAL: {
1115+
std::cout << "trigonal\n";
1116+
break;
1117+
}
1118+
case LatticeType::RHOMBOHEDRAL: {
1119+
std::cout << "rhombohedral\n";
1120+
break;
1121+
}
1122+
case LatticeType::TETRAGONAL: {
1123+
std::cout << "tetragonal\n";
1124+
break;
1125+
}
1126+
case LatticeType::ORTHORHOMBIC: {
1127+
std::cout << "orthorhombic\n";
1128+
break;
1129+
}
1130+
case LatticeType::MONOCLINIC: {
1131+
std::cout << "monoclinic\n";
1132+
break;
1133+
}
1134+
case LatticeType::TRICLINIC: {
1135+
std::cout << "triclinic\n";
1136+
break;
1137+
}
1138+
default: {
1139+
std::cout << "unknown\n";
1140+
}
1141+
}
10631142

1064-
std::cout << " Lattice parameters: ( ";
1065-
for (const auto& lp : light.lattice_parameters) {
1066-
std::cout << lp << " ";
1067-
}
1068-
std::cout << ")\n";
1143+
std::cout << " Lattice parameters: ( ";
1144+
for (const auto& lp : light.lattice_parameters) {
1145+
std::cout << lp << " ";
1146+
}
1147+
std::cout << ")\n";
10691148

1070-
std::cout << " Distance tolerance: " << light.distance_tolerance << "\n";
1071-
std::cout << " Sample direction: (" << light.sample_direction[0] << ", "
1072-
<< light.sample_direction[1] << ", " << light.sample_direction[2] << ")\n";
1073-
std::cout << " Output basename: " << light.lattice_basename << "\n";
1149+
std::cout << " Distance tolerance: " << light.distance_tolerance << "\n";
1150+
std::cout << " Sample direction: (" << light.sample_direction[0] << ", "
1151+
<< light.sample_direction[1] << ", " << light.sample_direction[2] << ")\n";
1152+
std::cout << " Output basename: " << light.lattice_basename << "\n";
10741153

1075-
if (!light.hkl_directions.empty()) {
1076-
std::cout << " HKL directions:\n";
1077-
for (const auto& hkl : light.hkl_directions) {
1078-
std::cout << " - [" << hkl[0] << ", " << hkl[1] << ", " << hkl[2] << "]\n";
1154+
if (!light.hkl_directions.empty()) {
1155+
std::cout << " HKL directions:\n";
1156+
for (const auto& hkl : light.hkl_directions) {
1157+
std::cout << " - [" << hkl[0] << ", " << hkl[1] << ", " << hkl[2] << "]\n";
1158+
}
10791159
}
10801160
}
10811161
}
10821162
}
1163+
std::cout << "=====================================\n\n";
10831164
}
10841165
}

src/options/option_parser_v2.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,24 @@ struct LightUpOptions {
990990
*/
991991
std::string lattice_basename = "lattice_avg_";
992992

993+
/**
994+
* @brief Lattice type that the user has set
995+
*/
993996
LatticeType lattice_type = LatticeType::CUBIC;
997+
998+
/**
999+
* @brief note whether or not a light-up file was auto-generated
1000+
*/
1001+
bool is_auto_generated = false;
1002+
1003+
/**
1004+
* @brief Equality operator for uniqueness checking
1005+
*/
1006+
bool operator==(const LightUpOptions& other) const {
1007+
// Compare all relevant fields except is_auto_generated
1008+
return material_name == other.material_name &&
1009+
region_id == other.region_id;
1010+
}
9941011

9951012
// Validation
9961013
bool validate() const;

src/options/option_post_processing.cpp

Lines changed: 54 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,13 @@ LightUpOptions parse_legacy_light_up(const toml::value& toml_input) {
7070
for (const auto& direction : hkl_array.as_array()) {
7171
if (direction.is_array() && direction.as_array().size() >= 3) {
7272
std::array<double, 3> hkl_dir;
73-
auto dir_vec = toml::get<std::vector<double>>(direction);
74-
hkl_dir[0] = dir_vec[0];
75-
hkl_dir[1] = dir_vec[1];
76-
hkl_dir[2] = dir_vec[2];
73+
if (direction.at(0).is(toml::value_t::integer)) {
74+
auto dir_vec = toml::get<std::vector<int>>(direction);
75+
std::copy_n(dir_vec.begin(), 3, hkl_dir.begin());
76+
} else {
77+
auto dir_vec = toml::get<std::vector<double>>(direction);
78+
std::copy_n(dir_vec.begin(), 3, hkl_dir.begin());
79+
}
7780
options.hkl_directions.push_back(hkl_dir);
7881
}
7982
}
@@ -87,11 +90,17 @@ LightUpOptions parse_legacy_light_up(const toml::value& toml_input) {
8790

8891
// Parse sample direction (light_s_dir -> sample_direction)
8992
if (viz_table.contains("light_s_dir")) {
90-
auto s_dir = toml::find<std::vector<double>>(viz_table, "light_s_dir");
91-
if (s_dir.size() >= 3) {
92-
options.sample_direction[0] = s_dir[0];
93-
options.sample_direction[1] = s_dir[1];
94-
options.sample_direction[2] = s_dir[2];
93+
const auto& dir = toml::find(toml_input, "light_s_dir");
94+
if (dir.at(0).is(toml::value_t::integer)) {
95+
auto dir_vec = toml::get<std::vector<int>>(dir);
96+
if (dir_vec.size() >= 3) {
97+
std::copy_n(dir_vec.begin(), 3, options.sample_direction.begin());
98+
}
99+
} else {
100+
auto dir_vec = toml::get<std::vector<double>>(dir);
101+
if (dir_vec.size() >= 3) {
102+
std::copy_n(dir_vec.begin(), 3, options.sample_direction.begin());
103+
}
95104
}
96105
}
97106

@@ -135,8 +144,13 @@ LightUpOptions LightUpOptions::from_toml(const toml::value& toml_input) {
135144
for (const auto& dir : hkl.as_array()) {
136145
if (dir.is_array() && dir.as_array().size() >= 3) {
137146
std::array<double, 3> direction;
138-
auto dir_vec = toml::get<std::vector<double>>(dir);
139-
std::copy_n(dir_vec.begin(), 3, direction.begin());
147+
if (dir.at(0).is(toml::value_t::integer)) {
148+
auto dir_vec = toml::get<std::vector<int>>(dir);
149+
std::copy_n(dir_vec.begin(), 3, direction.begin());
150+
} else {
151+
auto dir_vec = toml::get<std::vector<double>>(dir);
152+
std::copy_n(dir_vec.begin(), 3, direction.begin());
153+
}
140154
options.hkl_directions.push_back(direction);
141155
}
142156
}
@@ -147,8 +161,13 @@ LightUpOptions LightUpOptions::from_toml(const toml::value& toml_input) {
147161
for (const auto& dir : hkl.as_array()) {
148162
if (dir.is_array() && dir.as_array().size() >= 3) {
149163
std::array<double, 3> direction;
150-
auto dir_vec = toml::get<std::vector<double>>(dir);
151-
std::copy_n(dir_vec.begin(), 3, direction.begin());
164+
if (dir.at(0).is(toml::value_t::integer)) {
165+
auto dir_vec = toml::get<std::vector<int>>(dir);
166+
std::copy_n(dir_vec.begin(), 3, direction.begin());
167+
} else {
168+
auto dir_vec = toml::get<std::vector<double>>(dir);
169+
std::copy_n(dir_vec.begin(), 3, direction.begin());
170+
}
152171
options.hkl_directions.push_back(direction);
153172
}
154173
}
@@ -162,14 +181,30 @@ LightUpOptions LightUpOptions::from_toml(const toml::value& toml_input) {
162181
}
163182

164183
if (toml_input.contains("light_s_dir")) {
165-
auto dir = toml::find<std::vector<double>>(toml_input, "light_s_dir");
166-
if (dir.size() >= 3) {
167-
std::copy_n(dir.begin(), 3, options.sample_direction.begin());
184+
const auto& dir = toml::find(toml_input, "light_s_dir");
185+
if (dir.at(0).is(toml::value_t::integer)) {
186+
auto dir_vec = toml::get<std::vector<int>>(dir);
187+
if (dir_vec.size() >= 3) {
188+
std::copy_n(dir_vec.begin(), 3, options.sample_direction.begin());
189+
}
190+
} else {
191+
auto dir_vec = toml::get<std::vector<double>>(dir);
192+
if (dir_vec.size() >= 3) {
193+
std::copy_n(dir_vec.begin(), 3, options.sample_direction.begin());
194+
}
168195
}
169196
} else if (toml_input.contains("sample_direction")) {
170-
auto dir = toml::find<std::vector<double>>(toml_input, "sample_direction");
171-
if (dir.size() >= 3) {
172-
std::copy_n(dir.begin(), 3, options.sample_direction.begin());
197+
const auto& dir = toml::find(toml_input, "sample_direction");
198+
if (dir.at(0).is(toml::value_t::integer)) {
199+
auto dir_vec = toml::get<std::vector<int>>(dir);
200+
if (dir_vec.size() >= 3) {
201+
std::copy_n(dir_vec.begin(), 3, options.sample_direction.begin());
202+
}
203+
} else {
204+
auto dir_vec = toml::get<std::vector<double>>(dir);
205+
if (dir_vec.size() >= 3) {
206+
std::copy_n(dir_vec.begin(), 3, options.sample_direction.begin());
207+
}
173208
}
174209
}
175210

@@ -595,22 +630,6 @@ bool PostProcessingOptions::validate() const {
595630
for (const auto& light_config : light_up_configs) {
596631
if (!light_config.validate()) return false;
597632
}
598-
599-
// Check for duplicate material names
600-
std::set<std::string> material_names;
601-
for (const auto& light_config : light_up_configs) {
602-
if (light_config.enabled) {
603-
if (material_names.count(light_config.material_name) > 0) {
604-
std::ostringstream err;
605-
err << "Error: Multiple light_up configurations for material: "
606-
<< light_config.material_name << std::endl;
607-
WARNING_0_OPT(err.str());
608-
return false;
609-
}
610-
material_names.insert(light_config.material_name);
611-
}
612-
}
613-
614633
return true;
615634
}
616635

0 commit comments

Comments
 (0)