Skip to content

Latest commit

 

History

History
177 lines (145 loc) · 10.7 KB

File metadata and controls

177 lines (145 loc) · 10.7 KB

Physics Source Generator

The semantic quantities system is generated by Roslyn incremental generators in Semantics.SourceGenerators/, driven by metadata files in Semantics.SourceGenerators/Metadata/. This doc explains the schema and the workflow for adding or changing dimensions.

For the why (the unified vector model), see docs/strategy-unified-vector-quantities.md. For the runtime contracts (IVector0..IVector4, PhysicalQuantity<TSelf, T>), see Semantics.Quantities/.

What gets generated

Generator Output Notes
DimensionsGenerator PhysicalDimensions.g.cs One static record per dimension with its symbol and dimensional formula.
UnitsGenerator Units.g.cs All declared units with their conversion factors.
ConversionsGenerator ConversionConstants.g.cs Hard-coded conversion ratios (FeetToMeters, etc.) used by Units and operators.
MagnitudesGenerator MetricMagnitudes.g.cs SI prefixes and their numeric magnitudes.
PrecisionGenerator StorageTypes.g.cs The set of INumber<T> storage types the library opts into (double, float, decimal, …).
PhysicalConstantsGenerator PhysicalConstants.g.cs PhysicalConstants.Generic.X<T>() and PhysicalConstants.Conversion.X<T>() accessors backed by PreciseNumber.
QuantitiesGenerator one *.g.cs file per emitted type Vector0/V1/V2/V3/V4 bases, semantic overloads, factories, operators, magnitude extraction, dot/cross products.
LogarithmicScalesGenerator one *.g.cs file per logarithmic scale Decibel levels, pitch intervals, and pH from logarithmic.json: standalone readonly partial record structs with linear-quantity conversions, log-space arithmetic, and comparisons.

Outputs land under Semantics.Quantities/Generated/Semantics.SourceGenerators/<GeneratorName>/. Generated files are committed so that the project compiles without first running the generator.

dimensions.json schema

Semantics.SourceGenerators/Metadata/dimensions.json is a single object with one key, physicalDimensions, whose value is a list of dimension entries. Each entry looks like:

{
  "name": "Length",
  "symbol": "L",
  "dimensionalFormula": { "length": 1 },
  "availableUnits": [ "Meter", "Kilometer", "Foot", "Inch", "Mile",  ],
  "quantities": {
    "vector0": {
      "base": "Length",
      "overloads": [
        { "name": "Width",    "description": "Horizontal extent." },
        { "name": "Diameter", "description": "Distance across a circle.",
          "relationships": {
            "toRadius":   "Value / T.CreateChecked(2)",
            "fromRadius": "Value * T.CreateChecked(2)"
          }
        }
      ]
    },
    "vector1": { "base": "Displacement1D", "overloads": [ { "name": "Offset",  } ] },
    "vector2": { "base": "Displacement2D" },
    "vector3": { "base": "Displacement3D", "overloads": [ { "name": "Position3D",  } ] },
    "vector4": { "base": "Displacement4D" }
  },
  "integrals":     [ { "other": "Length", "result": "Area" } ],
  "derivatives":   [ ],
  "dotProducts":   [ ],
  "crossProducts": [ ]
}

Field reference

Field Required Meaning
name yes Stable dimension identifier; used for diagnostics and cross-references.
symbol yes Physics symbol (L, M, T, I, Θ, N, J, …).
dimensionalFormula yes Map of base dimension → exponent ({"length":1, "time":-2}). Used for dimensional-analysis equality.
availableUnits yes Names of units defined in the units metadata. The first entry is the SI base unit. The generator emits From{Unit} factories for each entry.
quantities.vectorN.base yes per declared form Base type emitted for that form (e.g. Length, Displacement1D, Force3D).
quantities.vectorN.overloads[] optional Semantic overloads of the base. Each gets its own type with implicit-widen / explicit-narrow conversions and a From(base) factory.
relationships (on an overload) optional C# expressions emitted as To{Other}() / From{Other}() instance methods. Reference Value and T.CreateChecked for type-correct constants.
integrals / derivatives optional Cross-dimensional * and / operator pairs. { "other": X, "result": Y } produces Self * X => Y and the inverse Y / X => Self.
dotProducts / crossProducts optional Vector-form-aware operators between this dimension's V≥1 forms and another dimension's V≥1 forms.
physicalConstraints optional (Planned) per-dimension floors/ceilings such as { "minValue": "0", "minValueUnit": "Kelvin" } — emitted as ArgumentException-throwing guards inside Create/From*. Tracked in issue #51.

Form-specific notes

  • Vector0: enforces non-negativity. V0 - V0 returns the same V0 of T.Abs(a - b) (decision locked). Generator emits Magnitude() only on V≥1 forms.
  • Vector1: signed scalar. Magnitude extraction returns the V0 base (Velocity1D.Magnitude() => Speed).
  • Vector2/3/4: per-component-signed. Magnitude() returns the V0 base via Euclidean norm.
  • Dot products: emitted on the higher-dimensional form, returning the V0 base of the result dimension. Example: Force3D.Dot(Displacement3D) => Energy.
  • Cross products: emitted on V3 only (V2 cross is intentionally unsupported). Result is the V3 of the result dimension.

Semantic overloads

An overload (e.g. Width, Height, Depth on Length) emits:

  • A record extending the base type but with its own identity: record Width<T> : Length<T>.
  • Implicit operator widening to the base.
  • Explicit operator narrowing from the base.
  • A From(base) factory.
  • Per-relationship To{Other}() / From{Other}() methods if relationships is set.

Operator preservation (e.g. Width + Width => Width vs. Width + Length => Length) is generated, and follows the rule "narrowest-shared overload wins; otherwise widen to base".

End-to-end: adding a dimension

  1. Edit Semantics.SourceGenerators/Metadata/dimensions.json. Add the dimension entry. Make sure availableUnits references units defined in the units metadata; if you need a new unit, add it there first.
  2. If the dimension introduces operators with existing dimensions, declare them under integrals / derivatives / dotProducts / crossProducts on one side of each pair. The generator emits forward and inverse forms automatically.
  3. Add per-dimension constraints (physicalConstraints) if there's a floor such as absolute zero.
  4. dotnet build. The generators run as analyzers; new files appear under Semantics.Quantities/Generated/Semantics.SourceGenerators/<GeneratorName>/.
  5. Diff the generated files. Verify the expected From* factories, operators, and Magnitude/Dot/Cross methods are present.
  6. Add tests under Semantics.Test/Quantities/ that exercise the new types and any new operator paths.
  7. Commit the metadata change and the regenerated *.g.cs files together.

End-to-end: adding a semantic overload

  1. Locate the dimension in dimensions.json.
  2. Append to its quantities.vectorN.overloads[]:
    { "name": "Heading", "description": "Direction of motion in 2D.",
      "relationships": { } }
  3. Build, diff the new Heading.g.cs, add tests, commit.

End-to-end: adding a physics relationship

For example, declaring Force × Distance = Work:

  1. Pick one side as the owner — usually the lower-rank operand. Add to Force's integrals:
    { "other": "Length", "result": "Energy" }
  2. The generator emits both Force * Length => Energy and Energy / Length => Force. No second declaration needed.
  3. For vector forms, declare on dotProducts or crossProducts — these are emitted on the matching vector forms only.

End-to-end: adding a logarithmic scale

Logarithmic quantities (decibel levels, pitch intervals, pH) don't obey linear arithmetic, so they are not dimensions. They live in Semantics.SourceGenerators/Metadata/logarithmic.json and are emitted by LogarithmicScalesGenerator as standalone readonly partial record structs around scale = multiplier · log_base(linear / reference):

{
  "name": "SoundPressureLevel",
  "description": "Represents a sound pressure level (SPL) in decibels…",
  "displayFormat": "{0} dB SPL",        // ToString template
  "scalarFactory": "FromDecibels",       // raw-value factory name
  "arithmetic": true,                    // emit log-space + and -
  "conversions": [
    {
      "linear": "SoundPressure",         // generated linear counterpart
      "multiplier": "20",                // 20 = field, 10 = power, 1200 = cents…
      "logBase": "10",                   // optional, defaults to 10
      "reference": { "constant": "ReferenceSoundPressure" }, // or { "value": "1000" }
      "fromName": "FromSoundPressure",   // optional, defaults to From{Linear}
      "toName": "ToSoundPressure"        // optional, defaults to To{Linear}
    }
  ]
}

Each conversion generates From{Linear}({Linear}<T>) and To{Linear}() methods; the core always gets the scalar factory, CompareTo, comparison operators, and ToString. Bespoke members that don't fit the schema — named constants (PH.Neutral, Semitones.Octave), cross-scale conversions (CentsSemitones), raw-T conveniences (Decibels.FromAmplitude(T)) — go in a hand-written partial next to the generated core (see Semantics.Quantities/AudioEngineering/ and Acoustics/).

SEM005 flags missing or duplicate scale names and conversions with no linear type.

Validation, diagnostics, and gotchas

  • Unknown dimension references in integrals / derivatives / dotProducts / crossProducts are currently dropped silently; this is tracked as a generator diagnostic improvement (issue #56). Until it lands, diff the output when editing metadata to catch typos.
  • availableUnits order matters: the first entry is treated as the SI base unit by UnitsGenerator.
  • relationships expressions are emitted verbatim into method bodies. Use Value for the current quantity and T.CreateChecked(...) (not literal numerics) for constants so all storage types stay correct.
  • Generator output is committed. CI must catch metadata/code drift; git status should be clean after a build.

Where to look in code

Concern File
Quantity emission, operators, overload preservation Semantics.SourceGenerators/Generators/QuantitiesGenerator.cs
Metadata model Semantics.SourceGenerators/Models/DimensionsMetadata.cs
Templates (records, classes, methods, properties) Semantics.SourceGenerators/Templates/
Runtime base + interfaces Semantics.Quantities/PhysicalQuantity.cs, IVector0.cs..IVector4.cs
Generated output Semantics.Quantities/Generated/Semantics.SourceGenerators/