@@ -485,134 +485,173 @@ simple rename with no logic changes.
485485
486486## Type Safety in Practice
487487
488- The following example contrasts a typical `double`-based HEP API with its mp-units
489- equivalent. It covers all four categories of bugs that specialized quantity types
490- eliminate: dimensional errors, argument swaps, unit scale mismatches, and same-kind
491- confusion .
488+ The following examples demonstrate four categories of bugs that specialized quantity
489+ types eliminate: dimensional errors, argument swaps, unit scale mismatches, and
490+ same-kind confusion. Each example compares legacy `double`-based code with the
491+ type-safe mp-units equivalent .
492492
493- === "Legacy (unsafe)"
493+ For all examples, consider this typical HEP API:
494+
495+ === "Legacy API"
494496
495497 ```cpp
496- // Typical HEP API - all parameters are just double
498+ // All parameters are just double - units documented in comments
497499 void setParticleProperties(double mass_MeV_per_c2, // Mass in MeV/c²
498500 double width_MeV, // Decay width in MeV
499501 double charge); // Electric charge
502+ ```
500503
501- // DISASTER 1: Wrong dimensions (energy passed as mass)
502- {
503- double higgs_mass = 125.0 * GeV; // Stores 125'000.0 (energy in MeV, not mass!)
504- double higgs_width = 4.0 * MeV; // Stores 4.0
504+ === "Type-safe API"
505505
506- setParticleProperties(higgs_mass, higgs_width, 0.0);
507- // Passes 125'000.0 as mass_MeV_per_c2 - but it's energy in MeV!
508- // Should have divided by c_squared first
509- // ✓ Compiles ☠️ Wrong physics ❌ No warning
510- }
506+ ```cpp
507+ // Types enforce correct dimensions and units at compile time
508+ void setParticleProperties(quantity<hep::rest_mass[MeV / c2]> mass,
509+ quantity<hep::decay_width[MeV]> width,
510+ quantity<hep::electric_charge[eplus]> charge);
511+ ```
511512
512- // DISASTER 2: Arguments swapped
513- {
514- double higgs_mass = 125.0 * GeV / c_squared; // Stores correct mass value
515- double higgs_width = 4.0 * MeV; // Stores 4.0
513+ ### Wrong Dimensions
516514
517- setParticleProperties(higgs_width, // should be higgs_mass!
518- higgs_mass, // should be higgs_width!
519- 0.0);
520- // Results: mass=4.0 MeV/c² (should be 125'000 MeV/c²), width has mass dimension!
521- // ✓ Compiles ☠️ Catastrophic ❌ No warning
522- }
515+ Passing a value with incorrect physical dimensions is a common error. For example,
516+ passing _energy_ (E) where _mass_ (E/c²) is expected. In legacy code, the compiler
517+ cannot detect this — both are just `double`. The wrong numerical value flows through
518+ calculations, producing physically incorrect results.
523519
524- // DISASTER 3: Wrong unit scale (ROOT GeV vs CLHEP MeV framework mismatch)
525- {
526- double higgs_mass = 125.0 * GeV / c_squared; // Stores correct mass value
527- double higgs_width = get_from_ROOT(); // Stores 0.004 (ROOT GeV base: 4 MeV = 0.004 GeV)
528-
529- // get_from_ROOT() returns 0.004 - correct in ROOT (GeV=1), but CLHEP API expects MeV!
530- setParticleProperties(higgs_mass, // correct mass value
531- higgs_width, // 0.004 → API reads as 0.004 MeV (should be 4 MeV!)
532- 0.0);
533- // Forgot ROOT→CLHEP conversion: value needs ×1000 to be in MeV!
534- // Results: width=0.004 MeV (should be 4 MeV) - off by 1000×!
535- // ✓ Compiles ☠️ 1000× wrong ❌ No warning
536- }
520+ === "Legacy (unsafe)"
537521
538- // DISASTER 4: Wrong quantity of the same kind (invariant_mass vs rest_mass)
539- {
540- double m_inv = compute_invariant_mass(); // reconstructed from 4-momenta, in MeV/c²
541- double higgs_width = 4.0 * MeV;
542-
543- setParticleProperties(m_inv, // invariant mass of a candidate event
544- higgs_width,
545- 0.0);
546- // Invariant mass of a single event ≠ rest mass of the Higgs boson!
547- // Both are mass dimension (MeV/c²), but physically different concepts
548- // ✓ Compiles ☠️ Wrong physics ❌ No warning
549- }
522+ ```cpp
523+ double higgs_mass = 125.0 * GeV; // Energy in MeV (125'000.0), not mass!
524+ double higgs_width = 4.0 * MeV;
525+
526+ // Should have divided by c_squared first - but compiler doesn't know!
527+ setParticleProperties(higgs_mass, higgs_width, 0.0);
528+ // Passes 125'000.0 as mass_MeV_per_c2 - catastrophically wrong
529+ // ✓ Compiles ☠️ Wrong physics ❌ No warning
550530 ```
551531
552532=== "mp-units (safe)"
553533
554534 ```cpp
555- // Type-safe API - compiler enforces correct types
556- void setParticleProperties(quantity<hep::rest_mass[MeV / c2]> mass,
557- quantity<hep::decay_width[MeV]> width,
558- quantity<hep::electric_charge[eplus]> charge);
535+ quantity higgs_mass = 125.0 * GeV; // Compiler knows this is energy
536+ quantity higgs_width = 4.0 * MeV;
559537
560- // ERROR 1: Wrong dimensions caught at compile time
561- {
562- quantity higgs_mass = 125.0 * GeV; // energy quantity, not mass
563- quantity higgs_width = 4.0 * MeV;
538+ // setParticleProperties(higgs_mass, higgs_width, 0.0 * eplus);
539+ // Compile error! Cannot convert energy to mass
540+ // "note: no known conversion from energy to rest_mass"
564541
565- // setParticleProperties(higgs_mass, higgs_width, 0.0 * eplus);
566- // Compile error! Cannot convert quantity<GeV> to rest_mass
567- // "note: no known conversion from energy to rest_mass"
542+ // Must explicitly convert - the dimension error is caught:
543+ setParticleProperties(higgs_mass / c2, higgs_width, 0.0 * eplus); // ✓
544+ ```
568545
569- // Must explicitly convert energy to mass:
570- setParticleProperties(higgs_mass / c2, higgs_width, 0.0 * eplus); // ✓
571- }
546+ ### Arguments Swapped
572547
573- // ERROR 2: Swapped arguments caught at compile time
574- {
575- quantity higgs_mass = 125.0 * GeV / c2;
576- quantity higgs_width = 4.0 * MeV;
548+ Function parameters with the same underlying type (`double`) but different semantic
549+ meanings can be accidentally swapped. The compiler has no way to detect this in
550+ legacy code because all parameters have identical types.
577551
578- // setParticleProperties(higgs_width, higgs_mass, 0.0 * eplus);
579- // Compile error! Cannot convert decay_width to rest_mass
580- // "note: no known conversion from decay_width to rest_mass"
552+ === "Legacy (unsafe)"
581553
582- setParticleProperties(higgs_mass, higgs_width, 0.0 * eplus); // ✓
583- }
554+ ```cpp
555+ double higgs_mass = 125.0 * GeV / c_squared;
556+ double higgs_width = 4.0 * MeV;
557+
558+ // Accidentally reversed the arguments!
559+ setParticleProperties(higgs_width, // should be higgs_mass
560+ higgs_mass, // should be higgs_width
561+ 0.0);
562+ // Results: mass=4.0 MeV/c² (off by 31'000×), width=125'000 MeV
563+ // ✓ Compiles ☠️ Catastrophic ❌ No warning
564+ ```
584565
585- // ERROR 3: Unit scale confusion caught - raw doubles rejected
586- {
587- quantity higgs_mass = 125.0 * GeV / c2;
588- double higgs_width = get_from_ROOT(); // 0.004
566+ === "mp-units (safe)"
589567
590- // setParticleProperties(higgs_mass, higgs_width, 0.0 * eplus);
591- // Compile error! Cannot convert double to quantity<decay_width[MeV]>
592- // Forces explicit unit attachment - you must decide: GeV or MeV?
568+ ```cpp
569+ quantity higgs_mass = 125.0 * GeV / c2;
570+ quantity higgs_width = 4.0 * MeV;
593571
594- // Correct: attach the right unit and conversion is automatic
595- setParticleProperties(higgs_mass, higgs_width * GeV, 0.0 * eplus);
596- // ✓ Compiles! 0.004 * GeV = 4 MeV, converted automatically
597- }
572+ // setParticleProperties(higgs_width, higgs_mass, 0.0 * eplus);
573+ // Compile error! Types don't match
574+ // "note: no known conversion from decay_width to rest_mass"
598575
599- // ERROR 4: Wrong quantity of the same kind caught at compile time
600- {
601- quantity<hep::invariant_mass[GeV / c2]> compute_invariant_mass();
576+ setParticleProperties(higgs_mass, higgs_width, 0.0 * eplus); // ✓
577+ ```
602578
603- quantity m_inv = compute_invariant_mass(); // from 4-momenta
604- quantity higgs_width = hep::decay_width(4.0 * MeV);
579+ ### Wrong Unit Scale (Framework Mismatch)
605580
606- // setParticleProperties(m_inv, higgs_width, 0.0 * eplus);
607- // Compile error! Cannot convert invariant_mass to rest_mass
608- // "note: no known conversion from invariant_mass to rest_mass"
609- // Both are mass, but they are distinct quantity kinds
610- }
581+ Different HEP frameworks use different base units (ROOT uses GeV=1, CLHEP uses MeV=1).
582+ When interfacing between frameworks, forgetting to convert units produces values
583+ off by factors of 1000 or more. In legacy code, these are just `double` values —
584+ the compiler cannot detect the mismatch.
585+
586+ === "Legacy (unsafe)"
587+
588+ ```cpp
589+ double higgs_mass = 125.0 * GeV / c_squared;
590+ double higgs_width = get_from_ROOT(); // Returns 0.004 (ROOT uses GeV=1)
591+
592+ // Forgot to convert ROOT's GeV to CLHEP's MeV!
593+ setParticleProperties(higgs_mass, higgs_width, 0.0);
594+ // API expects MeV, receives 0.004 → width = 0.004 MeV (off by 1000×!)
595+ // ✓ Compiles ☠️ 1000× wrong ❌ No warning
611596 ```
612597
613- The mp-units version makes all four classes of error **impossible** — they're caught at
614- compile time before the code ever runs. The physics constraints are enforced by the type
615- system.
598+ === "mp-units (safe)"
599+
600+ ```cpp
601+ quantity higgs_mass = 125.0 * GeV / c2;
602+ double higgs_width = get_from_ROOT(); // Returns bare 0.004
603+
604+ // setParticleProperties(higgs_mass, higgs_width, 0.0 * eplus);
605+ // Compile error! Cannot convert double to quantity
606+ // Forces explicit unit attachment
607+
608+ // Must specify the unit - conversion happens automatically:
609+ setParticleProperties(higgs_mass, higgs_width * GeV, 0.0 * eplus);
610+ // ✓ Compiles! 0.004 * GeV = 4 MeV (automatic conversion)
611+ ```
612+
613+ ### Wrong Quantity of the Same Kind
614+
615+ Some physically distinct concepts share the same dimension but have different meanings.
616+ For example, _invariant mass_ reconstructed from decay products vs. _rest mass_ of a
617+ particle species. Mixing these is physically meaningless even though they're
618+ dimensionally identical. Legacy `double` code cannot distinguish them.
619+
620+ === "Legacy (unsafe)"
621+
622+ ```cpp
623+ // From 4-momenta reconstruction
624+ double compute_invariant_mass();
625+
626+ double m_inv = compute_invariant_mass();
627+ double higgs_width = 4.0 * MeV;
628+
629+ // Invariant mass of ONE EVENT ≠ rest mass of THE HIGGS BOSON
630+ setParticleProperties(m_inv, higgs_width, 0.0);
631+ // Both are mass dimension (MeV/c²) - compiler cannot tell them apart
632+ // ✓ Compiles ☠️ Wrong physics ❌ No warning
633+ ```
634+
635+ === "mp-units (safe)"
636+
637+ ```cpp
638+ // From 4-momenta reconstruction
639+ quantity<hep::invariant_mass[GeV / c2]> compute_invariant_mass();
640+
641+ quantity m_inv = compute_invariant_mass();
642+ quantity higgs_width = hep::decay_width(4.0 * MeV);
643+
644+ // setParticleProperties(m_inv, higgs_width, 0.0 * eplus);
645+ // Compile error! Wrong quantity kind
646+ // "note: no known conversion from invariant_mass to rest_mass"
647+ // Physics constraints enforced by type system
648+ ```
649+
650+ ### Summary
651+
652+ The mp-units type system makes all four classes of error **impossible** — they're
653+ caught at compile time before the code ever runs. The physics constraints are
654+ enforced by the compiler, not just by documentation and code review.
616655
617656The type hierarchy also captures physical relationships. For _energy_, adding child
618657quantities yields their common parent, enforcing E = KE + mc²:
0 commit comments