diff --git a/src/data/spaceGroups.cpp b/src/data/spaceGroups.cpp index 9daafa6be0..6a5032a17e 100644 --- a/src/data/spaceGroups.cpp +++ b/src/data/spaceGroups.cpp @@ -703,4 +703,542 @@ std::string formattedInformation(SpaceGroupId sgid) symbols_[sgid].code(), symbols_[sgid].hallSymbol()); } +// Return enum option info for SpaceGroupID +EnumOptions spaceGroupID() +{ + return EnumOptions("SpaceGroupId", {{NoSpaceGroup, "NoSpaceGroup"}, + {SpaceGroup_1, "SpaceGroup_1"}, + {SpaceGroup_2, "SpaceGroup_2"}, + {SpaceGroup_3_b, "SpaceGroup_3_b"}, + {SpaceGroup_3_c, "SpaceGroup_3_c"}, + {SpaceGroup_3_a, "SpaceGroup_3_a"}, + {SpaceGroup_4_b, "SpaceGroup_4_b"}, + {SpaceGroup_4_c, "SpaceGroup_4_c"}, + {SpaceGroup_4_a, "SpaceGroup_4_a"}, + {SpaceGroup_5_b1, "SpaceGroup_5_b1"}, + {SpaceGroup_5_b2, "SpaceGroup_5_b2"}, + {SpaceGroup_5_b3, "SpaceGroup_5_b3"}, + {SpaceGroup_5_c1, "SpaceGroup_5_c1"}, + {SpaceGroup_5_c2, "SpaceGroup_5_c2"}, + {SpaceGroup_5_c3, "SpaceGroup_5_c3"}, + {SpaceGroup_5_a1, "SpaceGroup_5_a1"}, + {SpaceGroup_5_a2, "SpaceGroup_5_a2"}, + {SpaceGroup_5_a3, "SpaceGroup_5_a3"}, + {SpaceGroup_6_b, "SpaceGroup_6_b"}, + {SpaceGroup_6_c, "SpaceGroup_6_c"}, + {SpaceGroup_6_a, "SpaceGroup_6_a"}, + {SpaceGroup_7_b1, "SpaceGroup_7_b1"}, + {SpaceGroup_7_b2, "SpaceGroup_7_b2"}, + {SpaceGroup_7_b3, "SpaceGroup_7_b3"}, + {SpaceGroup_7_c1, "SpaceGroup_7_c1"}, + {SpaceGroup_7_c2, "SpaceGroup_7_c2"}, + {SpaceGroup_7_c3, "SpaceGroup_7_c3"}, + {SpaceGroup_7_a1, "SpaceGroup_7_a1"}, + {SpaceGroup_7_a2, "SpaceGroup_7_a2"}, + {SpaceGroup_7_a3, "SpaceGroup_7_a3"}, + {SpaceGroup_8_b1, "SpaceGroup_8_b1"}, + {SpaceGroup_8_b2, "SpaceGroup_8_b2"}, + {SpaceGroup_8_b3, "SpaceGroup_8_b3"}, + {SpaceGroup_8_c1, "SpaceGroup_8_c1"}, + {SpaceGroup_8_c2, "SpaceGroup_8_c2"}, + {SpaceGroup_8_c3, "SpaceGroup_8_c3"}, + {SpaceGroup_8_a1, "SpaceGroup_8_a1"}, + {SpaceGroup_8_a2, "SpaceGroup_8_a2"}, + {SpaceGroup_8_a3, "SpaceGroup_8_a3"}, + {SpaceGroup_9_b1, "SpaceGroup_9_b1"}, + {SpaceGroup_9_b2, "SpaceGroup_9_b2"}, + {SpaceGroup_9_b3, "SpaceGroup_9_b3"}, + {SpaceGroup_9_B1, "SpaceGroup_9_B1"}, + {SpaceGroup_9_B2, "SpaceGroup_9_B2"}, + {SpaceGroup_9_B3, "SpaceGroup_9_B3"}, + {SpaceGroup_9_c1, "SpaceGroup_9_c1"}, + {SpaceGroup_9_c2, "SpaceGroup_9_c2"}, + {SpaceGroup_9_c3, "SpaceGroup_9_c3"}, + {SpaceGroup_9_C1, "SpaceGroup_9_C1"}, + {SpaceGroup_9_C2, "SpaceGroup_9_C2"}, + {SpaceGroup_9_C3, "SpaceGroup_9_C3"}, + {SpaceGroup_9_a1, "SpaceGroup_9_a1"}, + {SpaceGroup_9_a2, "SpaceGroup_9_a2"}, + {SpaceGroup_9_a3, "SpaceGroup_9_a3"}, + {SpaceGroup_9_A1, "SpaceGroup_9_A1"}, + {SpaceGroup_9_A2, "SpaceGroup_9_A2"}, + {SpaceGroup_9_A3, "SpaceGroup_9_A3"}, + {SpaceGroup_10_b, "SpaceGroup_10_b"}, + {SpaceGroup_10_c, "SpaceGroup_10_c"}, + {SpaceGroup_10_a, "SpaceGroup_10_a"}, + {SpaceGroup_11_b, "SpaceGroup_11_b"}, + {SpaceGroup_11_c, "SpaceGroup_11_c"}, + {SpaceGroup_11_a, "SpaceGroup_11_a"}, + {SpaceGroup_12_b1, "SpaceGroup_12_b1"}, + {SpaceGroup_12_b2, "SpaceGroup_12_b2"}, + {SpaceGroup_12_b3, "SpaceGroup_12_b3"}, + {SpaceGroup_12_c1, "SpaceGroup_12_c1"}, + {SpaceGroup_12_c2, "SpaceGroup_12_c2"}, + {SpaceGroup_12_c3, "SpaceGroup_12_c3"}, + {SpaceGroup_12_a1, "SpaceGroup_12_a1"}, + {SpaceGroup_12_a2, "SpaceGroup_12_a2"}, + {SpaceGroup_12_a3, "SpaceGroup_12_a3"}, + {SpaceGroup_13_b1, "SpaceGroup_13_b1"}, + {SpaceGroup_13_b2, "SpaceGroup_13_b2"}, + {SpaceGroup_13_b3, "SpaceGroup_13_b3"}, + {SpaceGroup_13_c1, "SpaceGroup_13_c1"}, + {SpaceGroup_13_c2, "SpaceGroup_13_c2"}, + {SpaceGroup_13_c3, "SpaceGroup_13_c3"}, + {SpaceGroup_13_a1, "SpaceGroup_13_a1"}, + {SpaceGroup_13_a2, "SpaceGroup_13_a2"}, + {SpaceGroup_13_a3, "SpaceGroup_13_a3"}, + {SpaceGroup_14_b1, "SpaceGroup_14_b1"}, + {SpaceGroup_14_b2, "SpaceGroup_14_b2"}, + {SpaceGroup_14_b3, "SpaceGroup_14_b3"}, + {SpaceGroup_14_c1, "SpaceGroup_14_c1"}, + {SpaceGroup_14_c2, "SpaceGroup_14_c2"}, + {SpaceGroup_14_c3, "SpaceGroup_14_c3"}, + {SpaceGroup_14_a1, "SpaceGroup_14_a1"}, + {SpaceGroup_14_a2, "SpaceGroup_14_a2"}, + {SpaceGroup_14_a3, "SpaceGroup_14_a3"}, + {SpaceGroup_15_b1, "SpaceGroup_15_b1"}, + {SpaceGroup_15_b2, "SpaceGroup_15_b2"}, + {SpaceGroup_15_b3, "SpaceGroup_15_b3"}, + {SpaceGroup_15_B1, "SpaceGroup_15_B1"}, + {SpaceGroup_15_B2, "SpaceGroup_15_B2"}, + {SpaceGroup_15_B3, "SpaceGroup_15_B3"}, + {SpaceGroup_15_c1, "SpaceGroup_15_c1"}, + {SpaceGroup_15_c2, "SpaceGroup_15_c2"}, + {SpaceGroup_15_c3, "SpaceGroup_15_c3"}, + {SpaceGroup_15_C1, "SpaceGroup_15_C1"}, + {SpaceGroup_15_C2, "SpaceGroup_15_C2"}, + {SpaceGroup_15_C3, "SpaceGroup_15_C3"}, + {SpaceGroup_15_a1, "SpaceGroup_15_a1"}, + {SpaceGroup_15_a2, "SpaceGroup_15_a2"}, + {SpaceGroup_15_a3, "SpaceGroup_15_a3"}, + {SpaceGroup_15_A1, "SpaceGroup_15_A1"}, + {SpaceGroup_15_A2, "SpaceGroup_15_A2"}, + {SpaceGroup_15_A3, "SpaceGroup_15_A3"}, + {SpaceGroup_16, "SpaceGroup_16"}, + {SpaceGroup_17, "SpaceGroup_17"}, + {SpaceGroup_17_cab, "SpaceGroup_17_cab"}, + {SpaceGroup_17_bca, "SpaceGroup_17_bca"}, + {SpaceGroup_18, "SpaceGroup_18"}, + {SpaceGroup_18_cab, "SpaceGroup_18_cab"}, + {SpaceGroup_18_bca, "SpaceGroup_18_bca"}, + {SpaceGroup_19, "SpaceGroup_19"}, + {SpaceGroup_20, "SpaceGroup_20"}, + {SpaceGroup_20_cab, "SpaceGroup_20_cab"}, + {SpaceGroup_20_bca, "SpaceGroup_20_bca"}, + {SpaceGroup_21, "SpaceGroup_21"}, + {SpaceGroup_21_cab, "SpaceGroup_21_cab"}, + {SpaceGroup_21_bca, "SpaceGroup_21_bca"}, + {SpaceGroup_22, "SpaceGroup_22"}, + {SpaceGroup_23, "SpaceGroup_23"}, + {SpaceGroup_24, "SpaceGroup_24"}, + {SpaceGroup_25, "SpaceGroup_25"}, + {SpaceGroup_25_cab, "SpaceGroup_25_cab"}, + {SpaceGroup_25_bca, "SpaceGroup_25_bca"}, + {SpaceGroup_26, "SpaceGroup_26"}, + {SpaceGroup_26_baC, "SpaceGroup_26_baC"}, + {SpaceGroup_26_cab, "SpaceGroup_26_cab"}, + {SpaceGroup_26_Cba, "SpaceGroup_26_Cba"}, + {SpaceGroup_26_bca, "SpaceGroup_26_bca"}, + {SpaceGroup_26_aCb, "SpaceGroup_26_aCb"}, + {SpaceGroup_27, "SpaceGroup_27"}, + {SpaceGroup_27_cab, "SpaceGroup_27_cab"}, + {SpaceGroup_27_bca, "SpaceGroup_27_bca"}, + {SpaceGroup_28, "SpaceGroup_28"}, + {SpaceGroup_28_baC, "SpaceGroup_28_baC"}, + {SpaceGroup_28_cab, "SpaceGroup_28_cab"}, + {SpaceGroup_28_Cba, "SpaceGroup_28_Cba"}, + {SpaceGroup_28_bca, "SpaceGroup_28_bca"}, + {SpaceGroup_28_aCb, "SpaceGroup_28_aCb"}, + {SpaceGroup_29, "SpaceGroup_29"}, + {SpaceGroup_29_baC, "SpaceGroup_29_baC"}, + {SpaceGroup_29_cab, "SpaceGroup_29_cab"}, + {SpaceGroup_29_Cba, "SpaceGroup_29_Cba"}, + {SpaceGroup_29_bca, "SpaceGroup_29_bca"}, + {SpaceGroup_29_aCb, "SpaceGroup_29_aCb"}, + {SpaceGroup_30, "SpaceGroup_30"}, + {SpaceGroup_30_baC, "SpaceGroup_30_baC"}, + {SpaceGroup_30_cab, "SpaceGroup_30_cab"}, + {SpaceGroup_30_Cba, "SpaceGroup_30_Cba"}, + {SpaceGroup_30_bca, "SpaceGroup_30_bca"}, + {SpaceGroup_30_aCb, "SpaceGroup_30_aCb"}, + {SpaceGroup_31, "SpaceGroup_31"}, + {SpaceGroup_31_baC, "SpaceGroup_31_baC"}, + {SpaceGroup_31_cab, "SpaceGroup_31_cab"}, + {SpaceGroup_31_Cba, "SpaceGroup_31_Cba"}, + {SpaceGroup_31_bca, "SpaceGroup_31_bca"}, + {SpaceGroup_31_aCb, "SpaceGroup_31_aCb"}, + {SpaceGroup_32, "SpaceGroup_32"}, + {SpaceGroup_32_cab, "SpaceGroup_32_cab"}, + {SpaceGroup_32_bca, "SpaceGroup_32_bca"}, + {SpaceGroup_33, "SpaceGroup_33"}, + {SpaceGroup_33_baC, "SpaceGroup_33_baC"}, + {SpaceGroup_33_cab, "SpaceGroup_33_cab"}, + {SpaceGroup_33_Cba, "SpaceGroup_33_Cba"}, + {SpaceGroup_33_bca, "SpaceGroup_33_bca"}, + {SpaceGroup_33_aCb, "SpaceGroup_33_aCb"}, + {SpaceGroup_34, "SpaceGroup_34"}, + {SpaceGroup_34_cab, "SpaceGroup_34_cab"}, + {SpaceGroup_34_bca, "SpaceGroup_34_bca"}, + {SpaceGroup_35, "SpaceGroup_35"}, + {SpaceGroup_35_cab, "SpaceGroup_35_cab"}, + {SpaceGroup_35_bca, "SpaceGroup_35_bca"}, + {SpaceGroup_36, "SpaceGroup_36"}, + {SpaceGroup_36_baC, "SpaceGroup_36_baC"}, + {SpaceGroup_36_cab, "SpaceGroup_36_cab"}, + {SpaceGroup_36_Cba, "SpaceGroup_36_Cba"}, + {SpaceGroup_36_bca, "SpaceGroup_36_bca"}, + {SpaceGroup_36_aCb, "SpaceGroup_36_aCb"}, + {SpaceGroup_37, "SpaceGroup_37"}, + {SpaceGroup_37_cab, "SpaceGroup_37_cab"}, + {SpaceGroup_37_bca, "SpaceGroup_37_bca"}, + {SpaceGroup_38, "SpaceGroup_38"}, + {SpaceGroup_38_baC, "SpaceGroup_38_baC"}, + {SpaceGroup_38_cab, "SpaceGroup_38_cab"}, + {SpaceGroup_38_Cba, "SpaceGroup_38_Cba"}, + {SpaceGroup_38_bca, "SpaceGroup_38_bca"}, + {SpaceGroup_38_aCb, "SpaceGroup_38_aCb"}, + {SpaceGroup_39, "SpaceGroup_39"}, + {SpaceGroup_39_baC, "SpaceGroup_39_baC"}, + {SpaceGroup_39_cab, "SpaceGroup_39_cab"}, + {SpaceGroup_39_Cba, "SpaceGroup_39_Cba"}, + {SpaceGroup_39_bca, "SpaceGroup_39_bca"}, + {SpaceGroup_39_aCb, "SpaceGroup_39_aCb"}, + {SpaceGroup_40, "SpaceGroup_40"}, + {SpaceGroup_40_baC, "SpaceGroup_40_baC"}, + {SpaceGroup_40_cab, "SpaceGroup_40_cab"}, + {SpaceGroup_40_Cba, "SpaceGroup_40_Cba"}, + {SpaceGroup_40_bca, "SpaceGroup_40_bca"}, + {SpaceGroup_40_aCb, "SpaceGroup_40_aCb"}, + {SpaceGroup_41, "SpaceGroup_41"}, + {SpaceGroup_41_baC, "SpaceGroup_41_baC"}, + {SpaceGroup_41_cab, "SpaceGroup_41_cab"}, + {SpaceGroup_41_Cba, "SpaceGroup_41_Cba"}, + {SpaceGroup_41_bca, "SpaceGroup_41_bca"}, + {SpaceGroup_41_aCb, "SpaceGroup_41_aCb"}, + {SpaceGroup_42, "SpaceGroup_42"}, + {SpaceGroup_42_cab, "SpaceGroup_42_cab"}, + {SpaceGroup_42_bca, "SpaceGroup_42_bca"}, + {SpaceGroup_43, "SpaceGroup_43"}, + {SpaceGroup_43_cab, "SpaceGroup_43_cab"}, + {SpaceGroup_43_bca, "SpaceGroup_43_bca"}, + {SpaceGroup_44, "SpaceGroup_44"}, + {SpaceGroup_44_cab, "SpaceGroup_44_cab"}, + {SpaceGroup_44_bca, "SpaceGroup_44_bca"}, + {SpaceGroup_45, "SpaceGroup_45"}, + {SpaceGroup_45_cab, "SpaceGroup_45_cab"}, + {SpaceGroup_45_bca, "SpaceGroup_45_bca"}, + {SpaceGroup_46, "SpaceGroup_46"}, + {SpaceGroup_46_baC, "SpaceGroup_46_baC"}, + {SpaceGroup_46_cab, "SpaceGroup_46_cab"}, + {SpaceGroup_46_Cba, "SpaceGroup_46_Cba"}, + {SpaceGroup_46_bca, "SpaceGroup_46_bca"}, + {SpaceGroup_46_aCb, "SpaceGroup_46_aCb"}, + {SpaceGroup_47, "SpaceGroup_47"}, + {SpaceGroup_48_1, "SpaceGroup_48_1"}, + {SpaceGroup_48_2, "SpaceGroup_48_2"}, + {SpaceGroup_49, "SpaceGroup_49"}, + {SpaceGroup_49_cab, "SpaceGroup_49_cab"}, + {SpaceGroup_49_bca, "SpaceGroup_49_bca"}, + {SpaceGroup_50_1, "SpaceGroup_50_1"}, + {SpaceGroup_50_2, "SpaceGroup_50_2"}, + {SpaceGroup_50_1cab, "SpaceGroup_50_1cab"}, + {SpaceGroup_50_2cab, "SpaceGroup_50_2cab"}, + {SpaceGroup_50_1bca, "SpaceGroup_50_1bca"}, + {SpaceGroup_50_2bca, "SpaceGroup_50_2bca"}, + {SpaceGroup_51, "SpaceGroup_51"}, + {SpaceGroup_51_baC, "SpaceGroup_51_baC"}, + {SpaceGroup_51_cab, "SpaceGroup_51_cab"}, + {SpaceGroup_51_Cba, "SpaceGroup_51_Cba"}, + {SpaceGroup_51_bca, "SpaceGroup_51_bca"}, + {SpaceGroup_51_aCb, "SpaceGroup_51_aCb"}, + {SpaceGroup_52, "SpaceGroup_52"}, + {SpaceGroup_52_baC, "SpaceGroup_52_baC"}, + {SpaceGroup_52_cab, "SpaceGroup_52_cab"}, + {SpaceGroup_52_Cba, "SpaceGroup_52_Cba"}, + {SpaceGroup_52_bca, "SpaceGroup_52_bca"}, + {SpaceGroup_52_aCb, "SpaceGroup_52_aCb"}, + {SpaceGroup_53, "SpaceGroup_53"}, + {SpaceGroup_53_baC, "SpaceGroup_53_baC"}, + {SpaceGroup_53_cab, "SpaceGroup_53_cab"}, + {SpaceGroup_53_Cba, "SpaceGroup_53_Cba"}, + {SpaceGroup_53_bca, "SpaceGroup_53_bca"}, + {SpaceGroup_53_aCb, "SpaceGroup_53_aCb"}, + {SpaceGroup_54, "SpaceGroup_54"}, + {SpaceGroup_54_baC, "SpaceGroup_54_baC"}, + {SpaceGroup_54_cab, "SpaceGroup_54_cab"}, + {SpaceGroup_54_Cba, "SpaceGroup_54_Cba"}, + {SpaceGroup_54_bca, "SpaceGroup_54_bca"}, + {SpaceGroup_54_aCb, "SpaceGroup_54_aCb"}, + {SpaceGroup_55, "SpaceGroup_55"}, + {SpaceGroup_55_cab, "SpaceGroup_55_cab"}, + {SpaceGroup_55_bca, "SpaceGroup_55_bca"}, + {SpaceGroup_56, "SpaceGroup_56"}, + {SpaceGroup_56_cab, "SpaceGroup_56_cab"}, + {SpaceGroup_56_bca, "SpaceGroup_56_bca"}, + {SpaceGroup_57, "SpaceGroup_57"}, + {SpaceGroup_57_baC, "SpaceGroup_57_baC"}, + {SpaceGroup_57_cab, "SpaceGroup_57_cab"}, + {SpaceGroup_57_Cba, "SpaceGroup_57_Cba"}, + {SpaceGroup_57_bca, "SpaceGroup_57_bca"}, + {SpaceGroup_57_aCb, "SpaceGroup_57_aCb"}, + {SpaceGroup_58, "SpaceGroup_58"}, + {SpaceGroup_58_cab, "SpaceGroup_58_cab"}, + {SpaceGroup_58_bca, "SpaceGroup_58_bca"}, + {SpaceGroup_59_1, "SpaceGroup_59_1"}, + {SpaceGroup_59_2, "SpaceGroup_59_2"}, + {SpaceGroup_59_1cab, "SpaceGroup_59_1cab"}, + {SpaceGroup_59_2cab, "SpaceGroup_59_2cab"}, + {SpaceGroup_59_1bca, "SpaceGroup_59_1bca"}, + {SpaceGroup_59_2bca, "SpaceGroup_59_2bca"}, + {SpaceGroup_60, "SpaceGroup_60"}, + {SpaceGroup_60_baC, "SpaceGroup_60_baC"}, + {SpaceGroup_60_cab, "SpaceGroup_60_cab"}, + {SpaceGroup_60_Cba, "SpaceGroup_60_Cba"}, + {SpaceGroup_60_bca, "SpaceGroup_60_bca"}, + {SpaceGroup_60_aCb, "SpaceGroup_60_aCb"}, + {SpaceGroup_61, "SpaceGroup_61"}, + {SpaceGroup_61_baC, "SpaceGroup_61_baC"}, + {SpaceGroup_62, "SpaceGroup_62"}, + {SpaceGroup_62_baC, "SpaceGroup_62_baC"}, + {SpaceGroup_62_cab, "SpaceGroup_62_cab"}, + {SpaceGroup_62_Cba, "SpaceGroup_62_Cba"}, + {SpaceGroup_62_bca, "SpaceGroup_62_bca"}, + {SpaceGroup_62_aCb, "SpaceGroup_62_aCb"}, + {SpaceGroup_63, "SpaceGroup_63"}, + {SpaceGroup_63_baC, "SpaceGroup_63_baC"}, + {SpaceGroup_63_cab, "SpaceGroup_63_cab"}, + {SpaceGroup_63_Cba, "SpaceGroup_63_Cba"}, + {SpaceGroup_63_bca, "SpaceGroup_63_bca"}, + {SpaceGroup_63_aCb, "SpaceGroup_63_aCb"}, + {SpaceGroup_64, "SpaceGroup_64"}, + {SpaceGroup_64_baC, "SpaceGroup_64_baC"}, + {SpaceGroup_64_cab, "SpaceGroup_64_cab"}, + {SpaceGroup_64_Cba, "SpaceGroup_64_Cba"}, + {SpaceGroup_64_bca, "SpaceGroup_64_bca"}, + {SpaceGroup_64_aCb, "SpaceGroup_64_aCb"}, + {SpaceGroup_65, "SpaceGroup_65"}, + {SpaceGroup_65_cab, "SpaceGroup_65_cab"}, + {SpaceGroup_65_bca, "SpaceGroup_65_bca"}, + {SpaceGroup_66, "SpaceGroup_66"}, + {SpaceGroup_66_cab, "SpaceGroup_66_cab"}, + {SpaceGroup_66_bca, "SpaceGroup_66_bca"}, + {SpaceGroup_67, "SpaceGroup_67"}, + {SpaceGroup_67_baC, "SpaceGroup_67_baC"}, + {SpaceGroup_67_cab, "SpaceGroup_67_cab"}, + {SpaceGroup_67_Cba, "SpaceGroup_67_Cba"}, + {SpaceGroup_67_bca, "SpaceGroup_67_bca"}, + {SpaceGroup_67_aCb, "SpaceGroup_67_aCb"}, + {SpaceGroup_68_1, "SpaceGroup_68_1"}, + {SpaceGroup_68_2, "SpaceGroup_68_2"}, + {SpaceGroup_68_1baC, "SpaceGroup_68_1baC"}, + {SpaceGroup_68_2baC, "SpaceGroup_68_2baC"}, + {SpaceGroup_68_1cab, "SpaceGroup_68_1cab"}, + {SpaceGroup_68_2cab, "SpaceGroup_68_2cab"}, + {SpaceGroup_68_1Cba, "SpaceGroup_68_1Cba"}, + {SpaceGroup_68_2Cba, "SpaceGroup_68_2Cba"}, + {SpaceGroup_68_1bca, "SpaceGroup_68_1bca"}, + {SpaceGroup_68_2bca, "SpaceGroup_68_2bca"}, + {SpaceGroup_68_1aCb, "SpaceGroup_68_1aCb"}, + {SpaceGroup_68_2aCb, "SpaceGroup_68_2aCb"}, + {SpaceGroup_69, "SpaceGroup_69"}, + {SpaceGroup_70_1, "SpaceGroup_70_1"}, + {SpaceGroup_70_2, "SpaceGroup_70_2"}, + {SpaceGroup_71, "SpaceGroup_71"}, + {SpaceGroup_72, "SpaceGroup_72"}, + {SpaceGroup_72_cab, "SpaceGroup_72_cab"}, + {SpaceGroup_72_bca, "SpaceGroup_72_bca"}, + {SpaceGroup_73, "SpaceGroup_73"}, + {SpaceGroup_73_baC, "SpaceGroup_73_baC"}, + {SpaceGroup_74, "SpaceGroup_74"}, + {SpaceGroup_74_baC, "SpaceGroup_74_baC"}, + {SpaceGroup_74_cab, "SpaceGroup_74_cab"}, + {SpaceGroup_74_Cba, "SpaceGroup_74_Cba"}, + {SpaceGroup_74_bca, "SpaceGroup_74_bca"}, + {SpaceGroup_74_aCb, "SpaceGroup_74_aCb"}, + {SpaceGroup_75, "SpaceGroup_75"}, + {SpaceGroup_76, "SpaceGroup_76"}, + {SpaceGroup_77, "SpaceGroup_77"}, + {SpaceGroup_78, "SpaceGroup_78"}, + {SpaceGroup_79, "SpaceGroup_79"}, + {SpaceGroup_80, "SpaceGroup_80"}, + {SpaceGroup_81, "SpaceGroup_81"}, + {SpaceGroup_82, "SpaceGroup_82"}, + {SpaceGroup_83, "SpaceGroup_83"}, + {SpaceGroup_84, "SpaceGroup_84"}, + {SpaceGroup_85_1, "SpaceGroup_85_1"}, + {SpaceGroup_85_2, "SpaceGroup_85_2"}, + {SpaceGroup_86_1, "SpaceGroup_86_1"}, + {SpaceGroup_86_2, "SpaceGroup_86_2"}, + {SpaceGroup_87, "SpaceGroup_87"}, + {SpaceGroup_88_1, "SpaceGroup_88_1"}, + {SpaceGroup_88_2, "SpaceGroup_88_2"}, + {SpaceGroup_89, "SpaceGroup_89"}, + {SpaceGroup_90, "SpaceGroup_90"}, + {SpaceGroup_91, "SpaceGroup_91"}, + {SpaceGroup_92, "SpaceGroup_92"}, + {SpaceGroup_93, "SpaceGroup_93"}, + {SpaceGroup_94, "SpaceGroup_94"}, + {SpaceGroup_95, "SpaceGroup_95"}, + {SpaceGroup_96, "SpaceGroup_96"}, + {SpaceGroup_97, "SpaceGroup_97"}, + {SpaceGroup_98, "SpaceGroup_98"}, + {SpaceGroup_99, "SpaceGroup_99"}, + {SpaceGroup_100, "SpaceGroup_100"}, + {SpaceGroup_101, "SpaceGroup_101"}, + {SpaceGroup_102, "SpaceGroup_102"}, + {SpaceGroup_103, "SpaceGroup_103"}, + {SpaceGroup_104, "SpaceGroup_104"}, + {SpaceGroup_105, "SpaceGroup_105"}, + {SpaceGroup_106, "SpaceGroup_106"}, + {SpaceGroup_107, "SpaceGroup_107"}, + {SpaceGroup_108, "SpaceGroup_108"}, + {SpaceGroup_109, "SpaceGroup_109"}, + {SpaceGroup_110, "SpaceGroup_110"}, + {SpaceGroup_111, "SpaceGroup_111"}, + {SpaceGroup_112, "SpaceGroup_112"}, + {SpaceGroup_113, "SpaceGroup_113"}, + {SpaceGroup_114, "SpaceGroup_114"}, + {SpaceGroup_115, "SpaceGroup_115"}, + {SpaceGroup_116, "SpaceGroup_116"}, + {SpaceGroup_117, "SpaceGroup_117"}, + {SpaceGroup_118, "SpaceGroup_118"}, + {SpaceGroup_119, "SpaceGroup_119"}, + {SpaceGroup_120, "SpaceGroup_120"}, + {SpaceGroup_121, "SpaceGroup_121"}, + {SpaceGroup_122, "SpaceGroup_122"}, + {SpaceGroup_123, "SpaceGroup_123"}, + {SpaceGroup_124, "SpaceGroup_124"}, + {SpaceGroup_125_1, "SpaceGroup_125_1"}, + {SpaceGroup_125_2, "SpaceGroup_125_2"}, + {SpaceGroup_126_1, "SpaceGroup_126_1"}, + {SpaceGroup_126_2, "SpaceGroup_126_2"}, + {SpaceGroup_127, "SpaceGroup_127"}, + {SpaceGroup_128, "SpaceGroup_128"}, + {SpaceGroup_129_1, "SpaceGroup_129_1"}, + {SpaceGroup_129_2, "SpaceGroup_129_2"}, + {SpaceGroup_130_1, "SpaceGroup_130_1"}, + {SpaceGroup_130_2, "SpaceGroup_130_2"}, + {SpaceGroup_131, "SpaceGroup_131"}, + {SpaceGroup_132, "SpaceGroup_132"}, + {SpaceGroup_133_1, "SpaceGroup_133_1"}, + {SpaceGroup_133_2, "SpaceGroup_133_2"}, + {SpaceGroup_134_1, "SpaceGroup_134_1"}, + {SpaceGroup_134_2, "SpaceGroup_134_2"}, + {SpaceGroup_135, "SpaceGroup_135"}, + {SpaceGroup_136, "SpaceGroup_136"}, + {SpaceGroup_137_1, "SpaceGroup_137_1"}, + {SpaceGroup_137_2, "SpaceGroup_137_2"}, + {SpaceGroup_138_1, "SpaceGroup_138_1"}, + {SpaceGroup_138_2, "SpaceGroup_138_2"}, + {SpaceGroup_139, "SpaceGroup_139"}, + {SpaceGroup_140, "SpaceGroup_140"}, + {SpaceGroup_141_1, "SpaceGroup_141_1"}, + {SpaceGroup_141_2, "SpaceGroup_141_2"}, + {SpaceGroup_142_1, "SpaceGroup_142_1"}, + {SpaceGroup_142_2, "SpaceGroup_142_2"}, + {SpaceGroup_143, "SpaceGroup_143"}, + {SpaceGroup_144, "SpaceGroup_144"}, + {SpaceGroup_145, "SpaceGroup_145"}, + {SpaceGroup_146_H, "SpaceGroup_146_H"}, + {SpaceGroup_146_R, "SpaceGroup_146_R"}, + {SpaceGroup_147, "SpaceGroup_147"}, + {SpaceGroup_148_H, "SpaceGroup_148_H"}, + {SpaceGroup_148_R, "SpaceGroup_148_R"}, + {SpaceGroup_149, "SpaceGroup_149"}, + {SpaceGroup_150, "SpaceGroup_150"}, + {SpaceGroup_151, "SpaceGroup_151"}, + {SpaceGroup_152, "SpaceGroup_152"}, + {SpaceGroup_153, "SpaceGroup_153"}, + {SpaceGroup_154, "SpaceGroup_154"}, + {SpaceGroup_155_H, "SpaceGroup_155_H"}, + {SpaceGroup_155_R, "SpaceGroup_155_R"}, + {SpaceGroup_156, "SpaceGroup_156"}, + {SpaceGroup_157, "SpaceGroup_157"}, + {SpaceGroup_158, "SpaceGroup_158"}, + {SpaceGroup_159, "SpaceGroup_159"}, + {SpaceGroup_160_H, "SpaceGroup_160_H"}, + {SpaceGroup_160_R, "SpaceGroup_160_R"}, + {SpaceGroup_161_H, "SpaceGroup_161_H"}, + {SpaceGroup_161_R, "SpaceGroup_161_R"}, + {SpaceGroup_162, "SpaceGroup_162"}, + {SpaceGroup_163, "SpaceGroup_163"}, + {SpaceGroup_164, "SpaceGroup_164"}, + {SpaceGroup_165, "SpaceGroup_165"}, + {SpaceGroup_166_H, "SpaceGroup_166_H"}, + {SpaceGroup_166_R, "SpaceGroup_166_R"}, + {SpaceGroup_167_H, "SpaceGroup_167_H"}, + {SpaceGroup_167_R, "SpaceGroup_167_R"}, + {SpaceGroup_168, "SpaceGroup_168"}, + {SpaceGroup_169, "SpaceGroup_169"}, + {SpaceGroup_170, "SpaceGroup_170"}, + {SpaceGroup_171, "SpaceGroup_171"}, + {SpaceGroup_172, "SpaceGroup_172"}, + {SpaceGroup_173, "SpaceGroup_173"}, + {SpaceGroup_174, "SpaceGroup_174"}, + {SpaceGroup_175, "SpaceGroup_175"}, + {SpaceGroup_176, "SpaceGroup_176"}, + {SpaceGroup_177, "SpaceGroup_177"}, + {SpaceGroup_178, "SpaceGroup_178"}, + {SpaceGroup_179, "SpaceGroup_179"}, + {SpaceGroup_180, "SpaceGroup_180"}, + {SpaceGroup_181, "SpaceGroup_181"}, + {SpaceGroup_182, "SpaceGroup_182"}, + {SpaceGroup_183, "SpaceGroup_183"}, + {SpaceGroup_184, "SpaceGroup_184"}, + {SpaceGroup_185, "SpaceGroup_185"}, + {SpaceGroup_186, "SpaceGroup_186"}, + {SpaceGroup_187, "SpaceGroup_187"}, + {SpaceGroup_188, "SpaceGroup_188"}, + {SpaceGroup_189, "SpaceGroup_189"}, + {SpaceGroup_190, "SpaceGroup_190"}, + {SpaceGroup_191, "SpaceGroup_191"}, + {SpaceGroup_192, "SpaceGroup_192"}, + {SpaceGroup_193, "SpaceGroup_193"}, + {SpaceGroup_194, "SpaceGroup_194"}, + {SpaceGroup_195, "SpaceGroup_195"}, + {SpaceGroup_196, "SpaceGroup_196"}, + {SpaceGroup_197, "SpaceGroup_197"}, + {SpaceGroup_198, "SpaceGroup_198"}, + {SpaceGroup_199, "SpaceGroup_199"}, + {SpaceGroup_200, "SpaceGroup_200"}, + {SpaceGroup_201_1, "SpaceGroup_201_1"}, + {SpaceGroup_201_2, "SpaceGroup_201_2"}, + {SpaceGroup_202, "SpaceGroup_202"}, + {SpaceGroup_203_1, "SpaceGroup_203_1"}, + {SpaceGroup_203_2, "SpaceGroup_203_2"}, + {SpaceGroup_204, "SpaceGroup_204"}, + {SpaceGroup_205, "SpaceGroup_205"}, + {SpaceGroup_206, "SpaceGroup_206"}, + {SpaceGroup_207, "SpaceGroup_207"}, + {SpaceGroup_208, "SpaceGroup_208"}, + {SpaceGroup_209, "SpaceGroup_209"}, + {SpaceGroup_210, "SpaceGroup_210"}, + {SpaceGroup_211, "SpaceGroup_211"}, + {SpaceGroup_212, "SpaceGroup_212"}, + {SpaceGroup_213, "SpaceGroup_213"}, + {SpaceGroup_214, "SpaceGroup_214"}, + {SpaceGroup_215, "SpaceGroup_215"}, + {SpaceGroup_216, "SpaceGroup_216"}, + {SpaceGroup_217, "SpaceGroup_217"}, + {SpaceGroup_218, "SpaceGroup_218"}, + {SpaceGroup_219, "SpaceGroup_219"}, + {SpaceGroup_220, "SpaceGroup_220"}, + {SpaceGroup_221, "SpaceGroup_221"}, + {SpaceGroup_222_1, "SpaceGroup_222_1"}, + {SpaceGroup_222_2, "SpaceGroup_222_2"}, + {SpaceGroup_223, "SpaceGroup_223"}, + {SpaceGroup_224_1, "SpaceGroup_224_1"}, + {SpaceGroup_224_2, "SpaceGroup_224_2"}, + {SpaceGroup_225, "SpaceGroup_225"}, + {SpaceGroup_226, "SpaceGroup_226"}, + {SpaceGroup_227_1, "SpaceGroup_227_1"}, + {SpaceGroup_227_2, "SpaceGroup_227_2"}, + {SpaceGroup_228_1, "SpaceGroup_228_1"}, + {SpaceGroup_228_2, "SpaceGroup_228_2"}, + {SpaceGroup_229, "SpaceGroup_229"}, + {SpaceGroup_230, "SpaceGroup_230"}, + {nSpaceGroupIds, "nSpaceGroupIds"}}); +} +EnumOptions getEnumOptions(SpaceGroupId) { return spaceGroupID(); } + }; // namespace SpaceGroups diff --git a/src/data/spaceGroups.h b/src/data/spaceGroups.h index cb6b6f7e5f..30cfa9c07f 100644 --- a/src/data/spaceGroups.h +++ b/src/data/spaceGroups.h @@ -3,6 +3,7 @@ #pragma once +#include "base/enumOptions.h" #include "math/matrix4.h" #include "sginfo/sginfo.h" #include "templates/optionalRef.h" @@ -551,6 +552,9 @@ enum SpaceGroupId SpaceGroup_230, nSpaceGroupIds }; +// Return enum option info for SpaceGroupID +EnumOptions spaceGroupID(); +EnumOptions getEnumOptions(SpaceGroupId); // Space Group Symbols class SpaceGroupSymbol diff --git a/src/io/import/cif.cpp b/src/io/import/cif.cpp index f166b2bf29..d794332af2 100644 --- a/src/io/import/cif.cpp +++ b/src/io/import/cif.cpp @@ -886,6 +886,9 @@ bool CIFHandler::isValid() const return !molecularSpecies_.empty() || supercellSpecies_.fragment(0).size() != supercellSpecies_.nAtoms(); } +// Return supercell species +const Species &CIFHandler::supercellSpecies() const { return supercellSpecies_; } + // Return cleaned unit cell species const Species &CIFHandler::cleanedUnitCellSpecies() const { return cleanedUnitCellSpecies_; } diff --git a/src/io/import/cif.h b/src/io/import/cif.h index facb2e94a3..3e68985c51 100644 --- a/src/io/import/cif.h +++ b/src/io/import/cif.h @@ -177,6 +177,8 @@ class CIFHandler bool generate(CIFGenerationStage fromStage = CIFGenerationStage::CreateBasicUnitCell); // Return whether the generated data is valid bool isValid() const; + // Return supercell species + const Species &supercellSpecies() const; // Return cleaned unit cell species const Species &cleanedUnitCellSpecies() const; // Return the detected molecular species diff --git a/src/nodes/cifBondingOptions.cpp b/src/nodes/cifBondingOptions.cpp new file mode 100644 index 0000000000..5a0a2fedf9 --- /dev/null +++ b/src/nodes/cifBondingOptions.cpp @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#include "nodes/cifBondingOptions.h" + +CIFBondingOptionsNode::CIFBondingOptionsNode(Graph *parentGraph) : Node(parentGraph) +{ + // Inputs + addInput("CIFContext", "CIF handling context derived from parsing of CIF file", context_) + ->setFlags({ParameterBase::Required}); + + // Outputs + addOutput("CIFContext", "CIF handling context derived from parsing of CIF file", context_); + + // Options + addOption("BondingTolerance", "Bonding tolerance, if calculating bonding rather than using CIF definitions", + bondingTolerance_); + addOption("UseCIFBondingDefinitions", "Whether to use CIF bonding definitions", useCIFBondingDefinitions_); + addOption("PreventMetallicBonds", "Whether to prevent metallic bonding", preventMetallicBonds_); +} + +std::string_view CIFBondingOptionsNode::type() const { return "CIFBondingOptions"; } + +std::string_view CIFBondingOptionsNode::summary() const { return "Apply bonding options to a CIF context"; } + +// Run main processing +NodeConstants::ProcessResult CIFBondingOptionsNode::process() +{ + + context_->setBondingTolerance(bondingTolerance_.asDouble()); + context_->setUseCIFBondingDefinitions(useCIFBondingDefinitions_); + context_->setPreventMetallicBonds(preventMetallicBonds_); + + return NodeConstants::ProcessResult::Success; +} diff --git a/src/nodes/cifBondingOptions.h b/src/nodes/cifBondingOptions.h new file mode 100644 index 0000000000..03629c6e72 --- /dev/null +++ b/src/nodes/cifBondingOptions.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#pragma once + +#include "nodes/cifLoader.h" +#include "nodes/node.h" + +// CIFLoader Node +class CIFBondingOptionsNode : public Node +{ + public: + CIFBondingOptionsNode(Graph *parentGraph); + ~CIFBondingOptionsNode() override = default; + + public: + std::string_view type() const override; + std::string_view summary() const override; + + /* + * Definition + */ + private: + // CIF handler context + CIFLoaderNode::CIFContext *context_{nullptr}; + // Bonding tolerance, if calculating bonding rather than using CIF definitions + Number bondingTolerance_{1.1}; + // Whether to use CIF bonding definitions + bool useCIFBondingDefinitions_{false}; + // Whether to prevent metallic bonding + bool preventMetallicBonds_{true}; + + /* + * Processing + */ + private: + // Run main processing + NodeConstants::ProcessResult process() override; +}; diff --git a/src/nodes/cifConfiguration.cpp b/src/nodes/cifConfiguration.cpp new file mode 100644 index 0000000000..cb6b54a286 --- /dev/null +++ b/src/nodes/cifConfiguration.cpp @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#include "nodes/cifConfiguration.h" +#include +#include + +CIFConfigurationNode::CIFConfigurationNode(Graph *parentGraph) : Node(parentGraph) +{ + // Inputs + addInput("CIFContext", "CIF handling context derived from parsing of CIF file", context_) + ->setFlags({ParameterBase::Required}); + + // Outputs + addOutput("UnitCellSpecies", "Cleaned unit cell", unitCellSpecies_); + addOutput("SuperCellSpecies", "Supercell species", supercellSpecies_); + addOutput>("MolecularSpecies", "Detected molecular species", molecularSpecies_); + addOutput("SuperCellConfiguration", "Supercell configuration pointer", supercellConfiguration_); + + // Options + addOption("SuperCellRepeat", "Supercell repeat", supercellRepeat_); +} + +std::string_view CIFConfigurationNode::type() const { return "CIFConfiguration"; } + +std::string_view CIFConfigurationNode::summary() const { return "Generate a supercell configuration from a CIF context"; } + +// Run main processing +NodeConstants::ProcessResult CIFConfigurationNode::process() +{ + + // Generate from CIF context + context_->setSupercellRepeat(supercellRepeat_); + context_->generate(); + + // Get supercell configuration + supercellConfiguration_ = context_->generatedConfiguration(); + + // Get cleaned unit cell species + unitCellSpecies_ = &(context_->cleanedUnitCellSpecies()); + + // Get supercell species + supercellSpecies_ = &(context_->supercellSpecies()); + + // Get detected molecular species + auto &cifMols = context_->molecularSpecies(); + molecularSpecies_.clear(); + std::copy(cifMols.begin(), cifMols.end(), std::back_inserter(molecularSpecies_)); + + return NodeConstants::ProcessResult::Success; +} diff --git a/src/nodes/cifConfiguration.h b/src/nodes/cifConfiguration.h new file mode 100644 index 0000000000..de36ae300f --- /dev/null +++ b/src/nodes/cifConfiguration.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#pragma once + +#include "nodes/cifLoader.h" +#include "nodes/node.h" + +// CIFLoader Node +class CIFConfigurationNode : public Node +{ + public: + CIFConfigurationNode(Graph *parentGraph); + ~CIFConfigurationNode() override = default; + + public: + std::string_view type() const override; + std::string_view summary() const override; + + /* + * Definition + */ + private: + // CIF handler context + CIFLoaderNode::CIFContext *context_{nullptr}; + // Supercell configuration + Configuration *supercellConfiguration_{nullptr}; + // Unit cell species + const Species *unitCellSpecies_{nullptr}; + // Supercell species + const Species *supercellSpecies_{nullptr}; + // Detected molecular species + std::vector molecularSpecies_; + // Supercell repeat + Vector3i supercellRepeat_{1, 1, 1}; + + /* + * Processing + */ + private: + // Run main processing + NodeConstants::ProcessResult process() override; +}; diff --git a/src/nodes/cifLoader.cpp b/src/nodes/cifLoader.cpp new file mode 100644 index 0000000000..3d44cc6a17 --- /dev/null +++ b/src/nodes/cifLoader.cpp @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#include "nodes/cifLoader.h" +#include +#include +#include + +CIFLoaderNode::CIFLoaderNode(Graph *parentGraph) : Node(parentGraph) +{ + // Outputs + addPointerOutput("CIFContext", "CIF handling context derived from parsing of CIF file", context_) + ->setFlags({ParameterBase::Required}); + + // Option + addOption("FilePath", "File path", filePath_); + addOption("SpaceGroupID", "Set space group from index", spaceGroup_); +} + +std::string_view CIFLoaderNode::type() const { return "CIFLoader"; } + +std::string_view CIFLoaderNode::summary() const +{ + return "Load a CIF file and apply contained crystallographic data to a target configuration"; +} + +// Run main processing +NodeConstants::ProcessResult CIFLoaderNode::process() +{ + // Read contents of CIF file + if (context_.read(filePath_)) + { + if (spaceGroup_ != SpaceGroups::NoSpaceGroup) + context_.setSpaceGroup(spaceGroup_); + return NodeConstants::ProcessResult::Success; + } + + error("Failed to read contents of CIF file"); + + return NodeConstants::ProcessResult::Failed; +} diff --git a/src/nodes/cifLoader.h b/src/nodes/cifLoader.h new file mode 100644 index 0000000000..f1aec6766b --- /dev/null +++ b/src/nodes/cifLoader.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#pragma once + +#include "io/import/cif.h" +#include "nodes/node.h" + +// CIFLoader Node +class CIFLoaderNode : public Node +{ + public: + using CIFContext = CIFHandler; + + public: + CIFLoaderNode(Graph *parentGraph); + ~CIFLoaderNode() override = default; + + public: + std::string_view type() const override; + std::string_view summary() const override; + + /* + * Definition + */ + private: + // CIF handler context + CIFContext context_; + // Space group ID + SpaceGroups::SpaceGroupId spaceGroup_{SpaceGroups::SpaceGroupId::NoSpaceGroup}; + // CIF filepath + std::string filePath_; + + /* + * Processing + */ + private: + // Run main processing + NodeConstants::ProcessResult process() override; +}; diff --git a/src/nodes/cifMolecularSpecies.cpp b/src/nodes/cifMolecularSpecies.cpp new file mode 100644 index 0000000000..78ece84a3a --- /dev/null +++ b/src/nodes/cifMolecularSpecies.cpp @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#include "nodes/cifMolecularSpecies.h" +#include +#include + +CIFMolecularSpeciesNode::CIFMolecularSpeciesNode(Graph *parentGraph) : Node(parentGraph) +{ + // Inputs + addInput("CIFContext", "CIF handling context derived from parsing of CIF file", context_) + ->setFlags({ParameterBase::Required}); + + // Outputs + addOutput>("DetectedMolecularSpecies", "Detected molecular species", molecularSpecies_); + addOutput("SuperCellConfiguration", "Supercell configuration pointer", supercellConfiguration_); + + // Options + addOption("SuperCellRepeat", "Supercell repeat", supercellRepeat_); +} + +std::string_view CIFMolecularSpeciesNode::type() const { return "CIFMolecularSpecies"; } + +std::string_view CIFMolecularSpeciesNode::summary() const +{ + return "Output a configuration containing individual molecules based on detected species"; +} + +// Run main processing +NodeConstants::ProcessResult CIFMolecularSpeciesNode::process() +{ + // Generate from CIF context + context_->setSupercellRepeat(supercellRepeat_); + context_->generate(); + + // Get supercell configuration + supercellConfiguration_ = context_->generatedConfiguration(); + supercellConfiguration_->setName(context_->chemicalFormula()); + + // Get detected molecular species + auto &cifMols = context_->molecularSpecies(); + molecularSpecies_.clear(); + std::copy(cifMols.begin(), cifMols.end(), std::back_inserter(molecularSpecies_)); + + return NodeConstants::ProcessResult::Success; +} + +// Get cleaned unit cell species +const Species &CIFMolecularSpeciesNode::cleanedUnitCellSpecies() const { return context_->cleanedUnitCellSpecies(); } \ No newline at end of file diff --git a/src/nodes/cifMolecularSpecies.h b/src/nodes/cifMolecularSpecies.h new file mode 100644 index 0000000000..50df4671b8 --- /dev/null +++ b/src/nodes/cifMolecularSpecies.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#pragma once + +#include "nodes/cifLoader.h" +#include "nodes/node.h" + +// CIFLoader Node +class CIFMolecularSpeciesNode : public Node +{ + public: + CIFMolecularSpeciesNode(Graph *parentGraph); + ~CIFMolecularSpeciesNode() override = default; + + public: + std::string_view type() const override; + std::string_view summary() const override; + + /* + * Definition + */ + private: + // CIF handler context + CIFLoaderNode::CIFContext *context_{nullptr}; + // Supercell configuration + Configuration *supercellConfiguration_{nullptr}; + // Detected molecular species + std::vector molecularSpecies_; + // Supercell repeat + Vector3i supercellRepeat_{1, 1, 1}; + + /* + * Processing + */ + private: + // Run main processing + NodeConstants::ProcessResult process() override; + + /* + * Getters + */ + public: + // Get cleaned unit cell species + const Species &cleanedUnitCellSpecies() const; +}; diff --git a/src/nodes/cifPeriodicFramework.cpp b/src/nodes/cifPeriodicFramework.cpp new file mode 100644 index 0000000000..e903cebf7f --- /dev/null +++ b/src/nodes/cifPeriodicFramework.cpp @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#include "nodes/cifPeriodicFramework.h" + +CIFPeriodicFrameworkNode::CIFPeriodicFrameworkNode(Graph *parentGraph) : Node(parentGraph) +{ + // Inputs + addInput("CIFContext", "CIF handling context derived from parsing of CIF file", context_) + ->setFlags({ParameterBase::Required}); + + // Outputs + addOutput("FrameworkSpecies", "Species", frameworkSpecies_); + addOutput("SuperCellConfiguration", "Supercell configuration pointer", supercellConfiguration_); + + // Options + addOption("SuperCellRepeat", "Supercell repeat", supercellRepeat_); +} + +std::string_view CIFPeriodicFrameworkNode::type() const { return "CIFPeriodicFramework"; } + +std::string_view CIFPeriodicFrameworkNode::summary() const +{ + return "Create a single species with a periodic box and all atoms in the unit cell (suitable for framework-style models)"; +} + +// Run main processing +NodeConstants::ProcessResult CIFPeriodicFrameworkNode::process() +{ + // Generate from CIF context + context_->setSupercellRepeat(supercellRepeat_); + context_->generate(); + + // Get supercell configuration + supercellConfiguration_ = context_->generatedConfiguration(); + supercellConfiguration_->setName(context_->chemicalFormula()); + + // Get framework species + supercellSpecies_->copyBasic(&(context_->supercellSpecies())); + supercellSpecies_->updateIntramolecularTerms(); + frameworkSpecies_ = supercellSpecies_.get(); + + return NodeConstants::ProcessResult::Success; +} + +// Get cleaned unit cell species +const Species &CIFPeriodicFrameworkNode::cleanedUnitCellSpecies() const { return context_->cleanedUnitCellSpecies(); } \ No newline at end of file diff --git a/src/nodes/cifPeriodicFramework.h b/src/nodes/cifPeriodicFramework.h new file mode 100644 index 0000000000..607150cbd9 --- /dev/null +++ b/src/nodes/cifPeriodicFramework.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#pragma once + +#include "nodes/cifLoader.h" +#include "nodes/node.h" + +// CIFLoader Node +class CIFPeriodicFrameworkNode : public Node +{ + public: + CIFPeriodicFrameworkNode(Graph *parentGraph); + ~CIFPeriodicFrameworkNode() override = default; + + public: + std::string_view type() const override; + std::string_view summary() const override; + + /* + * Definition + */ + private: + // CIF handler context + CIFLoaderNode::CIFContext *context_{nullptr}; + // Supercell configuration + Configuration *supercellConfiguration_{nullptr}; + // Supercell species + std::unique_ptr supercellSpecies_; + // Framework species + const Species *frameworkSpecies_{nullptr}; + // Supercell repeat + Vector3i supercellRepeat_{1, 1, 1}; + + /* + * Processing + */ + private: + // Run main processing + NodeConstants::ProcessResult process() override; + + /* + * Getters + */ + public: + // Get cleaned unit cell species + const Species &cleanedUnitCellSpecies() const; +}; diff --git a/src/nodes/cifRemoveAtomic.cpp b/src/nodes/cifRemoveAtomic.cpp new file mode 100644 index 0000000000..bb4ba604fb --- /dev/null +++ b/src/nodes/cifRemoveAtomic.cpp @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#include "nodes/cifRemoveAtomic.h" + +CIFRemoveAtomicNode::CIFRemoveAtomicNode(Graph *parentGraph) : Node(parentGraph) +{ + // Inputs + addInput("CIFContext", "CIF handling context derived from parsing of CIF file", context_) + ->setFlags({ParameterBase::Required}); + + // Outputs + addOutput("CIFContext", "CIF handling context derived from parsing of CIF file", context_); + + // Options + addOption("RemoveAtomics", "Whether to remove free atomic moieties in clean-up", removeAtomics_); +} + +std::string_view CIFRemoveAtomicNode::type() const { return "CIFRemoveAtomic"; } + +std::string_view CIFRemoveAtomicNode::summary() const { return "Remove atomics from a CIF context"; } + +// Run main processing +NodeConstants::ProcessResult CIFRemoveAtomicNode::process() +{ + + context_->setRemoveAtomics(removeAtomics_); + + return NodeConstants::ProcessResult::Success; +} diff --git a/src/nodes/cifRemoveAtomic.h b/src/nodes/cifRemoveAtomic.h new file mode 100644 index 0000000000..4ac1c1c93a --- /dev/null +++ b/src/nodes/cifRemoveAtomic.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#pragma once + +#include "nodes/cifLoader.h" +#include "nodes/node.h" + +// CIFLoader Node +class CIFRemoveAtomicNode : public Node +{ + public: + CIFRemoveAtomicNode(Graph *parentGraph); + ~CIFRemoveAtomicNode() override = default; + + public: + std::string_view type() const override; + std::string_view summary() const override; + + /* + * Definition + */ + private: + // CIF handler context + CIFLoaderNode::CIFContext *context_{nullptr}; + // Whether to remove free atomic moieties in clean-up + bool removeAtomics_{false}; + + /* + * Processing + */ + private: + // Run main processing + NodeConstants::ProcessResult process() override; +}; diff --git a/src/nodes/cifRemoveWater.cpp b/src/nodes/cifRemoveWater.cpp new file mode 100644 index 0000000000..3b040d79b9 --- /dev/null +++ b/src/nodes/cifRemoveWater.cpp @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#include "nodes/cifRemoveWater.h" + +CIFRemoveWaterNode::CIFRemoveWaterNode(Graph *parentGraph) : Node(parentGraph) +{ + // Inputs + addInput("CIFContext", "CIF handling context derived from parsing of CIF file", context_) + ->setFlags({ParameterBase::Required}); + + // Outputs + addOutput("CIFContext", "CIF handling context derived from parsing of CIF file", context_); + + // Options + addOption("RemoveWaterAndCoordinatedOxygens", "Whether to remove water and coordinated oxygen atoms in clean-up", + RemoveWaterAndCoordinatedOxygens_); +} + +std::string_view CIFRemoveWaterNode::type() const { return "CIFRemoveWater"; } + +std::string_view CIFRemoveWaterNode::summary() const { return "Remove water from a CIF context"; } + +// Run main processing +NodeConstants::ProcessResult CIFRemoveWaterNode::process() +{ + + context_->setRemoveWaterAndCoordinateOxygens(RemoveWaterAndCoordinatedOxygens_); + + return NodeConstants::ProcessResult::Success; +} diff --git a/src/nodes/cifRemoveWater.h b/src/nodes/cifRemoveWater.h new file mode 100644 index 0000000000..129db91b01 --- /dev/null +++ b/src/nodes/cifRemoveWater.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#pragma once + +#include "nodes/cifLoader.h" +#include "nodes/node.h" + +// CIFLoader Node +class CIFRemoveWaterNode : public Node +{ + public: + CIFRemoveWaterNode(Graph *parentGraph); + ~CIFRemoveWaterNode() override = default; + + public: + std::string_view type() const override; + std::string_view summary() const override; + + /* + * Definition + */ + private: + // CIF handler context + CIFLoaderNode::CIFContext *context_{nullptr}; + // Whether to remove water and coordinated oxygen atoms in clean-up + bool RemoveWaterAndCoordinatedOxygens_{false}; + + /* + * Processing + */ + private: + // Run main processing + NodeConstants::ProcessResult process() override; +}; diff --git a/src/nodes/cifStructureCleanup.cpp b/src/nodes/cifStructureCleanup.cpp new file mode 100644 index 0000000000..ae2a5c0411 --- /dev/null +++ b/src/nodes/cifStructureCleanup.cpp @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#include "nodes/cifStructureCleanup.h" + +CIFStructureCleanupNode::CIFStructureCleanupNode(Graph *parentGraph) : Node(parentGraph) +{ + // Inputs + addInput("CIFContext", "CIF handling context derived from parsing of CIF file", context_) + ->setFlags({ParameterBase::Required}); + + // Outputs + addOutput("CIFContext", "CIF handling context derived from parsing of CIF file", context_); + + // Options + addOption("RemoveNETA", "Whether to remove by NETA definition in clean-up", removeNETA_); + addOption("removeNETAByFragment", "Whether to expand NETA matches to fragments when removing in clean-up", + removeNETAByFragment_); + addOption("MoietyRemovalNETA", "NETA for moiety removal", moietyRemovalNETA_); +} + +std::string_view CIFStructureCleanupNode::type() const { return "CIFStructureCleanup"; } + +std::string_view CIFStructureCleanupNode::summary() const { return "Clean up a CIF context"; } + +// Run main processing +NodeConstants::ProcessResult CIFStructureCleanupNode::process() +{ + + context_->setRemoveNETA(removeNETA_, removeNETAByFragment_); + context_->setMoietyRemovalNETA(std::string_view(moietyRemovalNETA_)); + + return NodeConstants::ProcessResult::Success; +} diff --git a/src/nodes/cifStructureCleanup.h b/src/nodes/cifStructureCleanup.h new file mode 100644 index 0000000000..b0b75bd928 --- /dev/null +++ b/src/nodes/cifStructureCleanup.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#pragma once + +#include "nodes/cifLoader.h" +#include "nodes/node.h" + +// CIFLoader Node +class CIFStructureCleanupNode : public Node +{ + public: + CIFStructureCleanupNode(Graph *parentGraph); + ~CIFStructureCleanupNode() override = default; + + public: + std::string_view type() const override; + std::string_view summary() const override; + + /* + * Definition + */ + private: + // CIF handler context + CIFLoaderNode::CIFContext *context_{nullptr}; + // Whether to remove by NETA definition in clean-up + bool removeNETA_{false}; + // Whether to expand NETA matches to fragments when removing in clean-up + bool removeNETAByFragment_{false}; + // NETA for moiety removal, if specified + std::string moietyRemovalNETA_; + + /* + * Processing + */ + private: + // Run main processing + NodeConstants::ProcessResult process() override; +}; diff --git a/src/nodes/cifSuperMolecule.cpp b/src/nodes/cifSuperMolecule.cpp new file mode 100644 index 0000000000..af8b6421c6 --- /dev/null +++ b/src/nodes/cifSuperMolecule.cpp @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#include "nodes/cifSuperMolecule.h" + +CIFSuperMoleculeNode::CIFSuperMoleculeNode(Graph *parentGraph) : Node(parentGraph) +{ + // Inputs + addInput("CIFContext", "CIF handling context derived from parsing of CIF file", context_) + ->setFlags({ParameterBase::Required}); + + // Outputs + addOutput("NonPeriodicSpecies", "Non-periodic species", nonPeriodicSpecies_); +} + +std::string_view CIFSuperMoleculeNode::type() const { return "CIFSuperMolecule"; } + +std::string_view CIFSuperMoleculeNode::summary() const +{ + return "Create a single non-periodic species (useful for generating 'chunks' of crystal material)"; +} + +// Run main processing +NodeConstants::ProcessResult CIFSuperMoleculeNode::process() +{ + // Generate from CIF context + context_->generate(); + + // Get non-periodic species + supercellSpecies_->copyBasic(&(context_->supercellSpecies())); + supercellSpecies_->removePeriodicBonds(); + supercellSpecies_->removeBox(); + supercellSpecies_->updateIntramolecularTerms(); + nonPeriodicSpecies_ = supercellSpecies_.get(); + + return NodeConstants::ProcessResult::Success; +} diff --git a/src/nodes/cifSuperMolecule.h b/src/nodes/cifSuperMolecule.h new file mode 100644 index 0000000000..f96e0261b4 --- /dev/null +++ b/src/nodes/cifSuperMolecule.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#pragma once + +#include "nodes/cifLoader.h" +#include "nodes/node.h" + +// CIFLoader Node +class CIFSuperMoleculeNode : public Node +{ + public: + CIFSuperMoleculeNode(Graph *parentGraph); + ~CIFSuperMoleculeNode() override = default; + + public: + std::string_view type() const override; + std::string_view summary() const override; + + /* + * Definition + */ + private: + // CIF handler context + CIFLoaderNode::CIFContext *context_{nullptr}; + // Non-periodic species + const Species *nonPeriodicSpecies_{nullptr}; + // Supercell species + std::unique_ptr supercellSpecies_; + + /* + * Processing + */ + private: + // Run main processing + NodeConstants::ProcessResult process() override; +}; diff --git a/src/nodes/registry.cpp b/src/nodes/registry.cpp index a82e77b842..5cf5a4894d 100644 --- a/src/nodes/registry.cpp +++ b/src/nodes/registry.cpp @@ -7,6 +7,14 @@ #include "nodes/angle.h" #include "nodes/atomicMC/atomicMC.h" #include "nodes/bragg.h" +#include "nodes/cifBondingOptions.h" +#include "nodes/cifLoader.h" +#include "nodes/cifMolecularSpecies.h" +#include "nodes/cifPeriodicFramework.h" +#include "nodes/cifRemoveAtomic.h" +#include "nodes/cifRemoveWater.h" +#include "nodes/cifStructureCleanup.h" +#include "nodes/cifSuperMolecule.h" #include "nodes/configuration.h" #include "nodes/data1DImport.h" #include "nodes/derivative.h" @@ -24,6 +32,7 @@ #include "nodes/multiply.h" #include "nodes/neutronSQ/neutronSQ.h" #include "nodes/numberNode.h" +#include "nodes/setCIFAtomGroupActivity.h" #include "nodes/setCell.h" #include "nodes/siteRDF.h" #include "nodes/species.h" @@ -55,7 +64,16 @@ void NodeRegistry::instantiateNodeProducers() {"Angle", makeDerivedNode()}, {"AtomicMC", makeDerivedNode()}, {"Bragg", makeDerivedNode()}, + {"CIFStructureCleanup", makeDerivedNode()}, {"Configuration", makeDerivedNode()}, + {"CIFBondingOptions", makeDerivedNode()}, + {"CIFLoader", makeDerivedNode()}, + {"CIFMolecularSpecies", makeDerivedNode()}, + {"CIFPeriodicFramework", makeDerivedNode()}, + {"CIFRemoveAtomic", makeDerivedNode()}, + {"CIFRemoveWater", makeDerivedNode()}, + {"CIFSuperMolecule", makeDerivedNode()}, + {"SetCIFAtomGroupActivity", makeDerivedNode()}, {"Data1DImport", makeDerivedNode()}, {"Derivative", makeDerivedNode()}, {"DotProduct", makeDerivedNode()}, diff --git a/src/nodes/setCIFAtomGroupActivity.cpp b/src/nodes/setCIFAtomGroupActivity.cpp new file mode 100644 index 0000000000..3f5d69a5ab --- /dev/null +++ b/src/nodes/setCIFAtomGroupActivity.cpp @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#include "nodes/setCIFAtomGroupActivity.h" + +SetCIFAtomGroupActivityNode::SetCIFAtomGroupActivityNode(Graph *parentGraph) : Node(parentGraph) +{ + // Inputs + addInput("CIFContext", "CIF handling context derived from parsing of CIF file", context_) + ->setFlags({ParameterBase::Required}); + + // Outputs + addOutput("CIFContext", "CIF handling context derived from parsing of CIF file", context_); + + // Options + addOption("Assembly", "CIF assembly name", assemblyName_); + addOption("AtomGroup", "CIF atom group name", atomGroupName_); + addOption("SetActive", "Activity status of selected CIF atom group", active_); +} + +std::string_view SetCIFAtomGroupActivityNode::type() const { return "SetCIFAtomGroupActivity"; } + +std::string_view SetCIFAtomGroupActivityNode::summary() const +{ + return "Set activity of CIF assembly atom groups and apply to a CIF context"; +} + +// Run main processing +NodeConstants::ProcessResult SetCIFAtomGroupActivityNode::process() +{ + + auto &atomGroup = context_->getAssembly(assemblyName_).getGroup(atomGroupName_); + atomGroup.setActive(active_); + + return NodeConstants::ProcessResult::Success; +} diff --git a/src/nodes/setCIFAtomGroupActivity.h b/src/nodes/setCIFAtomGroupActivity.h new file mode 100644 index 0000000000..93b532af89 --- /dev/null +++ b/src/nodes/setCIFAtomGroupActivity.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#pragma once + +#include "nodes/cifLoader.h" +#include "nodes/node.h" + +// CIFLoader Node +class SetCIFAtomGroupActivityNode : public Node +{ + public: + SetCIFAtomGroupActivityNode(Graph *parentGraph); + ~SetCIFAtomGroupActivityNode() override = default; + + public: + std::string_view type() const override; + std::string_view summary() const override; + + /* + * Definition + */ + private: + // CIF handler context + CIFLoaderNode::CIFContext *context_{nullptr}; + // Selected CIF assembly atom group name + std::string atomGroupName_; + // Selected CIF assembly name + std::string assemblyName_; + // Activity status of CIF atom group + bool active_; + + /* + * Processing + */ + private: + // Run main processing + NodeConstants::ProcessResult process() override; +}; diff --git a/tests/nodes/CMakeLists.txt b/tests/nodes/CMakeLists.txt index 3061a83f10..e0bb69e8bf 100644 --- a/tests/nodes/CMakeLists.txt +++ b/tests/nodes/CMakeLists.txt @@ -2,6 +2,7 @@ dissolve_add_test(SRC angle.cpp) dissolve_add_test(SRC atomicMC.cpp) dissolve_add_test(SRC bragg.cpp) dissolve_add_test(SRC broadening.cpp) +dissolve_add_test(SRC cif.cpp) dissolve_add_test(SRC flow.cpp) dissolve_add_test(SRC gr.cpp) dissolve_add_test(SRC graph.cpp) diff --git a/tests/nodes/cif.cpp b/tests/nodes/cif.cpp new file mode 100644 index 0000000000..e448b0338d --- /dev/null +++ b/tests/nodes/cif.cpp @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2026 Team Dissolve and contributors + +#include "classes/empiricalFormula.h" +#include "io/import/species.h" +#include "nodes/cifLoader.h" +#include "nodes/cifMolecularSpecies.h" +#include "tests/graphData.h" +#include "tests/testData.h" +#include +#include + +namespace UnitTest +{ +class CIFNodeTest : public ::testing::Test +{ + public: + CIFNodeTest() = default; + ~CIFNodeTest() = default; + + protected: + GraphTestData testData_; + const std::string delimiter_{".cif"}; + const std::string path_{"cif/"}; + + public: + // Molecular species information + using MolecularSpeciesInfo = std::tuple; + // Create CIF graph + void createGraph(std::string filename) + { + auto name = cifNameFromFile(filename); + auto loaderNode = testData_.graphRoot.createNode("CIFLoader", name); + loaderNode->setOption("FilePath", path_ + filename); + auto bondingNode = testData_.graphRoot.createNode("CIFBondingOptions", name + "//BondingOptions"); + auto removeAtomicNode = testData_.graphRoot.createNode("CIFRemoveAtomic", name + "//RemoveAtomic"); + auto removeWaterNode = testData_.graphRoot.createNode("CIFRemoveWater", name + "//RemoveWater"); + auto structureCleanupNode = testData_.graphRoot.createNode("CIFStructureCleanup", name + "//StructureCleanup"); + auto molecularSpeciesNode = testData_.graphRoot.createNode("CIFMolecularSpecies", name + "//MolecularSpecies"); + testData_.graphRoot.addEdge({name, "CIFContext", name + "//BondingOptions", "CIFContext"}); + testData_.graphRoot.addEdge({name + "//BondingOptions", "CIFContext", name + "//RemoveAtomic", "CIFContext"}); + testData_.graphRoot.addEdge({name + "//RemoveAtomic", "CIFContext", name + "//RemoveWater", "CIFContext"}); + testData_.graphRoot.addEdge({name + "//RemoveWater", "CIFContext", name + "//StructureCleanup", "CIFContext"}); + testData_.graphRoot.addEdge({name + "//StructureCleanup", "CIFContext", name + "//MolecularSpecies", "CIFContext"}); + } + // Determine CIF node name from filename + std::string cifNameFromFile(std::string filename) + { + auto name = filename.substr(0, filename.find(delimiter_)); + return name; + } + // Retrieve CIF context by filename + CIFLoaderNode::CIFContext *getContextByFileName(std::string filename) + { + auto name = cifNameFromFile(filename); + auto node = testData_.graphRoot.findNode(name); + auto context = node->getOutputValue("CIFContext"); + return context; + } + // Test Box definition + void testBox(const Configuration *cfg, const Vector3 &lengths, const Vector3 &angles, int nAtoms) + { + ASSERT_TRUE(cfg); + EXPECT_EQ(cfg->nAtoms(), nAtoms); + EXPECT_NEAR(cfg->box()->axisLengths().x, lengths.x, 1.0e-6); + EXPECT_NEAR(cfg->box()->axisLengths().y, lengths.y, 1.0e-6); + EXPECT_NEAR(cfg->box()->axisLengths().z, lengths.z, 1.0e-6); + EXPECT_NEAR(cfg->box()->axisAngles().x, angles.x, 1.0e-6); + EXPECT_NEAR(cfg->box()->axisAngles().y, angles.y, 1.0e-6); + EXPECT_NEAR(cfg->box()->axisAngles().z, angles.z, 1.0e-6); + } + // Test molecular species information provided + void testMolecularSpecies(const CIFMolecularSpecies &molSp, const MolecularSpeciesInfo &info) + { + EXPECT_EQ(molSp.species()->name(), std::get<0>(info)); + EXPECT_EQ(molSp.instances().size(), std::get<1>(info)); + EXPECT_EQ(molSp.species()->nAtoms(), std::get<2>(info)); + } + // Check instance consistency with reference coordinates + void testInstanceConsistency(const CIFMolecularSpecies &molSp, const Species &referenceCoordinates) + { + // Get the box from the reference species + const auto *box = referenceCoordinates.box(); + + // Loop over instances and ensure their stored atoms overlap exactly with one in the reference system + for (const auto &instance : molSp.instances()) + { + for (auto &&[instanceAtom, speciesAtom] : zip(instance.localAtoms(), molSp.species()->atoms())) + { + // Locate the atom in the reference system at the instance atom coordinates + auto instanceR = instanceAtom.r(); + auto spAtomIt = std::find_if(referenceCoordinates.atoms().begin(), referenceCoordinates.atoms().end(), + [box, instanceR](const auto &refAtom) + { return box->minimumDistance(refAtom.r(), instanceR) < 0.01; }); + std::cout << std::format("{} {} {} {}", Elements::symbol(speciesAtom.Z()), instanceAtom.r().x, + instanceAtom.r().y, instanceAtom.r().z) + << std::endl; + ASSERT_NE(spAtomIt, referenceCoordinates.atoms().end()); + EXPECT_EQ(spAtomIt->Z(), speciesAtom.Z()); + } + } + } +}; + +TEST_F(CIFNodeTest, Parse) +{ + // Test files + std::vector cifs = {"1557470.cif", "1557599.cif", "7705246.cif", "9000004.cif", "9000095.cif", "9000418.cif"}; + + for (auto &cif : cifs) + { + createGraph(cif); + ASSERT_EQ(testData_.graphRoot.findNode(cifNameFromFile(cif))->run(), NodeConstants::ProcessResult::Success); + } +} + +TEST_F(CIFNodeTest, NaCl) +{ + // Load the CIF file + auto cif = "NaCl-1000041.cif"; + createGraph(cif); + auto loaderNode = testData_.graphRoot.findNode(cifNameFromFile(cif)); + ASSERT_EQ(loaderNode->run(), NodeConstants::ProcessResult::Success); + + auto cifContext = getContextByFileName(cif); + ASSERT_TRUE(cifContext); + EXPECT_TRUE(cifContext->generate()); + + // Check basic info + auto molecularSpeciesNode = testData_.graphRoot.findNode(cifNameFromFile(cif) + "//MolecularSpecies"); + ASSERT_EQ(molecularSpeciesNode->run(), NodeConstants::ProcessResult::Success); + + EXPECT_EQ(cifContext->spaceGroup(), SpaceGroups::SpaceGroup_225); + constexpr double A = 5.62; + testBox(molecularSpeciesNode->getOutputValue("SuperCellConfiguration"), {A, A, A}, {90, 90, 90}, 8); + + // Calculating bonding is the default, but this gives a continuous framework... + EXPECT_EQ(molecularSpeciesNode->getOutputValue>("DetectedMolecularSpecies").size(), 0); + + // Get molecular species + auto bondingNode = testData_.graphRoot.findNode(cifNameFromFile(cif) + "//BondingOptions"); + bondingNode->setOption("UseCIFBondingDefinitions", true); + testData_.graphRoot.setUpdateRequired(); + ASSERT_EQ(molecularSpeciesNode->run(), NodeConstants::ProcessResult::Success); + + auto molecularSpecies = molecularSpeciesNode->getOutputValue>("DetectedMolecularSpecies"); + + EXPECT_EQ(molecularSpecies.size(), 2); + testMolecularSpecies(molecularSpecies.at(0), {"Na", 4, 1}); + std::vector R = {{0.0, 0.0, 0.0}, {0.0, A / 2, A / 2}, {A / 2, 0.0, A / 2}, {A / 2, A / 2, 0.0}}; + for (auto &&[instance, r2] : zip(molecularSpecies.at(0).instances(), R)) + DissolveSystemTest::checkVec3(instance.localAtoms()[0].r(), r2); + testMolecularSpecies(molecularSpecies.at(1), {"Cl", 4, 1}); + for (auto &&[instance, r2] : zip(molecularSpecies.at(1).instances(), R)) + DissolveSystemTest::checkVec3(instance.localAtoms()[0].r(), (r2 - A / 2).abs()); + + // 2x2x2 supercell + molecularSpeciesNode->setOption("SuperCellRepeat", {2, 2, 2}); + testData_.graphRoot.setUpdateRequired(); + ASSERT_EQ(molecularSpeciesNode->run(), NodeConstants::ProcessResult::Success); + testBox(molecularSpeciesNode->getOutputValue("SuperCellConfiguration"), {A * 2, A * 2, A * 2}, + {90, 90, 90}, 8 * 8); +} + +TEST_F(CIFNodeTest, NaClO3) +{ + // Load the CIF file + auto cif = "NaClO3-1010057.cif"; + createGraph(cif); + ASSERT_EQ(testData_.graphRoot.findNode(cifNameFromFile(cif))->run(), NodeConstants::ProcessResult::Success); + + auto cifContext = getContextByFileName(cif); + ASSERT_TRUE(cifContext); + EXPECT_TRUE(cifContext->generate()); + + // Check basic info + auto molecularSpeciesNode = testData_.graphRoot.findNode(cifNameFromFile(cif) + "//MolecularSpecies"); + ASSERT_EQ(molecularSpeciesNode->run(), NodeConstants::ProcessResult::Success); + + EXPECT_EQ(cifContext->spaceGroup(), SpaceGroups::SpaceGroup_198); + constexpr double A = 6.55; + testBox(molecularSpeciesNode->getOutputValue("SuperCellConfiguration"), {A, A, A}, {90, 90, 90}, 20); + + // Turn off automatic bond calculation - there are no bonding defs in the CIF, so we expect species for each atomic + // component (4 Na, 4 Cl, and 12 O) + auto bondingNode = testData_.graphRoot.findNode(cifNameFromFile(cif) + "//BondingOptions"); + bondingNode->setOption("UseCIFBondingDefinitions", true); + testData_.graphRoot.setUpdateRequired(); + ASSERT_EQ(molecularSpeciesNode->run(), NodeConstants::ProcessResult::Success); + + auto cifMolsA = molecularSpeciesNode->getOutputValue>("DetectedMolecularSpecies"); + ASSERT_EQ(cifMolsA.size(), 3); + testMolecularSpecies(cifMolsA.at(0), {"Na", 4, 1}); + testMolecularSpecies(cifMolsA.at(1), {"Cl", 4, 1}); + testMolecularSpecies(cifMolsA.at(2), {"O", 12, 1}); + + // Calculate bonding ourselves to get the correct species + bondingNode->setOption("UseCIFBondingDefinitions", false); + testData_.graphRoot.setUpdateRequired(); + ASSERT_EQ(molecularSpeciesNode->run(), NodeConstants::ProcessResult::Success); + auto cifMolsB = molecularSpeciesNode->getOutputValue>("DetectedMolecularSpecies"); + ASSERT_EQ(cifMolsB.size(), 2); + testMolecularSpecies(cifMolsB.at(0), {"Na", 4, 1}); + testMolecularSpecies(cifMolsB.at(1), {"ClO3", 4, 4}); +} + +TEST_F(CIFNodeTest, CuBTC) +{ + // Load the CIF file + auto cif = "CuBTC-7108574.cif"; + createGraph(cif); + ASSERT_EQ(testData_.graphRoot.findNode(cifNameFromFile(cif))->run(), NodeConstants::ProcessResult::Success); + + auto cifContext = getContextByFileName(cif); + ASSERT_TRUE(cifContext); + EXPECT_TRUE(cifContext->generate()); + + // Check basic info + auto molecularSpeciesNode = testData_.graphRoot.findNode(cifNameFromFile(cif) + "//MolecularSpecies"); + ASSERT_EQ(molecularSpeciesNode->run(), NodeConstants::ProcessResult::Success); + + EXPECT_EQ(cifContext->spaceGroup(), SpaceGroups::SpaceGroup_225); + constexpr auto A = 26.3336; + testBox(molecularSpeciesNode->getOutputValue("SuperCellConfiguration"), {A, A, A}, {90, 90, 90}, 672); + + // 16 basic formula units per unit cell + constexpr auto N = 16; + + // Check basic formula (which includes bound water oxygens - with no H - at this point) and using O group + EmpiricalFormula::EmpiricalFormulaMap cellFormulaH = { + {Elements::Cu, 3 * N}, {Elements::C, 18 * N}, {Elements::H, 6 * N}, {Elements::O, 15 * N}}; + EXPECT_EQ( + EmpiricalFormula::formula(molecularSpeciesNode->getOutputValue("SuperCellConfiguration")->atoms(), + [](const auto &i) { return i.speciesAtom()->Z(); }), + EmpiricalFormula::formula(cellFormulaH)); + auto cifMolsA = molecularSpeciesNode->getOutputValue>("DetectedMolecularSpecies"); + EXPECT_EQ(cifMolsA.size(), 2); + + // Change active assemblies to get amine-substituted structure + EmpiricalFormula::EmpiricalFormulaMap cellFormulaNH2 = cellFormulaH; + cellFormulaNH2[Elements::N] = 6 * N; + cellFormulaNH2[Elements::H] *= 2; + auto atomGroupA1 = testData_.graphRoot.createNode("SetCIFAtomGroupActivity", cifNameFromFile(cif) + "//AtomGroupA1"); + atomGroupA1->setOption("Assembly", std::string("A")); + atomGroupA1->setOption("AtomGroup", std::string("1")); + atomGroupA1->setOption("SetActive", false); + auto atomGroupB2 = testData_.graphRoot.createNode("SetCIFAtomGroupActivity", cifNameFromFile(cif) + "//AtomGroupB2"); + atomGroupB2->setOption("Assembly", std::string("B")); + atomGroupB2->setOption("AtomGroup", std::string("2")); + atomGroupB2->setOption("SetActive", true); + auto atomGroupC2 = testData_.graphRoot.createNode("SetCIFAtomGroupActivity", cifNameFromFile(cif) + "//AtomGroupC2"); + atomGroupC2->setOption("Assembly", std::string("C")); + atomGroupC2->setOption("AtomGroup", std::string("2")); + atomGroupC2->setOption("SetActive", true); + testData_.graphRoot.removeEdge( + {cifNameFromFile(cif) + "//StructureCleanup", "CIFContext", std::string(molecularSpeciesNode->name()), "CIFContext"}); + testData_.graphRoot.addEdge( + {cifNameFromFile(cif) + "//StructureCleanup", "CIFContext", std::string(atomGroupA1->name()), "CIFContext"}); + testData_.graphRoot.addEdge( + {std::string(atomGroupA1->name()), "CIFContext", std::string(atomGroupB2->name()), "CIFContext"}); + testData_.graphRoot.addEdge( + {std::string(atomGroupB2->name()), "CIFContext", std::string(atomGroupC2->name()), "CIFContext"}); + testData_.graphRoot.addEdge( + {std::string(atomGroupC2->name()), "CIFContext", std::string(molecularSpeciesNode->name()), "CIFContext"}); + testData_.graphRoot.setUpdateRequired(); + ASSERT_EQ(molecularSpeciesNode->run(), NodeConstants::ProcessResult::Success); + EXPECT_EQ( + EmpiricalFormula::formula(molecularSpeciesNode->getOutputValue("SuperCellConfiguration")->atoms(), + [](const auto &i) { return i.speciesAtom()->Z(); }), + EmpiricalFormula::formula(cellFormulaNH2)); + + // Remove those free oxygens so we just have a framework + auto removeAtomicsNode = testData_.graphRoot.findNode(cifNameFromFile(cif) + "//RemoveAtomic"); + removeAtomicsNode->setOption("RemoveAtomics", true); + testData_.graphRoot.setUpdateRequired(); + ASSERT_EQ(molecularSpeciesNode->run(), NodeConstants::ProcessResult::Success); + auto cifMolsB = molecularSpeciesNode->getOutputValue>("DetectedMolecularSpecies"); + EXPECT_EQ(cifMolsB.size(), 0); +} + +TEST_F(CIFNodeTest, MoleculeOrdering) +{ + const auto cifFiles = {"molecule-test-simple-ordered.cif", "molecule-test-simple-unordered.cif", + "molecule-test-simple-unordered-rotated.cif"}; + for (auto cifFile : cifFiles) + { + // Load the CIF file + createGraph(cifFile); + ASSERT_EQ(testData_.graphRoot.findNode(cifNameFromFile(cifFile))->run(), NodeConstants::ProcessResult::Success); + + auto cifContext = getContextByFileName(cifFile); + ASSERT_TRUE(cifContext); + EXPECT_TRUE(cifContext->generate()); + + auto molecularSpeciesNode = testData_.graphRoot.findNode(cifNameFromFile(cifFile) + "//MolecularSpecies"); + ASSERT_EQ(molecularSpeciesNode->run(), NodeConstants::ProcessResult::Success); + + auto molecularSpecies = + molecularSpeciesNode->getOutputValue>("DetectedMolecularSpecies"); + EXPECT_EQ(molecularSpecies.size(), 1); + + auto &cifMolecule = molecularSpecies.front(); + EmpiricalFormula::EmpiricalFormulaMap moleculeFormula = { + {Elements::Cl, 1}, {Elements::O, 1}, {Elements::C, 1}, {Elements::H, 3}}; + testMolecularSpecies(cifMolecule, {EmpiricalFormula::formula(moleculeFormula), 6, 6}); + + auto &unitCellSpecies = static_cast(molecularSpeciesNode)->cleanedUnitCellSpecies(); + testInstanceConsistency(cifMolecule, unitCellSpecies); + } +} + +TEST_F(CIFNodeTest, BigMoleculeOrdering) +{ + const auto cifFile = "Bisphen_n_arenes_1517789.cif"; + createGraph(cifFile); + ASSERT_EQ(testData_.graphRoot.findNode(cifNameFromFile(cifFile))->run(), NodeConstants::ProcessResult::Success); + + auto cifContext = getContextByFileName(cifFile); + ASSERT_TRUE(cifContext); + EXPECT_TRUE(cifContext->generate()); + + auto molecularSpeciesNode = testData_.graphRoot.findNode(cifNameFromFile(cifFile) + "//MolecularSpecies"); + ASSERT_EQ(molecularSpeciesNode->run(), NodeConstants::ProcessResult::Success); + + auto molecularSpecies = molecularSpeciesNode->getOutputValue>("DetectedMolecularSpecies"); + EXPECT_EQ(molecularSpecies.size(), 1); + + auto &cifMolecule = molecularSpecies.front(); + EmpiricalFormula::EmpiricalFormulaMap moleculeFormula = {{Elements::O, 6}, {Elements::C, 51}, {Elements::H, 54}}; + testMolecularSpecies(cifMolecule, {EmpiricalFormula::formula(moleculeFormula), 4, 111}); + + auto &unitCellSpecies = static_cast(molecularSpeciesNode)->cleanedUnitCellSpecies(); + testInstanceConsistency(cifMolecule, unitCellSpecies); +} + +} // namespace UnitTest \ No newline at end of file