@@ -613,6 +613,162 @@ TEST_CASE("Bookkeeper flags an inconsistent bend strength", "[bookkeeper]") {
613613 delete_tree (lat.leftover );
614614}
615615
616+ TEST_CASE (" Bookkeeper defaults the bend actual field from g_ref" ,
617+ " [bookkeeper]" ) {
618+ // Kn0_from_g_ref (bend.md) is true by default, so a bend that says nothing
619+ // about its actual field bends its own reference particle along the
620+ // reference curve: Kn0 = g_ref. The element carries no MagneticMultipoleP,
621+ // so the group is created to hold it, and the other three forms of the
622+ // dipole component follow as usual.
623+ struct lattices lat = expand_src (one_ele_yaml (
624+ " - bnd:\n "
625+ " kind: Bend\n "
626+ " length: 2.0\n "
627+ " BendP:\n "
628+ " g_ref: 0.1\n " ));
629+ YAMLTreeHandle t = lat.expanded ;
630+
631+ REQUIRE (close (param_num (t, " bnd" , " MagneticMultipoleP" , " Kn0" ), 0.1 ));
632+ REQUIRE (close (param_num (t, " bnd" , " MagneticMultipoleP" , " Kn0L" ), 0.2 ));
633+ REQUIRE (close_rel (param_num (t, " bnd" , " MagneticMultipoleP" , " Bn0" ),
634+ 0.1 / electron_factor ()));
635+ REQUIRE (close_rel (param_num (t, " bnd" , " MagneticMultipoleP" , " Bn0L" ),
636+ 0.2 / electron_factor ()));
637+ // The flag itself is a non-false default of a present group, so it is made
638+ // explicit alongside the enum defaults.
639+ YAMLNodeId bp = get_child_by_key (t, find_by_key (t, " bnd" ), " BendP" );
640+ REQUIRE (val_eq (t, get_child_by_key (t, bp, " Kn0_from_g_ref" ), " true" ));
641+ REQUIRE_FALSE (any_problem_contains (lat, " inconsistent" ));
642+
643+ free_lattice_problems (lat.problems );
644+ delete_tree (lat.original );
645+ delete_tree (lat.combined );
646+ delete_tree (lat.expanded );
647+ delete_tree (lat.leftover );
648+ }
649+
650+ TEST_CASE (" Bookkeeper writes no actual field when Kn0_from_g_ref is false" ,
651+ " [bookkeeper]" ) {
652+ // The author has said the actual field is not the reference one. Nothing is
653+ // written, and the bend keeps no multipole group at all.
654+ struct lattices lat = expand_src (one_ele_yaml (
655+ " - bnd:\n "
656+ " kind: Bend\n "
657+ " length: 2.0\n "
658+ " BendP:\n "
659+ " g_ref: 0.1\n "
660+ " Kn0_from_g_ref: false\n " ));
661+ YAMLTreeHandle t = lat.expanded ;
662+
663+ REQUIRE (get_child_by_key (t, find_by_key (t, " bnd" ),
664+ " MagneticMultipoleP" ) == YAML_NULL_ID );
665+ // The reference bend is untouched: only the actual field is being declined.
666+ REQUIRE (close (param_num (t, " bnd" , " BendP" , " angle_ref" ), 0.2 ));
667+
668+ free_lattice_problems (lat.problems );
669+ delete_tree (lat.original );
670+ delete_tree (lat.combined );
671+ delete_tree (lat.expanded );
672+ delete_tree (lat.leftover );
673+ }
674+
675+ TEST_CASE (" Bookkeeper keeps an actual bend field the author set" ,
676+ " [bookkeeper]" ) {
677+ // Kn0 is set to something other than g_ref -- a bend running off its
678+ // reference field, which is exactly what the parameters are separate for.
679+ // The default does not apply, and the difference is not an inconsistency.
680+ struct lattices lat = expand_src (one_ele_yaml (
681+ " - bnd:\n "
682+ " kind: Bend\n "
683+ " length: 2.0\n "
684+ " BendP:\n "
685+ " g_ref: 0.1\n "
686+ " MagneticMultipoleP:\n "
687+ " Kn0: 0.3\n " ));
688+ YAMLTreeHandle t = lat.expanded ;
689+
690+ REQUIRE (close (param_num (t, " bnd" , " MagneticMultipoleP" , " Kn0" ), 0.3 ));
691+ REQUIRE (close (param_num (t, " bnd" , " MagneticMultipoleP" , " Kn0L" ), 0.6 ));
692+ REQUIRE (close (param_num (t, " bnd" , " BendP" , " g_ref" ), 0.1 ));
693+ REQUIRE_FALSE (any_problem_contains (lat, " inconsistent" ));
694+
695+ free_lattice_problems (lat.problems );
696+ delete_tree (lat.original );
697+ delete_tree (lat.combined );
698+ delete_tree (lat.expanded );
699+ delete_tree (lat.leftover );
700+ }
701+
702+ TEST_CASE (" Bookkeeper counts an integrated actual field as set" ,
703+ " [bookkeeper]" ) {
704+ // Kn0L states the same field as Kn0, just integrated over the length, so
705+ // the default does not apply: Kn0 comes from Kn0L / length = 0.3, not from
706+ // g_ref. A Kn0 of 0.1 here would have contradicted the author's own value.
707+ struct lattices lat = expand_src (one_ele_yaml (
708+ " - bnd:\n "
709+ " kind: Bend\n "
710+ " length: 2.0\n "
711+ " BendP:\n "
712+ " g_ref: 0.1\n "
713+ " MagneticMultipoleP:\n "
714+ " Kn0L: 0.6\n " ));
715+ YAMLTreeHandle t = lat.expanded ;
716+
717+ REQUIRE (close (param_num (t, " bnd" , " MagneticMultipoleP" , " Kn0" ), 0.3 ));
718+ REQUIRE_FALSE (any_problem_contains (lat, " inconsistent" ));
719+
720+ free_lattice_problems (lat.problems );
721+ delete_tree (lat.original );
722+ delete_tree (lat.combined );
723+ delete_tree (lat.expanded );
724+ delete_tree (lat.leftover );
725+ }
726+
727+ TEST_CASE (" Bookkeeper adds the actual field to a group of other multipoles" ,
728+ " [bookkeeper]" ) {
729+ // A different order says nothing about the dipole component: the quadrupole
730+ // the author gave stays, and Kn0 joins it in the group already there.
731+ struct lattices lat = expand_src (one_ele_yaml (
732+ " - bnd:\n "
733+ " kind: Bend\n "
734+ " length: 2.0\n "
735+ " BendP:\n "
736+ " g_ref: 0.1\n "
737+ " MagneticMultipoleP:\n "
738+ " Kn1: 0.5\n " ));
739+ YAMLTreeHandle t = lat.expanded ;
740+
741+ REQUIRE (close (param_num (t, " bnd" , " MagneticMultipoleP" , " Kn0" ), 0.1 ));
742+ REQUIRE (close (param_num (t, " bnd" , " MagneticMultipoleP" , " Kn1" ), 0.5 ));
743+
744+ free_lattice_problems (lat.problems );
745+ delete_tree (lat.original );
746+ delete_tree (lat.combined );
747+ delete_tree (lat.expanded );
748+ delete_tree (lat.leftover );
749+ }
750+
751+ TEST_CASE (" Bookkeeper gives a straight bend no actual field" , " [bookkeeper]" ) {
752+ // No curvature, so there is no reference field to copy and nothing to hold:
753+ // a zero Kn0 is not written, and no group is created for it.
754+ struct lattices lat = expand_src (one_ele_yaml (
755+ " - bnd:\n "
756+ " kind: Bend\n "
757+ " length: 2.0\n "
758+ " BendP:\n "
759+ " e1: 0.01\n " ));
760+ YAMLTreeHandle t = lat.expanded ;
761+
762+ REQUIRE (get_child_by_key (t, find_by_key (t, " bnd" ),
763+ " MagneticMultipoleP" ) == YAML_NULL_ID );
764+
765+ free_lattice_problems (lat.problems );
766+ delete_tree (lat.original );
767+ delete_tree (lat.combined );
768+ delete_tree (lat.expanded );
769+ delete_tree (lat.leftover );
770+ }
771+
616772TEST_CASE (" Bookkeeper flags an inconsistent multipole component" ,
617773 " [bookkeeper]" ) {
618774 // Bn1 1.2 over length 0.5 implies Bn1L 0.6, but Bn1L is set to 0.5.
@@ -654,7 +810,8 @@ TEST_CASE("Bookkeeper materializes RF enum defaults and L_active",
654810
655811TEST_CASE (" Bookkeeper leaves absent parameter groups alone" , " [bookkeeper]" ) {
656812 // Groups not present in the file are not materialized: a plain drift gains no
657- // BendP/RFP/multipole groups. Only ReferenceP and FloorP are parser-added.
813+ // BendP/RFP/multipole groups. Only ReferenceP and FloorP are parser-added,
814+ // and MagneticMultipoleP on a curved bend, which has an actual field to hold.
658815 struct lattices lat = expand_bookkeeper ();
659816 YAMLTreeHandle t = lat.expanded ;
660817 YAMLNodeId d1 = find_by_key (t, " d1" );
0 commit comments