diff --git a/.claude/skills/add-physics-quantity/SKILL.md b/.claude/skills/add-physics-quantity/SKILL.md new file mode 100644 index 0000000..53ea128 --- /dev/null +++ b/.claude/skills/add-physics-quantity/SKILL.md @@ -0,0 +1,93 @@ +--- +name: add-physics-quantity +description: Add a new physics quantity type with units, dimensions, operators, and tests to the source-generated system +disable-model-invocation: true +--- + +# Add Physics Quantity + +Add a new physics quantity to the source-generated physics system. This is a fully metadata-driven process - no C# code needs to be written manually. + +## Required Information + +Before starting, gather from the user: +1. **Quantity name** (e.g., "Viscosity", "MagneticFlux") +2. **Physical dimension symbol** (e.g., "M L⁻¹ T⁻¹") +3. **Dimensional formula** (exponents for mass, length, time, temperature, current, amount, luminosity) +4. **SI unit name and symbol** (e.g., "Pascal" / "Pa") +5. **Which vector forms** are needed (magnitude only, or 1D/2D/3D/4D) +6. **Physics relationships** - what other quantities multiply/divide to produce this one +7. **Any semantic overloads** (named aliases like Weight for ForceMagnitude) + +## Steps + +### Step 1: Add Dimension to dimensions.json + +**File**: `Semantics.SourceGenerators/Metadata/dimensions.json` + +Add a new entry to the `physicalDimensions` array following the existing pattern. Each dimension needs: +- `name`: PascalCase dimension name +- `symbol`: Unicode dimension symbol +- `dimensionalFormula`: object with exponents for base dimensions +- `availableUnits`: array of unit names (must match units.json entries) +- `quantities`: object defining vector forms (vector0 = magnitude, vector1-4 = directional) +- `integrals`: array of `{other, result}` pairs where `Self * Other = Result` +- `derivatives`: array of `{other, result}` pairs where `Self / Other = Result` +- `dotProducts`: array for dot product relationships (vector forms only) +- `crossProducts`: array for cross product relationships (vector3 forms only) + +**Important**: The generator automatically creates inverse operators. If you define `Force * Length = Energy` on Force, the generator also creates `Energy / Length = Force` and `Energy / Force = Length` on Energy. + +### Step 2: Add Units to units.json (if new units needed) + +**File**: `Semantics.SourceGenerators/Metadata/units.json` + +Add unit entries to the appropriate `unitCategories` entry, or create a new category. Each unit needs: +- `name`: PascalCase unit name +- `symbol`: unit symbol string +- `description`: brief description +- `system`: one of "SIBase", "SIDerived", "Imperial", "USCustomary", "CGS", "Other" + +### Step 3: Add Conversions to conversions.json (if non-SI units exist) + +**File**: `Semantics.SourceGenerators/Metadata/conversions.json` + +Add conversion factor entries for converting between unit systems. Each factor needs: +- `name`: descriptive PascalCase name (e.g., "CalorieToJoules") +- `description`: includes the exact numeric value +- `value`: string representation of the conversion factor + +### Step 4: Add Physical Constants to domains.json (if applicable) + +**File**: `Semantics.SourceGenerators/Metadata/domains.json` + +If the quantity involves physical constants, add them to the relevant domain entry. + +### Step 5: Build and Verify + +```bash +cd Semantics.Quantities && dotnet build +``` + +The source generator will create: +- Quantity classes for each vector form in `Generated/` +- Updated `PhysicalDimensions.g.cs` with dimension metadata +- Updated `Units.g.cs` with unit definitions +- Operator overloads for all defined physics relationships + +### Step 6: Run Tests + +```bash +dotnet test +``` + +Verify all existing tests still pass and the new quantity types are correctly generated. + +## Validation Checklist + +- [ ] Dimensional formula exponents sum correctly for the physical dimension +- [ ] Physics relationships are dimensionally consistent (check both sides of equations) +- [ ] Inverse operators are NOT manually added (generator handles them automatically) +- [ ] Unit symbols follow standard conventions +- [ ] `availableUnits` entries match `units.json` unit names exactly +- [ ] No circular dependencies in physics relationships diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index c719496..60407af 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -190,6 +190,7 @@ jobs: path: | ./coverage/* retention-days: 7 + if-no-files-found: ignore winget: name: Update Winget Manifests diff --git a/.github/workflows/verify-generated.yml b/.github/workflows/verify-generated.yml new file mode 100644 index 0000000..cbb411b --- /dev/null +++ b/.github/workflows/verify-generated.yml @@ -0,0 +1,55 @@ +name: Verify Generated Files + +# Two kinds of generated artifacts are committed to this repo: +# 1. Source-generator output under Semantics.Quantities/Generated/ (EmitCompilerGeneratedFiles=true). +# 2. The storage-type alias props under Semantics.Quantities./buildTransitive/, produced by +# scripts/Generate-AliasProps.ps1 from the quantity catalogue. +# This job rebuilds and regenerates both, then fails if anything drifts from its source — so a +# committed generated file or alias-props file can never go stale. +# +# NOTE: this is a standalone workflow on purpose — dotnet.yml is centrally synced from a +# shared template across ktsu repos, so a step added there would be overwritten. If this +# check belongs in the shared KtsuBuild pipeline long-term, move it there and delete this. + +on: + pull_request: + paths-ignore: ["**.md"] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: verify-generated-${{ github.ref }} + cancel-in-progress: true + +jobs: + verify-generated: + name: Generated files up to date + # windows-latest so git's autocrlf matches the committed CRLF line endings and the + # diff doesn't flag end-of-line differences. + runs-on: windows-latest + timeout-minutes: 15 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: "10.0.x" + + - name: Build (regenerates source-generator output into Generated/) + run: dotnet build Semantics.Quantities/Semantics.Quantities.csproj -c Release + + - name: Regenerate storage-type alias props + shell: pwsh + run: ./scripts/Generate-AliasProps.ps1 + + - name: Fail if generated files or alias props are stale + shell: bash + run: | + if ! git diff --exit-code; then + echo "::error::Committed generated files or alias props are out of date. Run 'dotnet build Semantics.Quantities/Semantics.Quantities.csproj -c Release' then 'pwsh scripts/Generate-AliasProps.ps1', and commit the changes." + exit 1 + fi diff --git a/.serena/.gitignore b/.serena/.gitignore new file mode 100644 index 0000000..14d86ad --- /dev/null +++ b/.serena/.gitignore @@ -0,0 +1 @@ +/cache diff --git a/.serena/project.yml b/.serena/project.yml new file mode 100644 index 0000000..242b3bf --- /dev/null +++ b/.serena/project.yml @@ -0,0 +1,113 @@ +# the name by which the project can be referenced within Serena +project_name: "Semantics" + + +# list of languages for which language servers are started; choose from: +# al bash clojure cpp csharp +# csharp_omnisharp dart elixir elm erlang +# fortran fsharp go groovy haskell +# java julia kotlin lua markdown +# matlab nix pascal perl php +# php_phpactor powershell python python_jedi r +# rego ruby ruby_solargraph rust scala +# swift terraform toml typescript typescript_vts +# vue yaml zig +# (This list may be outdated. For the current list, see values of Language enum here: +# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py +# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) +# Note: +# - For C, use cpp +# - For JavaScript, use typescript +# - For Free Pascal/Lazarus, use pascal +# Special requirements: +# Some languages require additional setup/installations. +# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers +# When using multiple languages, the first language server that supports a given file will be used for that file. +# The first language is the default language and the respective language server will be used as a fallback. +# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. +languages: +- csharp + +# the encoding used by text files in the project +# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings +encoding: "utf-8" + +# whether to use project's .gitignore files to ignore files +ignore_all_files_in_gitignore: true + +# list of additional paths to ignore in this project. +# Same syntax as gitignore, so you can use * and **. +# Note: global ignored_paths from serena_config.yml are also applied additively. +ignored_paths: [] + +# whether the project is in read-only mode +# If set to true, all editing tools will be disabled and attempts to use them will result in an error +# Added on 2025-04-18 +read_only: false + +# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details. +# Below is the complete list of tools for convenience. +# To make sure you have the latest list of tools, and to view their descriptions, +# execute `uv run scripts/print_tool_overview.py`. +# +# * `activate_project`: Activates a project by name. +# * `check_onboarding_performed`: Checks whether project onboarding was already performed. +# * `create_text_file`: Creates/overwrites a file in the project directory. +# * `delete_lines`: Deletes a range of lines within a file. +# * `delete_memory`: Deletes a memory from Serena's project-specific memory store. +# * `execute_shell_command`: Executes a shell command. +# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced. +# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type). +# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type). +# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes. +# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file. +# * `initial_instructions`: Gets the initial instructions for the current project. +# Should only be used in settings where the system prompt cannot be set, +# e.g. in clients you have no control over, like Claude Desktop. +# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol. +# * `insert_at_line`: Inserts content at a given line in a file. +# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol. +# * `list_dir`: Lists files and directories in the given directory (optionally with recursion). +# * `list_memories`: Lists memories in Serena's project-specific memory store. +# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building). +# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context). +# * `read_file`: Reads a file within the project directory. +# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store. +# * `remove_project`: Removes a project from the Serena configuration. +# * `replace_lines`: Replaces a range of lines within a file with new content. +# * `replace_symbol_body`: Replaces the full definition of a symbol. +# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen. +# * `search_for_pattern`: Performs a search for a pattern in the project. +# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase. +# * `switch_modes`: Activates modes by providing a list of their names +# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information. +# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task. +# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed. +# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store. +excluded_tools: [] + +# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default) +included_optional_tools: [] + +# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools. +# This cannot be combined with non-empty excluded_tools or included_optional_tools. +fixed_tools: [] + +# list of mode names to that are always to be included in the set of active modes +# The full set of modes to be activated is base_modes + default_modes. +# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this setting overrides the global configuration. +# Set this to [] to disable base modes for this project. +# Set this to a list of mode names to always include the respective modes for this project. +base_modes: + +# list of mode names that are to be activated by default. +# The full set of modes to be activated is base_modes + default_modes. +# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this overrides the setting from the global configuration (serena_config.yml). +# This setting can, in turn, be overridden by CLI parameters (--mode). +default_modes: + +# initial prompt for the project. It will always be given to the LLM upon activating the project +# (contrary to the memories, which are loaded on demand). +initial_prompt: "" diff --git a/CLAUDE.md b/CLAUDE.md index 0c22312..1b986a6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,195 +1,168 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +Guidance for Claude Code working in this repository. Read this together with `docs/strategy-unified-vector-quantities.md` (the architecture spec for the semantic-quantity system) and `docs/physics-generator.md` (the metadata workflow). -## Project Overview +## Build commands -ktsu.Semantics is a comprehensive .NET library for creating type-safe, validated types with semantic meaning. The library consists of three main packages: +This is a multi-target .NET library using ktsu MSBuild SDKs. Strings and Paths target `net8.0`–`net10.0` plus `netstandard2.0`/`netstandard2.1`; Quantities and the storage-type alias packages target `net8.0`–`net10.0` (they require `INumber`). -- **Semantics.Strings**: Core semantic string types with validation framework -- **Semantics.Paths**: Specialized path handling with polymorphic interfaces -- **Semantics.Quantities**: Complete physics quantities system with 80+ quantities across 8 scientific domains +- **Build**: `dotnet build` +- **Test**: `dotnet test` +- **Test (verbose)**: `dotnet test --logger "console;verbosity=detailed"` +- **Clean**: `dotnet clean` +- **Restore**: `dotnet restore` +- **Format**: `dotnet format` -## Build Commands +Tests use MSTest. Generator output is emitted to `Semantics.Quantities/Generated/` (committed) so the project can be inspected without first running the generator. -### Building the Solution -```powershell -dotnet build Semantics.sln -``` +## Project layout + +| Project | Responsibility | +|---|---| +| `Semantics.Strings` | Strongly-typed string wrappers (`SemanticString`) and validation attributes/strategies. | +| `Semantics.Paths` | Polymorphic file system path types (`IPath`, `IFilePath`, `IDirectoryPath`, …). | +| `Semantics.Quantities` | Hand-written runtime types (`PhysicalQuantity`, `IVector0`..`IVector4`, `UnitSystem`) plus generator output under `Generated/`. | +| `Semantics.SourceGenerators` | Roslyn incremental generators that emit quantity types, units, conversions, magnitudes, physical constants, and storage-type helpers from metadata. | +| `Semantics.Quantities.{Double,Float,Decimal}` | Props-only satellite packages. Each ships a `buildTransitive` props file (generated by `scripts/Generate-AliasProps.ps1`) that injects global-using aliases binding every quantity to one storage type, so consumers write `Mass` instead of `Mass`. | +| `Semantics.Test` | MSTest project covering all of the above. | + +## Semantic quantities architecture (the unified vector model) + +The quantity system is **metadata-driven**. The single source of truth is +`Semantics.SourceGenerators/Metadata/dimensions.json`, which lists every physical dimension and the vector forms it supports. + +Every quantity is a vector. Dimensionality of the *direction space* is part of the type: + +| Form | Meaning | Sign | Examples | +|---|---|---|---| +| `IVector0` | Magnitude only | Always `>= 0` | `Speed`, `Mass`, `Energy`, `Distance`, `Area` | +| `IVector1` | Signed 1D | Signed | `Velocity1D`, `Force1D`, `Temperature`, `ElectricCharge` | +| `IVector2` | 2D directional | Per-component | `Velocity2D`, `Force2D`, `Acceleration2D` | +| `IVector3` | 3D directional | Per-component | `Velocity3D`, `Force3D`, `Position3D` | +| `IVector4` | 4D directional | Per-component | (reserved for relativistic / spacetime) | + +`IVectorN.Magnitude()` (for N >= 1) returns the corresponding `IVector0`. -### Running Tests -```powershell -# Run all tests -dotnet test Semantics.Test/Semantics.Test.csproj +All generated types are generic over a numeric storage type: `where T : struct, INumber`. -# Run specific test with filter -dotnet test --filter "FullyQualifiedName~SemanticStringTests" +### Resolved design decisions + +These are now baked into the generator and enforced by tests. **Do not reopen without an architecture discussion.** + +1. **`V0 - V0` returns the same `V0` of `T.Abs(a - b)`.** Magnitude subtraction stays non-negative; signed subtraction must use the V1 form explicitly. +2. **Dimensionless and angular quantities have both `Ratio` (V0) and `SignedRatio` (V1) bases.** Ratios that semantically must be non-negative (e.g. `RefractiveIndex`, `MachNumber`, `SpecificGravity`) are V0 overloads of `Ratio`. +3. **Semantic overloads widen implicitly to their base, narrow explicitly from it.** A `Weight` is implicitly a `ForceMagnitude`; the reverse requires `Weight.From(forceMagnitude)` or an explicit cast. +4. **Physical constraints are enforced structurally via the V0 (magnitude) form.** `Vector0` factories run `Vector0Guards.EnsureNonNegative` and throw `ArgumentException` on a negative value. That covers absolute zero (Temperature is V0, so Kelvin must be ≥ 0), non-negative frequency, non-negative absolute pressure, etc. A V0 *overload* can opt into a stricter rule by declaring `physicalConstraints: { "minExclusive": "0" }` in `dimensions.json` (#51); the generator then emits `Vector0Guards.EnsurePositive` and rejects zero too. Used today for `Wavelength`, `Period`, and `HalfLife` — quantities for which zero is unphysical. +5. **Logarithmic-scale quantities are generated from `logarithmic.json`, not declared as dimensions.** Decibel scales (`Decibels`, `SoundPressureLevel`, `SoundIntensityLevel`, `SoundPowerLevel`, `DirectionalityIndex`), pitch intervals (`Cents`, `Semitones`), and `PH` don't obey linear arithmetic, so they are emitted by `LogarithmicScalesGenerator` as standalone `readonly partial record struct`s built around `scale = multiplier · log_base(linear / reference)`, converting to and from their linear generated counterparts (`Gain`, `Ratio`, `SoundPressure`, `SoundIntensity`, `SoundPower`, `Concentration`). Bespoke members (named constants like `PH.Neutral`, cross-scale conversions like `Cents`↔`Semitones`) live in hand-written partials next to the metadata-generated core. Adding a new log-scale quantity means adding a `logarithmic.json` entry, plus a partial only if it needs bespoke members. + +### Physical constants + +`PhysicalConstants` is **generated** from `domains.json`. Public surface: + +```csharp +// Domain-grouped PreciseNumber values: +PhysicalConstants.Fundamental.SpeedOfLight +PhysicalConstants.Fundamental.PlanckConstant +PhysicalConstants.AngularMechanics.DegreesPerRadian + +// Generic accessors that materialise into any T : INumber: +PhysicalConstants.Generic.SpeedOfLight() +PhysicalConstants.Generic.PlanckConstant() +PhysicalConstants.Generic.DegreesPerRadian() ``` -### Restore Packages -```powershell -dotnet restore +Backing values are stored as `PreciseNumber` and converted with `T.CreateChecked` per call. + +### Operators and physics relationships + +Cross-dimensional relationships are also declared in `dimensions.json` (`integrals`, `derivatives`, `dotProducts`, `crossProducts`). The generator emits operators like: + +```csharp +public static Energy operator *(Force1D f, Length d) => + Energy.Create(f.Value * d.Value); ``` -### Clean Build Artifacts -```powershell -dotnet clean +All values are stored in SI base units, so operators read `.Value` directly. Suppress `CA2225` on physics operators because "named alternates" (`Add`, `Multiply`) don't carry the dimensional meaning: + +```csharp +[System.Diagnostics.CodeAnalysis.SuppressMessage( + "Usage", "CA2225:Operator overloads have named alternates", + Justification = "Physics relationship operators represent fundamental equations.")] ``` -## Architecture - -### Core Design Patterns - -1. **CRTP (Curiously Recurring Template Pattern)**: All semantic types use CRTP for type safety: - ```csharp - public abstract record SemanticString : ISemanticString - where TDerived : SemanticString - ``` - -2. **Bootstrap Architecture**: The physics system uses a sophisticated bootstrap pattern to resolve circular dependencies: - - `BootstrapUnits` provides initial unit definitions during system initialization - - `PhysicalDimensions` uses bootstrap units to define dimensions without circular dependencies - - `Units` class replaces bootstrap units with full unit definitions after initialization - -3. **Factory Pattern**: All semantic types support factory-based creation for dependency injection: - - `ISemanticStringFactory` interface - - `SemanticStringFactory` implementation - -### Project Structure - -#### Semantics.Strings -- Core base class: `SemanticString` -- Factory infrastructure: `ISemanticStringFactory`, `SemanticStringFactory` -- Validation framework in `Validation/`: - - `Attributes/`: 50+ built-in validation attributes - - `Rules/`: Validation rule implementations - - `Strategies/`: `ValidateAllStrategy`, `ValidateAnyStrategy` - -#### Semantics.Paths -- Base class: `SemanticPath` (extends `SemanticString`) -- `Implementations/`: Concrete path types (AbsoluteFilePath, RelativeDirectoryPath, etc.) -- `Interfaces/`: Polymorphic path interfaces (IPath, IFilePath, IAbsolutePath, etc.) -- `Primitives/`: Core path primitives -- Path canonicalization normalizes directory separators and removes trailing separators - -#### Semantics.Quantities -- Core base classes: - - `SemanticQuantity`: Base quantity with storage type - - `PhysicalQuantity`: Physics quantities with dimensional analysis -- `Core/`: Physical dimension system, units, and constants - - `PhysicalDimension`: Dimensional analysis with SI base quantities - - `PhysicalConstants`: Centralized physical constants with type-safe generic access - - `BootstrapUnits` and `Units`: Bootstrap architecture for circular dependency resolution -- Domain-specific quantities in subdirectories: - - `Mechanics/`: Length, Mass, Force, Energy, Velocity, etc. (15 quantities) - - `Electrical/`: Voltage, Current, Resistance, Capacitance, etc. (11 quantities) - - `Thermal/`: Temperature, Heat, Entropy, Thermal Conductivity, etc. (10 quantities) - - `Chemical/`: Concentration, pH, Molar Mass, Reaction Rate, etc. (10 quantities) - - `Acoustic/`: Frequency, Sound Intensity, Wavelength, etc. (20 quantities) - - `Nuclear/`: Radioactive Activity, Absorbed Dose, etc. (5 quantities) - - `Optical/`: Luminous Flux, Illuminance, Refractive Index, etc. (6 quantities) - - `FluidDynamics/`: Viscosity, Flow Rate, Reynolds Number, etc. (5 quantities) - -### Key Architectural Concepts - -1. **Type Safety Through Generics**: The library extensively uses generic type parameters and constraints to enforce compile-time type safety. - -2. **Validation Architecture**: - - Attribute-based validation using reflection - - Strategy pattern for combining validation rules (`ValidateAll` vs `ValidateAny`) - - Validation occurs in `MakeCanonical()` before object construction - -3. **Canonicalization**: `MakeCanonical(string input)` method provides a hook for derived types to normalize input before validation (e.g., path separator normalization). - -4. **WeakString Property**: The underlying string value is exposed via `WeakString` property to enable interoperability with regular string APIs while maintaining type safety. - -5. **Physical Constants System**: All constants are centralized in `PhysicalConstants` with derived constants validated against fundamental relationships in tests. - -## Code Style Requirements - -### File Headers -All C# files must include this copyright header: +## Code standards + +### File headers + ```csharp // Copyright (c) ktsu.dev // All rights reserved. // Licensed under the MIT license. ``` -### Code Style -- **Indentation**: Use tabs (tab_width = 4) for C# files -- **Namespaces**: File-scoped namespaces (`csharp_style_namespace_declarations = file_scoped:error`) -- **Usings**: Inside namespace (`csharp_using_directive_placement = inside_namespace:error`) -- **Braces**: Always use braces (`csharp_prefer_braces = true:error`) -- **Expression bodies**: When on single line (`when_on_single_line:error`) -- **Primary constructors**: Preferred (`csharp_style_prefer_primary_constructors = true:error`) -- **No top-level statements**: (`csharp_style_prefer_top_level_statements = false:error`) - -### Warning Suppressions -- **Never add global suppressions** for warnings in project properties -- Use explicit `[SuppressMessage]` attributes with justifications when needed -- Fallback to `#pragma warning disable` preprocessor directives with comment justifications only if attributes are unavailable -- Make suppressions as targeted as possible (smallest scope) - -### Common Suppressions in Project -Some projects suppress specific warnings in `.csproj`: -- CA1866: Use 'string.Method(char)' instead of 'string.Method(string)' -- CA2249: Consider using String.Contains instead of String.IndexOf -- IDE0057: Substring can be simplified - -## Target Frameworks - -The library supports multiple target frameworks for maximum compatibility: -- .NET 9.0, 8.0, 7.0, 6.0, 5.0 -- .NET Standard 2.0, 2.1 - -Use conditional compilation for framework-specific features: -```csharp -#if NET5_0_OR_GREATER - // .NET 5+ specific code -#else - // Fallback for older frameworks -#endif -``` +Generator-emitted files additionally carry `// `. -## Testing +### Validation and error handling -- Test project: `Semantics.Test/` -- Uses MSTest framework with MSTest.Sdk -- Target framework: net9.0 only -- Tests validate: - - All validation attributes and rules - - Physics quantity relationships and dimensional analysis - - Derived physical constants match calculated values - - Path operations and file system interactions - - Factory pattern and dependency injection - - Performance characteristics +- Throw `ArgumentException` for validation failures (not `FormatException`). +- Throw `DivideByZeroException` when dividing by zero in `DivideToStorage`. +- Use the most specific exception type available. -## SDK and Build System +### Testing -- Uses custom `ktsu.Sdk` MSBuild SDK (version 1.75.0) -- Central package management enabled via `Directory.Packages.props` -- CI/CD pipeline managed by PowerShell module `scripts/PSBuild.psm1` -- GitHub Actions workflow in `.github/workflows/dotnet.yml` handles: - - Building and testing - - SonarQube analysis - - NuGet package publishing - - Release creation with changelog management - - Winget manifest updates +- Use explicit types (no `var`) in test bodies. +- Pre-create fixtures outside measurement loops in performance tests. +- Mark OS-specific tests with `[TestCategory("OS-Specific")]`. +- Use 259-character path limit for cross-platform path tests. +- Force GC before memory measurements: `GC.Collect(); GC.WaitForPendingFinalizers();` -## Important Implementation Notes +### XML documentation -1. **Physics Relationships**: When implementing new physics quantities, ensure dimensional analysis is correct and operators return proper types based on dimensional multiplication/division. +- `/// Gets the physical dimension of [].` style for dimension properties. +- Include ``, ``, ``, and `` tags on public APIs. -2. **Unit Conversions**: Use `In(IUnit targetUnit)` method for unit conversions. Handle both linear units (most common) and offset units (like temperature conversions). +## Important implementation notes -3. **Path Canonicalization**: Paths automatically normalize directory separators to platform-specific separators and remove trailing separators (except for root paths). +### Semantic string creation -4. **Factory Methods**: Semantic types support multiple creation patterns: - - Direct: `EmailAddress.Create("user@example.com")` - - Factory: `factory.Create("user@example.com")` - - Casting: `(EmailAddress)"user@example.com"` - - Safe: `EmailAddress.TryCreate("maybe@invalid", out var result)` +```csharp +var email = EmailAddress.Create("user@example.com"); +var userId = UserId.Create("USER_123"); + +// Extension method conversion +var email2 = "user@example.com".As(); -5. **Validation Strategies**: Use `[ValidateAll]` (default) for AND logic or `[ValidateAny]` for OR logic when combining multiple validation attributes. +// Cross-type conversion +var converted = sourceString.As(); +``` -6. **Physical Constants**: Access constants via `PhysicalConstants.Generic` with type parameter for storage type, e.g., `PhysicalConstants.Generic.SpeedOfLight()`. +### Path conversion + +- `AsAbsolute()` — convert to absolute using current working directory. +- `AsAbsolute(baseDirectory)` — convert to absolute using a specific base. +- `AsRelative(baseDirectory)` — convert to relative against a specific base. + +### Working with the source generator + +- Edit `Semantics.SourceGenerators/Metadata/dimensions.json` to add a dimension, vector form, semantic overload, or relationship. +- Rebuild `Semantics.SourceGenerators` and the consuming `Semantics.Quantities` project; emitted files appear in `Semantics.Quantities/Generated/Semantics.SourceGenerators//`. +- Treat generator output as committed source. Diff it before commit so accidental regressions are visible. +- After adding or renaming a quantity, regenerate the storage-type alias props with `pwsh scripts/Generate-AliasProps.ps1` (it reads the generated catalogue and rewrites `Semantics.Quantities.{Double,Float,Decimal}/buildTransitive/*.props`) and commit them. The `verify-generated` workflow rebuilds, regenerates, and fails the PR if either the generated sources or the alias props drift. +- Factory names are the **singular lemma** (#49). The generator emits `From{name}` using each unit's `name` from `units.json` verbatim (e.g. `Length.FromMeter`, `Mass.FromKilogram`, `Speed.FromMeterPerSecond`, `Length.FromFoot`, `Frequency.FromHertz`). The rule is purely mechanical, so `name` must itself be the singular lemma — including compounds, whose leading noun is singular too (`MeterPerSecond`, `RevolutionPerMinute`, `PartPerMillion`, not `MeterPerSecond`/`RevolutionPerMinute`/`PartPerMillion`). There is no `factoryName` field and no pluralisation step; the generator never has to know English pluralisation. +- Generator diagnostics: + - **SEM001** — a relationship in `dimensions.json` references a dimension that does not exist (typo or rename). The operator is silently dropped. + - **SEM002** — schema-level validation issue (missing `name`/`symbol`, empty `availableUnits`, duplicate type names, no vector forms declared). + - **SEM003** — a relationship's explicit `forms` list references a vector form not declared on a participating dimension. Use `forms` to constrain a relationship to specific vector forms (e.g. `crossProducts: [{ "other": "Length", "result": "Torque", "forms": [3] }]`); when omitted, the legacy "emit at every common form" behaviour is preserved. + - **SEM004** — a dimension's `availableUnits` array references a unit name that isn't declared anywhere in `units.json`. Without the diagnostic the generator silently emits an identity-conversion `From{Unit}` factory, which is wrong for any non-base unit; SEM004 catches the typo at build time. + - **SEM005** — schema-level validation issue in `logarithmic.json` (missing or duplicate scale names, a conversion with no linear type). +- See `docs/physics-generator.md` for the full schema and an end-to-end "add a dimension" walk-through. + +This file is the entry point. For deeper material: + +- `docs/strategy-unified-vector-quantities.md` — architecture spec for the unified vector model. +- `docs/physics-generator.md` — generator + `dimensions.json` schema. +- `docs/architecture.md` — semantic strings/paths/validation architecture (SOLID, design patterns). +- `docs/complete-library-guide.md` — user-facing guide to all components. +- `docs/validation-reference.md` — list of validation attributes. +- `docs/advanced-usage.md` — advanced patterns for strings/paths. +- `docs/migration-guide-2.0.md` — 1.x → 2.0 upgrade guide (renames, namespace moves, behavioral changes). diff --git a/DESCRIPTION.md b/DESCRIPTION.md index 3c4f956..ef2133f 100644 --- a/DESCRIPTION.md +++ b/DESCRIPTION.md @@ -1 +1 @@ -A comprehensive .NET library for creating type-safe, validated string and physics quantity types using semantic meaning. Transform primitive string and numeric obsession into strongly-typed, self-validating domain models with 50+ validation attributes, polymorphic path handling, complete physics system covering 80+ quantities across 8 scientific domains, centralized physical constants with dimensional analysis, and performance-optimized utilities. Features include bootstrap architecture for circular dependency resolution, factory pattern support, dependency injection integration, and enterprise-ready capabilities for building robust, maintainable scientific and domain-specific applications. +A comprehensive .NET library for replacing primitive obsession with strongly-typed, self-validating domain models across three pillars: semantic strings with 50+ validation attributes, polymorphic path handling, and metadata-generated semantic quantities. The quantity system covers 60+ physical dimensions and 200+ generated types under a unified vector model, with compile-time dimensional safety, generated unit conversions and physics relationships, centralized physical constants, and optional per-storage-type alias packages. Features factory-pattern and dependency-injection support for building robust, maintainable scientific and domain-specific applications. diff --git a/Directory.Packages.props b/Directory.Packages.props index 8e3bd04..378664d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,13 +3,19 @@ true - - - + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 9f2d114..0cd2db0 100644 --- a/README.md +++ b/README.md @@ -1,408 +1,229 @@ # ktsu.Semantics -[![NuGet Version](https://img.shields.io/nuget/v/ktsu.Semantics.svg)](https://www.nuget.org/packages/ktsu.Semantics/) -[![NuGet Downloads](https://img.shields.io/nuget/dt/ktsu.Semantics.svg)](https://www.nuget.org/packages/ktsu.Semantics/) -[![Build Status](https://github.com/ktsu-dev/Semantics/workflows/CI/badge.svg)](https://github.com/ktsu-dev/Semantics/actions) +[![License](https://img.shields.io/github/license/ktsu-dev/Semantics.svg?label=License&logo=nuget)](LICENSE.md) +[![NuGet Version](https://img.shields.io/nuget/v/ktsu.Semantics?label=Stable&logo=nuget)](https://nuget.org/packages/ktsu.Semantics) +[![NuGet Version](https://img.shields.io/nuget/vpre/ktsu.Semantics?label=Latest&logo=nuget)](https://nuget.org/packages/ktsu.Semantics) +[![NuGet Downloads](https://img.shields.io/nuget/dt/ktsu.Semantics?label=Downloads&logo=nuget)](https://nuget.org/packages/ktsu.Semantics) +[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/ktsu-dev/Semantics?label=Commits&logo=github)](https://github.com/ktsu-dev/Semantics/commits/main) +[![GitHub contributors](https://img.shields.io/github/contributors/ktsu-dev/Semantics?label=Contributors&logo=github)](https://github.com/ktsu-dev/Semantics/graphs/contributors) +[![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/ktsu-dev/Semantics/dotnet.yml?label=Build&logo=github)](https://github.com/ktsu-dev/Semantics/actions) -A comprehensive .NET library for creating type-safe, validated types with semantic meaning. Transform primitive string and numeric obsession into strongly-typed, self-validating domain models with comprehensive validation, specialized path handling, and a complete physics quantities system covering 80+ quantities across 8 major scientific domains with dimensional analysis and centralized physical constants. +A .NET library for replacing primitive obsession with strongly-typed, self-validating domain models. Three pillars: -## Overview +- **Semantic Strings** — type-safe wrappers like `EmailAddress`, `UserId`, `BlogSlug` with attribute-driven validation. +- **Semantic Paths** — polymorphic `IPath` hierarchy for files, directories, absolute, relative, and combinations. +- **Semantic Quantities** — a metadata-generated, type-safe quantity system with a unified `IVector0..IVector4` model covering 60+ physical dimensions and 200+ generated types. Optional per-storage-type alias packages let you write `Mass` instead of `Mass`. -The Semantics library enables you to create strongly-typed wrappers that carry semantic meaning and built-in validation. Instead of passing raw primitives around your application, you can create specific types like `EmailAddress`, `FilePath`, `Temperature`, or `UserId` that are impossible to misuse and automatically validate their content. +Targets `net8.0`–`net10.0`. Semantic Strings and Paths additionally target `netstandard2.0`/`netstandard2.1`; Semantic Quantities is `net8.0`+ (it requires `INumber`). -## 🌟 Key Features - -- **Type Safety**: Eliminate primitive obsession with strongly-typed wrappers -- **Comprehensive Validation**: 50+ built-in validation attributes for all common scenarios -- **Path Handling**: Specialized path types with polymorphic interfaces and file system operations -- **Complete Physics System**: 80+ physics quantities across 8 scientific domains with dimensional analysis -- **Physical Constants**: Centralized, type-safe access to fundamental and derived constants with validation -- **Bootstrap Architecture**: Clean circular dependency resolution for complex type systems -- **Unit Conversions**: Automatic unit handling with compile-time dimensional safety -- **Factory Pattern**: Clean object creation with dependency injection support -- **Performance Optimized**: Span-based operations, pooled builders, and minimal allocations -- **Enterprise Ready**: Full .NET ecosystem integration (ASP.NET Core, Entity Framework, etc.) -- **Comprehensive Testing**: Derived constants validation and physics relationship verification - -## 🚀 Quick Start - -### Installation +## Install ```bash dotnet add package ktsu.Semantics ``` -### Basic Usage +## Semantic strings ```csharp -using ktsu.Semantics; +using ktsu.Semantics.Strings; -// Define strongly-typed domain models -[IsEmail] +[IsEmailAddress] public sealed record EmailAddress : SemanticString { } -[HasLength(8, 50), IsNotEmpty] +[StartsWith("USER_"), HasNonWhitespaceContent] public sealed record UserId : SemanticString { } -// Simple direct usage - Clean API with type inference: - -// 1. Create methods (recommended) - no generic parameters needed! -var email1 = EmailAddress.Create("user@example.com"); -var userId1 = UserId.Create("USER_12345"); - -// 2. From character arrays -char[] emailChars = ['u', 's', 'e', 'r', '@', 'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm']; -var email2 = EmailAddress.Create(emailChars); - -// 3. From ReadOnlySpan (performance optimized) -var userId2 = UserId.Create("USER_12345".AsSpan()); - -// 4. Explicit string casting -var email3 = (EmailAddress)"user@example.com"; -var userId3 = (UserId)"USER_12345"; +// Direct construction — no generic params needed +var email = EmailAddress.Create("user@example.com"); +var userId = UserId.Create("USER_12345"); -// 5. Safe creation with TryCreate (no exceptions) -if (EmailAddress.TryCreate("maybe@invalid", out EmailAddress? safeEmail)) -{ - // Use safeEmail - validation succeeded -} +// Span-based and char[] overloads exist too +var email2 = EmailAddress.Create("user@example.com".AsSpan()); -// Compile-time safety prevents mistakes -public void SendWelcomeEmail(EmailAddress to, UserId userId) { /* ... */ } +// Safe creation +if (EmailAddress.TryCreate("maybe@invalid", out EmailAddress? safe)) { /* … */ } -// This won't compile - type safety in action! -// SendWelcomeEmail(userId, email); // ❌ Compiler error! +// Compile-time safety +public void SendWelcomeEmail(EmailAddress to, UserId userId) { /* … */ } +// SendWelcomeEmail(userId, email); // ❌ compiler error ``` -### Factory Pattern Usage +### Combining attributes ```csharp -// Use factory pattern (recommended for dependency injection) -var emailFactory = new SemanticStringFactory(); -var userFactory = new SemanticStringFactory(); - -// Clean overloaded API - Create methods -var email = emailFactory.Create("user@example.com"); -var userId = userFactory.Create("USER_12345"); - -// All input types supported via overloading -var email2 = emailFactory.Create(['u', 's', 'e', 'r', '@', 'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm']); -var userId2 = userFactory.Create("USER_12345".AsSpan()); - -// Safe creation with TryCreate -if (emailFactory.TryCreate("maybe@invalid", out EmailAddress? safeEmail)) -{ - // Success! -} +// All must pass (default) +[IsEmailAddress, EndsWith(".com")] +public sealed record DotComEmail : SemanticString { } -// Legacy FromString methods still available -var email3 = emailFactory.FromString("user@example.com"); +// Any can pass +[ValidateAny] +[IsEmailAddress, IsUri] +public sealed record ContactMethod : SemanticString { } ``` -### Physics Quantities System +The full attribute catalogue (text, format, casing, first-class .NET types, paths) lives in [`docs/validation-reference.md`](docs/validation-reference.md). -```csharp -// Complete physics system with 80+ quantities across 8 domains -public sealed record Temperature : PhysicalQuantity, T> where T : struct, INumber { } -public sealed record Force : PhysicalQuantity, T> where T : struct, INumber { } -public sealed record Energy : PhysicalQuantity, T> where T : struct, INumber { } - -// Create quantities with dimensional safety -var temp = Temperature.FromCelsius(25.0); // 298.15 K -var force = Force.FromNewtons(100.0); // 100 N -var distance = Length.FromMeters(5.0); // 5 m - -// Physics relationships with compile-time safety -var work = force * distance; // Results in Energy -var power = work / Time.FromSeconds(10.0); // Results in Power - -// Type-safe unit conversions -Console.WriteLine(temp.ToFahrenheit()); // 77°F -Console.WriteLine(force.ToPounds()); // 22.48 lbf - -// Access physical constants with type safety -var gasConstant = PhysicalConstants.Generic.GasConstant(); // 8.314 J/(mol·K) -var speedOfLight = PhysicalConstants.Generic.SpeedOfLight(); // 299,792,458 m/s -var planckConstant = PhysicalConstants.Generic.PlanckConstant(); // Type-safe constant access - -// Dimensional analysis prevents errors -// var invalid = force + temp; // ❌ Compiler error! -``` - -### Path Handling - -```csharp -// Use specialized path types -var fileFactory = new SemanticStringFactory(); -var configFile = fileFactory.Create(@"C:\app\config.json"); - -// Rich path operations -Console.WriteLine(configFile.FileName); // config.json -Console.WriteLine(configFile.FileExtension); // .json -Console.WriteLine(configFile.DirectoryPath); // C:\app -Console.WriteLine(configFile.Exists); // True/False - -// Polymorphic path collections -List allPaths = [ - AbsoluteFilePath.FromString(@"C:\data.txt"), - RelativeDirectoryPath.FromString(@"logs\app"), - FilePath.FromString(@"document.pdf") -]; - -// Filter by interface type -var filePaths = allPaths.OfType().ToList(); -var absolutePaths = allPaths.OfType().ToList(); -``` - -### Complex Validation +## Semantic paths ```csharp -// Combine multiple validation rules -[IsNotEmpty, IsEmail, HasLength(5, 100)] -public sealed record BusinessEmail : SemanticString { } +using ktsu.Semantics.Paths; -// Use validation strategies for flexible requirements -[ValidateAny] // Either email OR phone is acceptable -[IsEmail, RegexMatch(@"^\+?\d{10,15}$")] -public sealed record ContactInfo : SemanticString { } +var configFile = AbsoluteFilePath.Create(@"C:\app\config.json"); -// First-class type validation -[IsDateTime] -public sealed record ScheduledDate : SemanticString { } +configFile.FileName; // config.json +configFile.FileExtension; // .json +configFile.DirectoryPath; // C:\app +configFile.Exists; // bool -[IsDecimal, IsPositive] -public sealed record Price : SemanticString { } +// Polymorphic collections +List all = [ + AbsoluteFilePath.Create(@"C:\data.txt"), + RelativeDirectoryPath.Create(@"logs\app"), + FilePath.Create(@"document.pdf"), +]; -[IsGuid] -public sealed record TransactionId : SemanticString { } +var files = all.OfType().ToList(); +var absolutes = all.OfType().ToList(); ``` -## 🔧 Common Use Cases - -### E-commerce Domain - -```csharp -[HasLength(3, 20), IsNotEmpty] -public sealed record ProductSku : SemanticString { } - -[IsPositive, IsDecimal] -public sealed record Price : SemanticString { } +Conversions: `AsAbsolute()`, `AsAbsolute(baseDirectory)`, `AsRelative(baseDirectory)`. -[IsEmail] -public sealed record CustomerEmail : SemanticString { } +## Semantic quantities -public class Order -{ - public CustomerEmail CustomerEmail { get; set; } - public ProductSku[] Items { get; set; } - public Price TotalAmount { get; set; } -} -``` +Every quantity is a vector. Direction-space dimensionality is part of the type: -### Configuration Management +| Form | Sign | Examples | +|---|---|---| +| `IVector0` (magnitude) | `>= 0` | `Speed`, `Mass`, `Energy`, `Distance`, `Area` | +| `IVector1` (signed 1D) | signed | `Velocity1D`, `Force1D`, `Temperature` | +| `IVector2` (2D) | per-component | `Velocity2D`, `Force2D`, `Acceleration2D` | +| `IVector3` (3D) | per-component | `Velocity3D`, `Force3D`, `Position3D` | +| `IVector4` (4D) | per-component | reserved (relativistic / spacetime) | ```csharp -[IsAbsolutePath, DoesExist] -public sealed record ConfigFilePath : SemanticString { } +using ktsu.Semantics.Quantities; -[IsIpAddress] -public sealed record ServerAddress : SemanticString { } +// V0 magnitudes — From{Unit} factories use the singular lemma (#49) +var speed = Speed.FromMeterPerSecond(15.0); +var mass = Mass.FromKilogram(10.0); +var distance = Distance.FromMeter(5.0); -[IsInRange(1, 65535)] -public sealed record Port : SemanticQuantity { } -``` +// V3 directional — object-initializer syntax (X/Y/Z components) +var force3d = new Force3D { X = 0.0, Y = 0.0, Z = -9.8 }; +var disp3d = new Displacement3D { X = 3.0, Y = 4.0, Z = 0.0 }; -### Physical Constants System - -All physical constants are centralized in `PhysicalConstants` with type-safe generic access: - -```csharp -// Fundamental constants (SI 2019 definitions) -var c = PhysicalConstants.Generic.SpeedOfLight(); // 299,792,458 m/s -var h = PhysicalConstants.Generic.PlanckConstant(); // 6.62607015×10⁻³⁴ J⋅s -var k = PhysicalConstants.Generic.BoltzmannConstant(); // 1.380649×10⁻²³ J/K -var NA = PhysicalConstants.Generic.AvogadroNumber(); // 6.02214076×10²³ /mol +// Operators flow from dimensions.json +var work = ForceMagnitude.FromNewton(10.0) * distance; // F·d = Energy +var power = work / Duration.FromSecond(2.0); // W/t = Power -// Temperature constants -var T0 = PhysicalConstants.Generic.StandardTemperature(); // 273.15 K -var P0 = PhysicalConstants.Generic.StandardAtmosphericPressure(); // 101,325 Pa +// Vector ops +var workDot = force3d.Dot(disp3d); // Energy +var torque = force3d.Cross(disp3d); // Torque3D +var mag = disp3d.Magnitude(); // Length (always >= 0) -// Conversion factors with derived validation -var ftToM = PhysicalConstants.Generic.FeetToMeters(); // 0.3048 m/ft -var sqFtToSqM = PhysicalConstants.Generic.SquareFeetToSquareMeters(); // Derived: ftToM² +// Construction-time invariants (#50, #51) +// Speed.FromMeterPerSecond(-1.0) // ArgumentException — V0 must be non-negative +// Wavelength.FromMeter(0.0) // ArgumentException — strict-positive overload -// All constants have comprehensive test coverage ensuring derived values match calculations +// Type safety +// var nope = force3d + speed; // ❌ compiler error ``` -### Complete Physics Domains +Semantic overloads (e.g. `Weight` over `ForceMagnitude`, `Diameter` ↔ `Radius`): -The library includes **80+ physics quantities** across **8 scientific domains**: - -#### 🔧 Mechanics (15 quantities) ```csharp -// Kinematics and dynamics -var velocity = Velocity.FromMetersPerSecond(15.0); -var acceleration = Acceleration.FromMetersPerSecondSquared(9.8); -var force = Mass.FromKilograms(10.0) * acceleration; // F = ma - -// Work and energy -var work = force * Length.FromMeters(5.0); // W = F⋅d -var power = work / Time.FromSeconds(2.0); // P = W/t -``` +var raw = ForceMagnitude.FromNewton(686.0); +var weight = Weight.From(raw); // explicit narrow +ForceMagnitude back = weight; // implicit widen -#### ⚡ Electrical (11 quantities) -```csharp -// Ohm's law relationships -var voltage = Voltage.FromVolts(12.0); -var current = Current.FromAmperes(2.0); -var resistance = voltage / current; // R = V/I -var power = voltage * current; // P = VI +var radius = Radius.FromMeter(2.0); +var diameter = radius.ToDiameter(); // 4 m via metadata-defined relationship ``` -#### 🌡️ Thermal (10 quantities) -```csharp -// Thermodynamics -var temp = Temperature.FromCelsius(25.0); -var heat = Heat.FromJoules(1000.0); -var capacity = HeatCapacity.FromJoulesPerKelvin(100.0); -var entropy = heat / temp; // S = Q/T -``` +Physical constants are exposed in two shapes: -#### 🧪 Chemical (10 quantities) ```csharp -// Chemical calculations -var moles = AmountOfSubstance.FromMoles(0.5); -var molarity = moles / Volume.FromLiters(2.0); // M = n/V -var rate = ReactionRate.FromMolarPerSecond(0.01); -``` +// Domain-grouped, exact PreciseNumber values: +PhysicalConstants.Fundamental.SpeedOfLight; // 299_792_458 m/s as PreciseNumber +PhysicalConstants.Chemistry.GasConstant; // 8.31446... J/(mol·K) -#### 🔊 Acoustic (20 quantities) -```csharp -// Sound and vibration -var frequency = Frequency.FromHertz(440.0); // A4 note -var wavelength = SoundSpeed.Default / frequency; // λ = v/f -var intensity = SoundIntensity.FromWattsPerSquareMeter(1e-6); +// Generic accessors — materialise into any T : INumber: +var c = PhysicalConstants.Generic.SpeedOfLight(); +var R = PhysicalConstants.Generic.GasConstant(); ``` -#### ☢️ Nuclear (5 quantities) -```csharp -// Nuclear physics -var activity = RadioactiveActivity.FromBecquerels(1000.0); -var dose = AbsorbedDose.FromGrays(0.001); -var exposure = Exposure.FromCoulombsPerKilogram(1e-6); -``` +### Storage-type alias packages -#### 💡 Optical (6 quantities) -```csharp -// Photometry and optics -var flux = LuminousFlux.FromLumens(800.0); -var illuminance = flux / Area.FromSquareMeters(4.0); // E = Φ/A -var luminance = Luminance.FromCandelasPerSquareMeter(100.0); -``` +Every quantity is generic over its storage type (`Mass`, `Speed`, …). If a project uses one storage type throughout, reference a satellite package and drop the generic argument entirely: -#### 🌊 Fluid Dynamics (5 quantities) -```csharp -// Fluid mechanics -var viscosity = DynamicViscosity.FromPascalSeconds(0.001); -var flowRate = VolumetricFlowRate.FromCubicMetersPerSecond(0.1); -var reynolds = ReynoldsNumber.Calculate(velocity, Length.FromMeters(0.1), viscosity); +```xml + ``` -## 🏛️ Architecture & Design - -### Bootstrap Architecture - -The library uses a sophisticated bootstrap architecture to resolve circular dependencies: - ```csharp -// BootstrapUnits class provides initial unit definitions during system initialization -// PhysicalDimensions uses BootstrapUnits to define dimensions without circular dependencies -// Units class replaces bootstrap units with full unit definitions after initialization +using ktsu.Semantics.Quantities; -// This clean separation enables complex type systems while maintaining performance +// No anywhere — the package injects global-using aliases for every quantity. +Mass mass = Mass.FromKilogram(10.0); +Speed speed = Speed.FromMeterPerSecond(15.0); +Mass total = mass + Mass.FromKilogram(2.0); // still a Mass, full identity ``` -### Derived Constants Validation +The aliases are real `Mass` (etc.), so they interoperate with the whole API with no conversion. Packages exist for `Double`, `Float`, and `Decimal` — reference exactly one per project to pick the storage type. The alias lists are generated from the quantity catalogue (`scripts/Generate-AliasProps.ps1`) and validated in CI. -All derived physical constants are validated against their fundamental relationships: +The unified vector model and its rationale: [`docs/strategy-unified-vector-quantities.md`](docs/strategy-unified-vector-quantities.md). +A per-domain tour: [`docs/physics-domains-guide.md`](docs/physics-domains-guide.md). +How the source generator turns `dimensions.json` into types: [`docs/physics-generator.md`](docs/physics-generator.md). -```csharp -// Example: Area conversions are validated to ensure SquareFeetToSquareMeters = FeetToMeters² -[TestMethod] -public void DerivedConstants_AreaConversions_MatchCalculatedValues() -{ - var feetToMeters = PhysicalConstants.Conversion.FeetToMeters; - var calculatedSquareFeet = feetToMeters * feetToMeters; - var storedSquareFeet = PhysicalConstants.Conversion.SquareFeetToSquareMeters; - - Assert.AreEqual(calculatedSquareFeet, storedSquareFeet, tolerance); -} -// Comprehensive test coverage ensures physical relationships are mathematically correct -``` - -## 🏗️ Dependency Injection +## Dependency injection ```csharp -// Register factories in your DI container services.AddTransient, SemanticStringFactory>(); -// Use in services -public class UserService +public class UserService(ISemanticStringFactory emails) { - private readonly ISemanticStringFactory _emailFactory; - - public UserService(ISemanticStringFactory emailFactory) - { - _emailFactory = emailFactory; - } - - public async Task CreateUserAsync(string email) - { - // Factory handles validation and throws meaningful exceptions - var validatedEmail = _emailFactory.Create(email); - return new User(validatedEmail); - } + public Task CreateUserAsync(string raw) => + emails.TryCreate(raw, out var email) + ? Task.FromResult(new User(email)) + : throw new ArgumentException("invalid email"); } ``` -## 📖 Documentation - -Comprehensive documentation is available in the [`docs/`](docs/) directory: +## Architecture -- **[Complete Library Guide](docs/complete-library-guide.md)** - 🌟 **START HERE** - Complete overview of all library features and components -- **[Architecture Guide](docs/architecture.md)** - SOLID principles, design patterns, and system architecture -- **[Advanced Usage Guide](docs/advanced-usage.md)** - Advanced features, custom validation, and best practices -- **[Validation Reference](docs/validation-reference.md)** - Complete reference of all validation attributes -- **[FluentValidation Integration](docs/fluent-validation-integration.md)** - Integration with FluentValidation library +The physics system is **metadata-driven**. The single source of truth is +`Semantics.SourceGenerators/Metadata/dimensions.json` (with `units.json`, `magnitudes.json`, `conversions.json`, and `domains.json` alongside it), and a Roslyn incremental generator emits: -## 💡 Examples +- One record per quantity (Vector0/1/2/3/4 base + semantic overloads). +- A `From{Unit}` factory per declared unit, with built-in unit conversion to the SI base unit and a `Vector0Guards` enforce-non-negative (or strict-positive) check on V0 types. +- Cross-dimensional `*`, `/`, `Dot`, `Cross` operators driven by `integrals` / `derivatives` / `dotProducts` / `crossProducts` declarations. +- `PhysicalConstants` with both domain-grouped `PreciseNumber` fields and generic `T.CreateChecked`-backed accessors. -Extensive examples are available in [`docs/examples/`](docs/examples/): +Generator diagnostics catch metadata problems at build time: -- **[Getting Started](docs/examples/getting-started.md)** - Basic usage patterns -- **[Physics Relationships](docs/examples/PhysicsRelationshipExamples.md)** - Physics calculations and relationships -- **[Validation Attributes](docs/examples/validation-attributes.md)** - Built-in and custom validation -- **[Path Handling](docs/examples/path-handling.md)** - File system operations -- **[Factory Pattern](docs/examples/factory-pattern.md)** - Object creation and DI -- **[String Operations](docs/examples/string-operations.md)** - String compatibility and LINQ -- **[Type Conversions](docs/examples/type-conversions.md)** - Cross-type conversions -- **[Real-World Scenarios](docs/examples/real-world-scenarios.md)** - Complete domain examples +- **SEM001** — relationship references an unknown dimension name. +- **SEM002** — schema-level metadata issue (missing fields, duplicate type names, etc). +- **SEM003** — relationship's `forms` list references a vector form not declared on a participating dimension. +- **SEM004** — `availableUnits` references a unit not declared in `units.json`. -## 🤝 Contributing +Generated output is committed to `Semantics.Quantities/Generated/` so the project compiles without first running the generator. -Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change. +The string and path systems use the same building blocks: an attribute → strategy → rule → factory pipeline. See [`docs/architecture.md`](docs/architecture.md). -## 📄 License +## Documentation -This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details. +- [Complete library guide](docs/complete-library-guide.md) — start here for a feature tour. +- [Architecture (strings/paths/validation)](docs/architecture.md) +- [Architecture (physics — unified vector model)](docs/strategy-unified-vector-quantities.md) +- [Source-generator workflow](docs/physics-generator.md) +- [Physics domains tour](docs/physics-domains-guide.md) +- [Validation attributes reference](docs/validation-reference.md) +- [Advanced usage patterns](docs/advanced-usage.md) -## 🆘 Support +## Contributing -- 📖 [Documentation](https://github.com/ktsu-dev/Semantics/wiki) -- 🐛 [Issues](https://github.com/ktsu-dev/Semantics/issues) -- 💬 [Discussions](https://github.com/ktsu-dev/Semantics/discussions) -- 📦 [NuGet Package](https://www.nuget.org/packages/ktsu.Semantics/) +Contributions are welcome — please open an issue first for major changes so we can discuss the direction. The branch's open work items are tracked as GitHub issues. ---- +## License -*Transform your primitive-obsessed code into a strongly-typed, self-validating domain model with ktsu.Semantics.* +MIT — see [`LICENSE.md`](LICENSE.md). diff --git a/Semantics.Paths/Semantics.Paths.csproj b/Semantics.Paths/Semantics.Paths.csproj index a9cc634..e498fea 100644 --- a/Semantics.Paths/Semantics.Paths.csproj +++ b/Semantics.Paths/Semantics.Paths.csproj @@ -1,23 +1,19 @@ - - net10.0;net9.0;net8.0;net7.0;net6.0;net5.0;netstandard2.0;netstandard2.1; - $(NoWarn);CA1866;CA2249;IDE0056;IDE0057;IDE0032; - - - - - + net10.0;net9.0;net8.0;netstandard2.0;netstandard2.1 + $(NoWarn);CA1716;CA2225;IDE0032;CA1866;CA2249;IDE0056;IDE0057 - - + + - + + + diff --git a/Semantics.Paths/Validation/Attributes/Path/IsExtensionAttribute.cs b/Semantics.Paths/Validation/Attributes/Path/IsExtensionAttribute.cs new file mode 100644 index 0000000..b9f0899 --- /dev/null +++ b/Semantics.Paths/Validation/Attributes/Path/IsExtensionAttribute.cs @@ -0,0 +1,42 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Paths; + +using System; +using ktsu.Semantics.Strings; + +/// +/// Validates that a string represents a valid file extension (starts with a period) +/// +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] +public sealed class IsExtensionAttribute : NativeSemanticStringValidationAttribute +{ + /// + /// Creates the validation adapter for file extension validation. + /// + /// A validation adapter for file extensions + protected override ValidationAdapter CreateValidator() => new ExtensionValidator(); + + /// + /// Validation adapter for file extensions. + /// + private sealed class ExtensionValidator : ValidationAdapter + { + /// + /// Validates that a string represents a valid file extension. + /// + /// The string value to validate + /// A validation result indicating success or failure + protected override ValidationResult ValidateValue(string value) + { + if (string.IsNullOrEmpty(value) || value.StartsWith('.')) + { + return ValidationResult.Success(); + } + + return ValidationResult.Failure("File extension must start with a period (.)."); + } + } +} diff --git a/Semantics.Paths/Validation/Attributes/Path/IsValidFileNameAttribute.cs b/Semantics.Paths/Validation/Attributes/Path/IsValidFileNameAttribute.cs new file mode 100644 index 0000000..cacbd79 --- /dev/null +++ b/Semantics.Paths/Validation/Attributes/Path/IsValidFileNameAttribute.cs @@ -0,0 +1,51 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Paths; + +using System; +using System.IO; +using ktsu.Semantics.Strings; + +/// +/// Validates that a path string contains valid filename characters using span semantics. +/// +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] +public sealed class IsValidFileNameAttribute : NativeSemanticStringValidationAttribute +{ + /// + /// Creates the validation adapter for valid filename validation. + /// + /// A validation adapter for valid filename strings + protected override ValidationAdapter CreateValidator() => new ValidFileNameValidator(); + + /// + /// Validation adapter for valid filename strings. + /// + private sealed class ValidFileNameValidator : ValidationAdapter + { + private static readonly char[] InvalidFileNameChars = Path.GetInvalidFileNameChars(); + + /// + /// Validates that a filename string contains only valid filename characters. + /// + /// The filename string to validate + /// A validation result indicating success or failure + protected override ValidationResult ValidateValue(string value) + { + if (string.IsNullOrEmpty(value)) + { + return ValidationResult.Success(); + } + + ReadOnlySpan valueSpan = value.AsSpan(); + if (valueSpan.IndexOfAny(InvalidFileNameChars) != -1) + { + return ValidationResult.Failure("The filename contains invalid characters."); + } + + return ValidationResult.Success(); + } + } +} diff --git a/Semantics.Paths/Validation/Attributes/Path/IsValidPathAttribute.cs b/Semantics.Paths/Validation/Attributes/Path/IsValidPathAttribute.cs new file mode 100644 index 0000000..710a9c3 --- /dev/null +++ b/Semantics.Paths/Validation/Attributes/Path/IsValidPathAttribute.cs @@ -0,0 +1,51 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Paths; + +using System; +using System.IO; +using ktsu.Semantics.Strings; + +/// +/// Validates that a path string contains valid path characters using span semantics. +/// +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] +public sealed class IsValidPathAttribute : NativeSemanticStringValidationAttribute +{ + /// + /// Creates the validation adapter for valid path validation. + /// + /// A validation adapter for valid path strings + protected override ValidationAdapter CreateValidator() => new ValidPathValidator(); + + /// + /// Validation adapter for valid path strings. + /// + private sealed class ValidPathValidator : ValidationAdapter + { + private static readonly char[] InvalidPathChars = Path.GetInvalidPathChars(); + + /// + /// Validates that a path string contains only valid path characters. + /// + /// The path string to validate + /// A validation result indicating success or failure + protected override ValidationResult ValidateValue(string value) + { + if (string.IsNullOrEmpty(value)) + { + return ValidationResult.Success(); + } + + ReadOnlySpan valueSpan = value.AsSpan(); + if (valueSpan.IndexOfAny(InvalidPathChars) != -1) + { + return ValidationResult.Failure("The path contains invalid characters."); + } + + return ValidationResult.Success(); + } + } +} diff --git a/Semantics.Quantities.Decimal/Semantics.Quantities.Decimal.csproj b/Semantics.Quantities.Decimal/Semantics.Quantities.Decimal.csproj new file mode 100644 index 0000000..39a6a7f --- /dev/null +++ b/Semantics.Quantities.Decimal/Semantics.Quantities.Decimal.csproj @@ -0,0 +1,24 @@ + + + + + + + net10.0;net9.0;net8.0 + + false + Storage-type aliases for ktsu.Semantics.Quantities. Reference this package to write `Mass` instead of `Mass<decimal>` (and every other quantity) bound to decimal, project-wide. Use one storage-type alias package per project. + + $(NoWarn);NU5128 + + + + + + + + + + + + diff --git a/Semantics.Quantities.Decimal/buildTransitive/ktsu.Semantics.Quantities.Decimal.props b/Semantics.Quantities.Decimal/buildTransitive/ktsu.Semantics.Quantities.Decimal.props new file mode 100644 index 0000000..d6cfa00 --- /dev/null +++ b/Semantics.Quantities.Decimal/buildTransitive/ktsu.Semantics.Quantities.Decimal.props @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Semantics.Quantities.Double/Semantics.Quantities.Double.csproj b/Semantics.Quantities.Double/Semantics.Quantities.Double.csproj new file mode 100644 index 0000000..5129741 --- /dev/null +++ b/Semantics.Quantities.Double/Semantics.Quantities.Double.csproj @@ -0,0 +1,24 @@ + + + + + + + net10.0;net9.0;net8.0 + + false + Storage-type aliases for ktsu.Semantics.Quantities. Reference this package to write `Mass` instead of `Mass<double>` (and every other quantity) bound to double, project-wide. Use one storage-type alias package per project. + + $(NoWarn);NU5128 + + + + + + + + + + + + diff --git a/Semantics.Quantities.Double/buildTransitive/ktsu.Semantics.Quantities.Double.props b/Semantics.Quantities.Double/buildTransitive/ktsu.Semantics.Quantities.Double.props new file mode 100644 index 0000000..8382ba7 --- /dev/null +++ b/Semantics.Quantities.Double/buildTransitive/ktsu.Semantics.Quantities.Double.props @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Semantics.Quantities.Float/Semantics.Quantities.Float.csproj b/Semantics.Quantities.Float/Semantics.Quantities.Float.csproj new file mode 100644 index 0000000..c10f80c --- /dev/null +++ b/Semantics.Quantities.Float/Semantics.Quantities.Float.csproj @@ -0,0 +1,24 @@ + + + + + + + net10.0;net9.0;net8.0 + + false + Storage-type aliases for ktsu.Semantics.Quantities. Reference this package to write `Mass` instead of `Mass<float>` (and every other quantity) bound to float, project-wide. Use one storage-type alias package per project. + + $(NoWarn);NU5128 + + + + + + + + + + + + diff --git a/Semantics.Quantities.Float/buildTransitive/ktsu.Semantics.Quantities.Float.props b/Semantics.Quantities.Float/buildTransitive/ktsu.Semantics.Quantities.Float.props new file mode 100644 index 0000000..bfc7c60 --- /dev/null +++ b/Semantics.Quantities.Float/buildTransitive/ktsu.Semantics.Quantities.Float.props @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Semantics.Quantities/Acoustic/AcousticImpedance.cs b/Semantics.Quantities/Acoustic/AcousticImpedance.cs deleted file mode 100644 index 59975f1..0000000 --- a/Semantics.Quantities/Acoustic/AcousticImpedance.cs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an acoustic impedance quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record AcousticImpedance : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of acousticimpedance [M L⁻² T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.AcousticImpedance; - - /// - /// Initializes a new instance of the class. - /// - public AcousticImpedance() : base() { } - - /// - /// Creates a new AcousticImpedance from a value in pascal-seconds per meter. - /// - /// The value in pascal-seconds per meter. - /// A new AcousticImpedance instance. - public static AcousticImpedance FromPascalSecondsPerMeter(T pascalSecondsPerMeter) => Create(pascalSecondsPerMeter); - - /// - /// Creates a new AcousticImpedance from a value in rayls. - /// - /// The value in rayls (Pa·s/m). - /// A new AcousticImpedance instance. - public static AcousticImpedance FromRayls(T rayls) => Create(rayls); - - /// - /// Calculates acoustic impedance from material density and sound speed (Z = ρc). - /// - /// The material density. - /// The sound speed in the material. - /// The resulting acoustic impedance. - public static AcousticImpedance FromDensityAndSoundSpeed(Density density, SoundSpeed soundSpeed) - { - Ensure.NotNull(density); - Ensure.NotNull(soundSpeed); - - T impedanceValue = density.Value * soundSpeed.Value; - - return Create(impedanceValue); - } - - /// - /// Calculates sound speed from acoustic impedance and density (c = Z/ρ). - /// - /// The acoustic impedance. - /// The material density. - /// The resulting sound speed. - public static SoundSpeed CalculateSoundSpeed(AcousticImpedance impedance, Density density) - { - Ensure.NotNull(impedance); - Ensure.NotNull(density); - - T soundSpeedValue = impedance.Value / density.Value; - - return SoundSpeed.Create(soundSpeedValue); - } - - /// - /// Calculates material density from acoustic impedance and sound speed (ρ = Z/c). - /// - /// The acoustic impedance. - /// The sound speed in the material. - /// The resulting material density. - public static Density CalculateDensity(AcousticImpedance impedance, SoundSpeed soundSpeed) - { - Ensure.NotNull(impedance); - Ensure.NotNull(soundSpeed); - - T densityValue = impedance.Value / soundSpeed.Value; - - return Density.Create(densityValue); - } - - /// - /// Calculates acoustic impedance using standard air properties at 20°C. - /// - /// The acoustic impedance of air at standard conditions. - public static AcousticImpedance ForStandardAir() - { - T airDensity = PhysicalConstants.Generic.StandardAirDensity(); - T airSoundSpeed = T.CreateChecked(343.0); // m/s at 20°C - T impedanceValue = airDensity * airSoundSpeed; - - return Create(impedanceValue); - } - - /// - /// Multiplies density by sound speed to create acoustic impedance. - /// - /// The density. - /// The sound speed. - /// The resulting acoustic impedance. - public static AcousticImpedance Multiply(Density density, SoundSpeed soundSpeed) - { - Ensure.NotNull(density); - Ensure.NotNull(soundSpeed); - return Create(density.Value * soundSpeed.Value); - } - - /// - /// Divides sound pressure by velocity to create acoustic impedance. - /// - /// The sound pressure. - /// The particle velocity. - /// The resulting acoustic impedance. - public static AcousticImpedance Divide(SoundPressure soundPressure, Velocity velocity) - { - Ensure.NotNull(soundPressure); - Ensure.NotNull(velocity); - return Create(soundPressure.Value / velocity.Value); - } -} diff --git a/Semantics.Quantities/Acoustic/DirectionalityIndex.cs b/Semantics.Quantities/Acoustic/DirectionalityIndex.cs deleted file mode 100644 index 9bec9cc..0000000 --- a/Semantics.Quantities/Acoustic/DirectionalityIndex.cs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a directionality index quantity with compile-time dimensional safety. -/// Directionality index (DI) measures how directional a sound source is, in dB. -/// -public sealed record DirectionalityIndex : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of directionality index [1]. - public override PhysicalDimension Dimension => PhysicalDimensions.DirectionalityIndex; - - /// - /// Initializes a new instance of the DirectionalityIndex class. - /// - public DirectionalityIndex() : base() { } - - /// - /// Creates a new DirectionalityIndex from a value in decibels. - /// - /// The directionality index in dB. - /// A new DirectionalityIndex instance. - public static DirectionalityIndex FromDecibels(T decibels) => Create(decibels); - - /// - /// Creates a DirectionalityIndex from directivity factor Q. - /// DI = 10 * log₁₀(Q) - /// - /// The directivity factor Q. - /// The corresponding directionality index. - public static DirectionalityIndex FromDirectivityFactor(T directivityFactor) - { - T decibels = T.CreateChecked(10.0 * Math.Log10(double.CreateChecked(directivityFactor))); - return FromDecibels(decibels); - } - - /// - /// Converts to directivity factor Q. - /// Q = 10^(DI/10) - /// - /// The directivity factor. - public T ToDirectivityFactor() - { - double factor = Math.Pow(10.0, double.CreateChecked(Value) / 10.0); - return T.CreateChecked(factor); - } - - /// - /// Gets the directivity pattern description. - /// - /// A string describing the directivity pattern. - public string GetDirectivityPattern() => double.CreateChecked(Value) switch - { - < 1.0 => "Omnidirectional (no directivity)", - < 3.0 => "Slightly directional", - < 6.0 => "Moderately directional", - < 9.0 => "Highly directional", - < 12.0 => "Very directional", - _ => "Extremely directional (beam-like)" - }; - - /// - /// Estimates the beamwidth (approximate half-power angle) from DI. - /// This is a rough approximation based on circular patterns. - /// - /// Estimated beamwidth in degrees. - public T EstimateBeamwidth() - { - // Rough approximation: θ ≈ 58.3° / sqrt(Q) - T directivityFactor = ToDirectivityFactor(); - double beamwidth = 58.3 / Math.Sqrt(double.CreateChecked(directivityFactor)); - return T.CreateChecked(beamwidth); - } - - /// - /// Calculates the front-to-back ratio for typical loudspeaker patterns. - /// - /// Front-to-back ratio in dB (estimate). - public T EstimateFrontToBackRatio() - { - // Rough correlation between DI and front-to-back ratio - double ratio = double.CreateChecked(Value) * 1.5; // Empirical approximation - return T.CreateChecked(Math.Min(ratio, 30.0)); // Cap at 30 dB - } - - /// - /// Gets the typical application based on directionality index. - /// - /// A string describing typical applications. - public string GetTypicalApplication() => double.CreateChecked(Value) switch - { - < 2.0 => "Ambient sound sources, subwoofers", - < 4.0 => "Monitor speakers, near-field applications", - < 6.0 => "Home audio, bookshelf speakers", - < 8.0 => "Studio monitors, PA speakers", - < 10.0 => "Horn-loaded speakers, line arrays", - _ => "Highly directional arrays, sound reinforcement" - }; - - /// - /// Calculates coverage angle for symmetrical patterns. - /// - /// Coverage level in dB below peak (typically -3, -6, or -10 dB). - /// Coverage angle in degrees. - public T CoverageAngle(T level) - { - // Simplified calculation based on circular symmetry - // Real calculations require detailed polar patterns - T adjustedDi = Value + level; // Adjust for coverage level - T adjustedQ = T.CreateChecked(Math.Pow(10.0, double.CreateChecked(adjustedDi) / 10.0)); - double angle = 2.0 * Math.Acos(1.0 / Math.Sqrt(double.CreateChecked(adjustedQ))) * 180.0 / Math.PI; - return T.CreateChecked(angle); - } - - /// - /// Estimates sound pressure level gain compared to omnidirectional source. - /// - /// SPL gain in dB on-axis. - public T OnAxisGain() => Value; // DI directly represents on-axis gain vs omnidirectional -} diff --git a/Semantics.Quantities/Acoustic/Frequency.cs b/Semantics.Quantities/Acoustic/Frequency.cs deleted file mode 100644 index 7744e1c..0000000 --- a/Semantics.Quantities/Acoustic/Frequency.cs +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a frequency quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Frequency : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of frequency [T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.Frequency; - - /// - /// Initializes a new instance of the class. - /// - public Frequency() : base() { } - - /// - /// Creates a new instance with the specified value. - /// - /// The value for the quantity. - /// A new instance of the quantity. - /// Thrown when the frequency is negative. - public static new Frequency Create(T value) - { - if (T.IsNegative(value)) - { - throw new ArgumentException("Frequency cannot be negative.", nameof(value)); - } - - return new Frequency() with { Quantity = value }; - } - - /// - /// Creates a new Frequency from a value in hertz. - /// - /// The value in hertz. - /// A new Frequency instance. - public static Frequency FromHertz(T hertz) => Create(hertz); - - /// - /// Creates a new Frequency from a value in kilohertz. - /// - /// The value in kilohertz. - /// A new Frequency instance. - public static Frequency FromKilohertz(T kilohertz) => Create(kilohertz * T.CreateChecked(1000)); - - /// - /// Creates a new Frequency from a value in megahertz. - /// - /// The value in megahertz. - /// A new Frequency instance. - public static Frequency FromMegahertz(T megahertz) => Create(megahertz * T.CreateChecked(1_000_000)); - - /// - /// Calculates electromagnetic wavelength from frequency using speed of light (λ = c/f). - /// - /// The electromagnetic frequency. - /// The resulting wavelength in vacuum. - public static Wavelength GetElectromagneticWavelength(Frequency frequency) - { - Ensure.NotNull(frequency); - - T speedOfLight = PhysicalConstants.Generic.SpeedOfLight(); - T wavelengthValue = speedOfLight / frequency.Value; - - return Wavelength.Create(wavelengthValue); - } - - /// - /// Calculates photon energy from frequency using Planck's constant (E = hf). - /// - /// The photon frequency. - /// The resulting photon energy. - public static Energy GetPhotonEnergy(Frequency frequency) - { - Ensure.NotNull(frequency); - - T planckConstant = T.CreateChecked(PhysicalConstants.Fundamental.PlanckConstant); - T energyValue = planckConstant * frequency.Value; - - return Energy.Create(energyValue); - } - - /// - /// Calculates frequency from photon energy using Planck's constant (f = E/h). - /// - /// The photon energy. - /// The resulting frequency. - public static Frequency FromPhotonEnergy(Energy photonEnergy) - { - Ensure.NotNull(photonEnergy); - - T planckConstant = T.CreateChecked(PhysicalConstants.Fundamental.PlanckConstant); - T frequencyValue = photonEnergy.Value / planckConstant; - - return Create(frequencyValue); - } - - /// - /// Divides one by time to create frequency. - /// - /// The value one. - /// The time period. - /// The resulting frequency. - public static Frequency Divide(T one, Time time) - { - Ensure.NotNull(time); - return Create(one / time.Value); - } - - /// - /// Multiplies frequency by wavelength to get speed. - /// - /// The frequency. - /// The wavelength. - /// The resulting speed. - public static Velocity Multiply(Frequency frequency, Wavelength wavelength) - { - Ensure.NotNull(frequency); - Ensure.NotNull(wavelength); - return Velocity.Create(frequency.Value * wavelength.Value); - } - - /// - /// Calculates the wave speed from frequency and wavelength (v = f × λ). - /// - /// The frequency. - /// The wavelength. - /// The resulting wave speed. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static SoundSpeed operator *(Frequency frequency, Wavelength wavelength) - { - Ensure.NotNull(frequency); - Ensure.NotNull(wavelength); - - T speedValue = frequency.Value * wavelength.Value; - - return SoundSpeed.Create(speedValue); - } - - /// - /// Calculates the period from frequency (T = 1/f). - /// - /// The value 1 (dimensionless). - /// The frequency. - /// The resulting period (time). - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Time operator /(T one, Frequency frequency) - { - Ensure.NotNull(frequency); - - T periodValue = one / frequency.Value; - - return Time.Create(periodValue); - } - - /// - /// Calculates the wavelength from frequency and wave speed (λ = v/f). - /// - /// The wave speed. - /// The frequency. - /// The resulting wavelength. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Wavelength operator /(SoundSpeed speed, Frequency frequency) - { - Ensure.NotNull(speed); - Ensure.NotNull(frequency); - - T wavelengthValue = speed.Value / frequency.Value; - - return Wavelength.Create(wavelengthValue); - } -} diff --git a/Semantics.Quantities/Acoustic/Loudness.cs b/Semantics.Quantities/Acoustic/Loudness.cs deleted file mode 100644 index c4936f5..0000000 --- a/Semantics.Quantities/Acoustic/Loudness.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a loudness quantity with compile-time dimensional safety. -/// Loudness is a perceptual measure of sound strength, typically measured in sones. -/// -public sealed record Loudness : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of loudness [1]. - public override PhysicalDimension Dimension => PhysicalDimensions.Loudness; - - /// - /// Initializes a new instance of the Loudness class. - /// - public Loudness() : base() { } - - /// - /// Creates a new Loudness from a value in sones. - /// - /// The loudness in sones. - /// A new Loudness instance. - public static Loudness FromSones(T sones) => Create(sones); - - /// - /// Creates a new Loudness from a value in phons (loudness level). - /// This uses Stevens' power law: S = k * (φ - φ₀)^n, where n ≈ 0.3 for loudness - /// - /// The loudness level in phons. - /// A new Loudness instance. - public static Loudness FromPhons(T phons) - { - // Stevens' power law approximation for loudness - // At 40 phons = 1 sone (reference) - T phonDifference = phons - T.CreateChecked(40.0); - T sones = T.CreateChecked(Math.Pow(2.0, double.CreateChecked(phonDifference) / 10.0)); - return FromSones(sones); - } - - /// - /// Converts loudness to phons (loudness level). - /// - /// The loudness level in phons. - public T ToPhons() - { - // Inverse of Stevens' power law - T logRatio = T.CreateChecked(Math.Log2(double.CreateChecked(Value))); - return T.CreateChecked(40.0) + (logRatio * T.CreateChecked(10.0)); - } - - /// - /// Gets loudness as a multiple of the reference (1 sone). - /// - /// The loudness ratio. - public T ToLoudnessRatio() => Value; - - /// - /// Calculates the combined loudness of multiple sound sources. - /// Combined loudness is not simply additive due to masking effects. - /// This uses a simplified model: L_total = (L₁^α + L₂^α + ...)^(1/α) where α ≈ 0.3 - /// - /// Another loudness value. - /// The combined loudness. - public Loudness CombineWith(Loudness other) - { - Ensure.NotNull(other); - - // Simplified combination using power law - double alpha = 0.3; - double l1 = Math.Pow(double.CreateChecked(Value), alpha); - double l2 = Math.Pow(double.CreateChecked(other.Value), alpha); - double combined = Math.Pow(l1 + l2, 1.0 / alpha); - - return FromSones(T.CreateChecked(combined)); - } - - /// - /// Gets the loudness category based on sone value. - /// - /// A string describing the loudness level. - public string GetLoudnessCategory() => double.CreateChecked(Value) switch - { - < 0.1 => "Very Quiet", - < 0.25 => "Quiet", - < 1.0 => "Moderate", - < 4.0 => "Loud", - < 16.0 => "Very Loud", - _ => "Extremely Loud" - }; - - /// - /// Estimates the sound pressure level that would produce this loudness for a 1 kHz tone. - /// This is an approximation based on the equal-loudness contours. - /// - /// The estimated SPL in dB. - public T EstimateSPLAt1kHz() - { - T phons = ToPhons(); - // For 1 kHz pure tone, phons ≈ dB SPL - return phons; - } -} diff --git a/Semantics.Quantities/Acoustic/NoiseReductionCoefficient.cs b/Semantics.Quantities/Acoustic/NoiseReductionCoefficient.cs deleted file mode 100644 index 3ee2738..0000000 --- a/Semantics.Quantities/Acoustic/NoiseReductionCoefficient.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a noise reduction coefficient quantity with compile-time dimensional safety. -/// NRC is the average absorption coefficient at 250, 500, 1000, and 2000 Hz. -/// -public sealed record NoiseReductionCoefficient : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of noise reduction coefficient [1]. - public override PhysicalDimension Dimension => PhysicalDimensions.NoiseReductionCoefficient; - - /// - /// Initializes a new instance of the NoiseReductionCoefficient class. - /// - public NoiseReductionCoefficient() : base() { } - - /// - /// Creates a new NoiseReductionCoefficient from a value (0 to 1.25). - /// - /// The NRC value (0-1.25, typically 0-1). - /// A new NoiseReductionCoefficient instance. - public static NoiseReductionCoefficient FromCoefficient(T coefficient) => Create(coefficient); - - /// - /// Calculates NRC from absorption coefficients at standard frequencies. - /// NRC = (α₂₅₀ + α₅₀₀ + α₁₀₀₀ + α₂₀₀₀) / 4 - /// - /// Absorption coefficient at 250 Hz. - /// Absorption coefficient at 500 Hz. - /// Absorption coefficient at 1000 Hz. - /// Absorption coefficient at 2000 Hz. - /// The calculated NRC. - public static NoiseReductionCoefficient FromAbsorptionCoefficients( - SoundAbsorption alpha250Hz, - SoundAbsorption alpha500Hz, - SoundAbsorption alpha1000Hz, - SoundAbsorption alpha2000Hz) - { - Ensure.NotNull(alpha250Hz); - Ensure.NotNull(alpha500Hz); - Ensure.NotNull(alpha1000Hz); - Ensure.NotNull(alpha2000Hz); - - T sum = alpha250Hz.Value + alpha500Hz.Value + alpha1000Hz.Value + alpha2000Hz.Value; - T average = sum / T.CreateChecked(4); - return FromCoefficient(average); - } - - /// - /// Rounds to the nearest 0.05 as per ASTM standards. - /// - /// The rounded NRC value. - public NoiseReductionCoefficient RoundToStandard() - { - T roundedValue = T.CreateChecked(Math.Round(double.CreateChecked(Value) * 20.0) / 20.0); - return FromCoefficient(roundedValue); - } - - /// - /// Converts to percentage. - /// - /// The NRC as a percentage. - public T ToPercentage() => Value * T.CreateChecked(100); - - /// - /// Gets the acoustic class rating based on NRC value. - /// - /// A string describing the acoustic performance class. - public string GetAcousticClass() => double.CreateChecked(Value) switch - { - < 0.15 => "Class E (Poor)", - < 0.25 => "Class D (Fair)", - < 0.35 => "Class C (Good)", - < 0.50 => "Class B (Very Good)", - < 0.75 => "Class A (Excellent)", - _ => "Class A+ (Superior)" - }; -} diff --git a/Semantics.Quantities/Acoustic/Pitch.cs b/Semantics.Quantities/Acoustic/Pitch.cs deleted file mode 100644 index c139a78..0000000 --- a/Semantics.Quantities/Acoustic/Pitch.cs +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a pitch quantity with compile-time dimensional safety. -/// Pitch is the perceptual correlate of frequency, measured in Hz, mels, or barks. -/// -public sealed record Pitch : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of pitch [T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.Pitch; - - /// - /// Initializes a new instance of the Pitch class. - /// - public Pitch() : base() { } - - /// - /// Creates a new Pitch from a frequency value in Hz. - /// - /// The frequency in Hz. - /// A new Pitch instance. - public static Pitch FromHertz(T hertz) => Create(hertz); - - /// - /// Creates a new Pitch from a value in mels (perceptual pitch scale). - /// Mel scale: f_mel = 1127 * ln(1 + f_hz / 700) - /// - /// The pitch in mels. - /// A new Pitch instance. - public static Pitch FromMels(T mels) - { - double melValue = double.CreateChecked(mels); - double hertz = 700.0 * (Math.Exp(melValue / 1127.0) - 1.0); - return FromHertz(T.CreateChecked(hertz)); - } - - /// - /// Creates a new Pitch from a value in barks (critical band scale). - /// Bark scale: f_bark = 7 * ln(f_hz / 650 + sqrt((f_hz / 650)² + 1)) - /// - /// The pitch in barks. - /// A new Pitch instance. - public static Pitch FromBarks(T barks) - { - // Inverse bark formula: f_hz = 650 * sinh(f_bark / 7) - double barkValue = double.CreateChecked(barks); - double hertz = 650.0 * Math.Sinh(barkValue / 7.0); - return FromHertz(T.CreateChecked(hertz)); - } - - /// - /// Converts frequency to mels (perceptual pitch scale). - /// - /// The pitch in mels. - public T ToMels() - { - double hertz = double.CreateChecked(Value); - double mels = 1127.0 * Math.Log(1.0 + (hertz / 700.0)); - return T.CreateChecked(mels); - } - - /// - /// Converts frequency to barks (critical band scale). - /// - /// The pitch in barks. - public T ToBarks() - { - double hertz = double.CreateChecked(Value); - double ratio = hertz / 650.0; - double barks = 7.0 * Math.Log(ratio + Math.Sqrt((ratio * ratio) + 1.0)); - return T.CreateChecked(barks); - } - - /// - /// Calculates the musical interval between two pitches in cents. - /// - /// The other pitch. - /// The interval in cents (1200 cents = 1 octave). - public T IntervalInCents(Pitch other) - { - Ensure.NotNull(other); - - double ratio = double.CreateChecked(other.Value) / double.CreateChecked(Value); - double cents = 1200.0 * Math.Log2(ratio); - return T.CreateChecked(cents); - } - - /// - /// Gets the musical note name closest to this pitch (assuming equal temperament). - /// - /// The note name and octave. - public string GetNoteName() - { - double frequency = double.CreateChecked(Value); - - // A4 = 440 Hz as reference - double a4 = 440.0; - double semitonesFromA4 = 12.0 * Math.Log2(frequency / a4); - int semitones = (int)Math.Round(semitonesFromA4); - - string[] noteNames = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"]; - int noteIndex = ((semitones % 12) + 12) % 12; - int octave = 4 + ((semitones + 9) / 12); - - return $"{noteNames[noteIndex]}{octave}"; - } - - /// - /// Calculates the frequency of a musical note given its offset in semitones from this pitch. - /// - /// The number of semitones (positive = higher, negative = lower). - /// The resulting pitch. - public Pitch TransposeBySemitones(int semitones) - { - double ratio = Math.Pow(2.0, semitones / 12.0); - T newFrequency = T.CreateChecked(double.CreateChecked(Value) * ratio); - return FromHertz(newFrequency); - } - - /// - /// Gets the pitch category description. - /// - /// A string describing the pitch range. - public string GetPitchCategory() => double.CreateChecked(Value) switch - { - < 20 => "Infrasonic", - < 200 => "Very Low", - < 500 => "Low", - < 2000 => "Mid", - < 5000 => "High", - < 20000 => "Very High", - _ => "Ultrasonic" - }; -} diff --git a/Semantics.Quantities/Acoustic/ReflectionCoefficient.cs b/Semantics.Quantities/Acoustic/ReflectionCoefficient.cs deleted file mode 100644 index ed4ec95..0000000 --- a/Semantics.Quantities/Acoustic/ReflectionCoefficient.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a reflection coefficient quantity with compile-time dimensional safety. -/// Reflection coefficient is the ratio of reflected to incident sound energy at an interface. -/// -public sealed record ReflectionCoefficient : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of reflection coefficient [1]. - public override PhysicalDimension Dimension => PhysicalDimensions.ReflectionCoefficient; - - /// - /// Initializes a new instance of the ReflectionCoefficient class. - /// - public ReflectionCoefficient() : base() { } - - /// - /// Creates a new ReflectionCoefficient from a value (0 to 1). - /// - /// The reflection coefficient (0 = perfect absorption, 1 = perfect reflection). - /// A new ReflectionCoefficient instance. - public static ReflectionCoefficient FromCoefficient(T coefficient) => Create(coefficient); - - /// - /// Creates a ReflectionCoefficient from an absorption coefficient. - /// R = 1 - α (where α is the absorption coefficient) - /// - /// The absorption coefficient. - /// The corresponding reflection coefficient. - public static ReflectionCoefficient FromAbsorptionCoefficient(SoundAbsorption absorptionCoefficient) - { - Ensure.NotNull(absorptionCoefficient); - return FromCoefficient(T.One - absorptionCoefficient.Value); - } - - /// - /// Gets the corresponding absorption coefficient. - /// α = 1 - R - /// - /// The absorption coefficient. - public SoundAbsorption ToAbsorptionCoefficient() => SoundAbsorption.Create(T.One - Value); - - /// - /// Calculates the reflected sound pressure amplitude ratio. - /// For normal incidence: r = (Z₂ - Z₁) / (Z₂ + Z₁) - /// - /// Acoustic impedance of first medium. - /// Acoustic impedance of second medium. - /// The pressure reflection coefficient. - public static ReflectionCoefficient FromImpedances(AcousticImpedance impedance1, AcousticImpedance impedance2) - { - Ensure.NotNull(impedance1); - Ensure.NotNull(impedance2); - - T numerator = impedance2.Value - impedance1.Value; - T denominator = impedance2.Value + impedance1.Value; - T reflectionCoeff = numerator / denominator; - return FromCoefficient(reflectionCoeff); - } - - /// - /// Calculates the transmission coefficient. - /// T = 1 - R (for energy) - /// - /// The transmission coefficient. - public T TransmissionCoefficient() => T.One - Value; - - /// - /// Calculates reflection at oblique incidence (simplified Fresnel equation). - /// - /// Angle of incidence in radians. - /// Ratio of acoustic impedances Z₂/Z₁. - /// The oblique reflection coefficient. - public static ReflectionCoefficient AtObliqueIncidence(T incidenceAngle, T impedanceRatio) - { - T sinTheta = T.CreateChecked(Math.Sin(double.CreateChecked(incidenceAngle))); - - // Simplified calculation for oblique incidence - T normalReflection = (impedanceRatio - T.One) / (impedanceRatio + T.One); - T obliqueCorrection = T.One - (sinTheta * sinTheta / (impedanceRatio * impedanceRatio)); - T obliqueReflection = normalReflection * obliqueCorrection; - - return FromCoefficient(obliqueReflection); - } -} diff --git a/Semantics.Quantities/Acoustic/ReverberationTime.cs b/Semantics.Quantities/Acoustic/ReverberationTime.cs deleted file mode 100644 index 660dc09..0000000 --- a/Semantics.Quantities/Acoustic/ReverberationTime.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a reverberation time quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record ReverberationTime : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of reverberationtime [T]. - public override PhysicalDimension Dimension => PhysicalDimensions.ReverberationTime; - - /// - /// Initializes a new instance of the class. - /// - public ReverberationTime() : base() { } - - /// - /// Creates a new ReverberationTime from a value in seconds. - /// - /// The value in seconds. - /// A new ReverberationTime instance. - public static ReverberationTime FromSeconds(T seconds) => Create(seconds); - - /// - /// Creates a new ReverberationTime from T60 measurement. - /// - /// The T60 time in seconds. - /// A new ReverberationTime instance. - public static ReverberationTime FromT60(T t60) => Create(t60); - - /// - /// Creates a new ReverberationTime from T30 measurement (extrapolated to T60). - /// - /// The T30 time in seconds. - /// A new ReverberationTime instance. - public static ReverberationTime FromT30(T t30) => Create(t30 * T.CreateChecked(2)); - - /// - /// Calculates reverberation time using Sabine formula: RT = 0.161 * V / A. - /// - /// The room volume. - /// The total absorption. - /// The calculated reverberation time. - public static ReverberationTime CalculateSabine(Volume volume, T totalAbsorption) - { - Ensure.NotNull(volume); - return Create(T.CreateChecked(0.161) * volume.Value / totalAbsorption); - } - - /// - /// Converts ReverberationTime to Time. - /// - /// The equivalent time. - public Time ToTime() => Time.Create(Value); -} diff --git a/Semantics.Quantities/Acoustic/Sensitivity.cs b/Semantics.Quantities/Acoustic/Sensitivity.cs deleted file mode 100644 index 2512026..0000000 --- a/Semantics.Quantities/Acoustic/Sensitivity.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a sensitivity quantity with compile-time dimensional safety. -/// Sensitivity measures the efficiency of electroacoustic transducers. -/// -public sealed record Sensitivity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of sensitivity [1]. - public override PhysicalDimension Dimension => PhysicalDimensions.Sensitivity; - - /// - /// Initializes a new instance of the Sensitivity class. - /// - public Sensitivity() : base() { } - - /// - /// Creates a new Sensitivity from a value in dB (SPL/W). - /// - /// The sensitivity in dB SPL/W. - /// A new Sensitivity instance. - public static Sensitivity FromDbSplPerWatt(T dbSplPerWatt) => Create(dbSplPerWatt); - - /// - /// Creates a new Sensitivity from a value in dB (SPL/V). - /// - /// The sensitivity in dB SPL/V. - /// A new Sensitivity instance. - public static Sensitivity FromDbSplPerVolt(T dbSplPerVolt) => Create(dbSplPerVolt); - - /// - /// Creates a new Sensitivity from a value in mV/Pa (microphone sensitivity). - /// - /// The sensitivity in mV/Pa. - /// A new Sensitivity instance. - public static Sensitivity FromMvPerPa(T mvPerPa) - { - // Convert mV/Pa to dB re 1 V/Pa - // dB = 20 * log10(mV/Pa / 1000) - double dbValue = 20.0 * Math.Log10(double.CreateChecked(mvPerPa) / 1000.0); - return Create(T.CreateChecked(dbValue)); - } - - /// - /// Converts sensitivity to mV/Pa (for microphones). - /// - /// The sensitivity in mV/Pa. - public T ToMvPerPa() - { - // Inverse conversion from dB re 1 V/Pa to mV/Pa - double mvPerPa = 1000.0 * Math.Pow(10.0, double.CreateChecked(Value) / 20.0); - return T.CreateChecked(mvPerPa); - } - - /// - /// Gets the sensitivity efficiency category. - /// - /// A string describing the efficiency level. - public string GetEfficiencyCategory() => double.CreateChecked(Value) switch - { - < 80 => "Very Low Efficiency", - < 85 => "Low Efficiency", - < 90 => "Moderate Efficiency", - < 95 => "High Efficiency", - < 100 => "Very High Efficiency", - _ => "Exceptional Efficiency" - }; - - /// - /// Estimates power consumption for a target SPL. - /// - /// The target sound pressure level. - /// Estimated power consumption in watts. - public T EstimatePowerConsumption(SoundPressureLevel targetSpl) - { - Ensure.NotNull(targetSpl); - - // Power (dB) = Target SPL - Sensitivity - // Power (W) = 10^(Power(dB)/10) - T powerDb = targetSpl.Value - Value; - double powerWatts = Math.Pow(10.0, double.CreateChecked(powerDb) / 10.0); - return T.CreateChecked(powerWatts); - } - - /// - /// Calculates the maximum SPL at 1 meter for a given power input. - /// - /// The input power. - /// The maximum SPL at 1 meter. - public SoundPressureLevel MaximumSplAt1m(Power inputPower) - { - Ensure.NotNull(inputPower); - - // SPL = Sensitivity + 10*log10(Power) - T powerDb = T.CreateChecked(10.0 * Math.Log10(double.CreateChecked(inputPower.Value))); - T maxSpl = Value + powerDb; - return SoundPressureLevel.FromDecibels(maxSpl); - } - - /// - /// Gets the typical application based on sensitivity value. - /// - /// A string describing typical applications. - public string GetTypicalApplication() => double.CreateChecked(Value) switch - { - < 82 => "High-power PA systems, subwoofers", - < 87 => "Home audio, bookshelf speakers", - < 92 => "Car audio, portable speakers", - < 97 => "Headphones, efficient speakers", - _ => "Horn-loaded, compression drivers" - }; -} diff --git a/Semantics.Quantities/Acoustic/Sharpness.cs b/Semantics.Quantities/Acoustic/Sharpness.cs deleted file mode 100644 index e7119c8..0000000 --- a/Semantics.Quantities/Acoustic/Sharpness.cs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a sharpness quantity with compile-time dimensional safety. -/// Sharpness is a perceptual measure of the high-frequency content of sound, measured in acums. -/// -public sealed record Sharpness : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of sharpness [1]. - public override PhysicalDimension Dimension => PhysicalDimensions.Sharpness; - - /// - /// Initializes a new instance of the Sharpness class. - /// - public Sharpness() : base() { } - - /// - /// Creates a new Sharpness from a value in acums. - /// - /// The sharpness in acums. - /// A new Sharpness instance. - public static Sharpness FromAcums(T acums) => Create(acums); - - /// - /// Gets sharpness as a multiple of the reference (1 acum = narrow band noise at 1 kHz and 60 dB). - /// - /// The sharpness ratio. - public T ToSharpnessRatio() => Value; - - /// - /// Gets the sharpness category based on acum value. - /// - /// A string describing the sharpness level. - public string GetSharpnessCategory() => double.CreateChecked(Value) switch - { - < 0.5 => "Very Dull", - < 1.0 => "Dull", - < 1.5 => "Moderate", - < 2.5 => "Sharp", - < 4.0 => "Very Sharp", - _ => "Extremely Sharp" - }; - - /// - /// Estimates perceived sound quality based on sharpness. - /// - /// A string describing the perceived quality. - public string GetPerceivedQuality() => double.CreateChecked(Value) switch - { - < 0.8 => "Warm and Pleasant", - < 1.2 => "Balanced", - < 2.0 => "Bright", - < 3.0 => "Harsh", - _ => "Piercing" - }; - - /// - /// Combines sharpness values (simplified linear model). - /// Note: Actual sharpness combination is complex and frequency-dependent. - /// - /// Another sharpness value. - /// The combined sharpness. - public Sharpness CombineWith(Sharpness other) - { - Ensure.NotNull(other); - - // Simplified linear combination - // Real sharpness calculation requires detailed spectral analysis - T combined = Value + other.Value; - return FromAcums(combined); - } - - /// - /// Estimates the dominant frequency content based on sharpness. - /// This is a rough approximation. - /// - /// A string describing the frequency content. - public string GetFrequencyContent() => double.CreateChecked(Value) switch - { - < 0.5 => "Low frequency dominant", - < 1.0 => "Mid frequency dominant", - < 2.0 => "Upper mid frequency dominant", - < 3.0 => "High frequency dominant", - _ => "Very high frequency dominant" - }; -} diff --git a/Semantics.Quantities/Acoustic/SoundAbsorption.cs b/Semantics.Quantities/Acoustic/SoundAbsorption.cs deleted file mode 100644 index acae0dc..0000000 --- a/Semantics.Quantities/Acoustic/SoundAbsorption.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a sound absorption coefficient quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record SoundAbsorption : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of soundabsorption [dimensionless]. - public override PhysicalDimension Dimension => PhysicalDimensions.SoundAbsorption; - - /// - /// Initializes a new instance of the class. - /// - public SoundAbsorption() : base() { } - - /// - /// Creates a new SoundAbsorption from a dimensionless coefficient (0-1). - /// - /// The absorption coefficient (0-1). - /// A new SoundAbsorption instance. - public static SoundAbsorption FromCoefficient(T coefficient) => Create(coefficient); - - /// - /// Creates a new SoundAbsorption from a percentage value. - /// - /// The absorption percentage (0-100). - /// A new SoundAbsorption instance. - public static SoundAbsorption FromPercentage(T percentage) => Create(percentage / T.CreateChecked(100)); - - /// - /// Calculates the reflection coefficient (1 - absorption). - /// - /// The reflection coefficient. - public T ReflectionCoefficient() => T.CreateChecked(1) - Value; - - /// - /// Converts to percentage. - /// - /// The absorption as a percentage. - public T ToPercentage() => Value * T.CreateChecked(100); -} diff --git a/Semantics.Quantities/Acoustic/SoundIntensity.cs b/Semantics.Quantities/Acoustic/SoundIntensity.cs deleted file mode 100644 index f7cc423..0000000 --- a/Semantics.Quantities/Acoustic/SoundIntensity.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a sound intensity quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record SoundIntensity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of soundintensity [M T⁻³]. - public override PhysicalDimension Dimension => PhysicalDimensions.SoundIntensity; - - /// - /// Initializes a new instance of the class. - /// - public SoundIntensity() : base() { } - - /// - /// Creates a new SoundIntensity from a value in watts per square meter. - /// - /// The value in watts per square meter. - /// A new SoundIntensity instance. - public static SoundIntensity FromWattsPerSquareMeter(T wattsPerSquareMeter) => Create(wattsPerSquareMeter); - - /// - /// Creates a new SoundIntensity from a value in microwatts per square centimeter. - /// - /// The value in microwatts per square centimeter. - /// A new SoundIntensity instance. - public static SoundIntensity FromMicrowattsPerSquareCentimeter(T microwattsPerSquareCentimeter) => - Create(microwattsPerSquareCentimeter / T.CreateChecked(100)); - - /// - /// Divides sound power by area to create sound intensity. - /// - /// The sound power. - /// The area. - /// The resulting sound intensity. - public static SoundIntensity Divide(SoundPower soundPower, Area area) - { - Ensure.NotNull(soundPower); - Ensure.NotNull(area); - return Create(soundPower.Value / area.Value); - } - - /// - /// Calculates sound power from sound intensity and area (P = I×A). - /// - /// The sound intensity. - /// The area. - /// The resulting sound power. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static SoundPower operator *(SoundIntensity intensity, Area area) - { - Ensure.NotNull(intensity); - Ensure.NotNull(area); - - T powerValue = intensity.Value * area.Value; - - return SoundPower.Create(powerValue); - } -} diff --git a/Semantics.Quantities/Acoustic/SoundIntensityLevel.cs b/Semantics.Quantities/Acoustic/SoundIntensityLevel.cs deleted file mode 100644 index 14cd279..0000000 --- a/Semantics.Quantities/Acoustic/SoundIntensityLevel.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a sound intensity level quantity with compile-time dimensional safety. -/// Sound intensity level (IL) is a logarithmic measure of sound intensity relative to 10⁻¹² W/m². -/// -public sealed record SoundIntensityLevel : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of sound intensity level [1]. - public override PhysicalDimension Dimension => PhysicalDimensions.SoundIntensityLevel; - - /// - /// Initializes a new instance of the SoundIntensityLevel class. - /// - public SoundIntensityLevel() : base() { } - - /// - /// Creates a new SoundIntensityLevel from a value in decibels IL. - /// - /// The sound intensity level in dB IL. - /// A new SoundIntensityLevel instance. - public static SoundIntensityLevel FromDecibels(T decibels) => Create(decibels); - - /// - /// Creates a SoundIntensityLevel from a SoundIntensity value using IL formula. - /// IL = 10 * log₁₀(I / I₀) where I₀ = 10⁻¹² W/m² - /// - /// The sound intensity. - /// The corresponding sound intensity level in dB IL. - public static SoundIntensityLevel FromSoundIntensity(SoundIntensity soundIntensity) - { - Ensure.NotNull(soundIntensity); - - // Reference intensity: 10⁻¹² W/m² - T referenceIntensity = T.CreateChecked(1e-12); - T ratio = soundIntensity.Value / referenceIntensity; - T decibels = T.CreateChecked(10.0 * Math.Log10(double.CreateChecked(ratio))); - return Create(decibels); - } - - /// - /// Converts this SoundIntensityLevel back to SoundIntensity. - /// I = I₀ * 10^(IL/10) - /// - /// The corresponding sound intensity. - public SoundIntensity ToSoundIntensity() - { - T referenceIntensity = T.CreateChecked(1e-12); // 10⁻¹² W/m² - T exponent = Value / T.CreateChecked(10.0); - T intensity = referenceIntensity * T.CreateChecked(Math.Pow(10.0, double.CreateChecked(exponent))); - return SoundIntensity.Create(intensity); - } - - /// - /// Calculates the combined intensity level of multiple sources. - /// IL_total = 10 * log₁₀(10^(IL₁/10) + 10^(IL₂/10) + ...) - /// - /// Another sound intensity level. - /// The combined sound intensity level. - public SoundIntensityLevel CombineWith(SoundIntensityLevel other) - { - Ensure.NotNull(other); - - T exp1 = T.CreateChecked(Math.Pow(10.0, double.CreateChecked(Value / T.CreateChecked(10.0)))); - T exp2 = T.CreateChecked(Math.Pow(10.0, double.CreateChecked(other.Value / T.CreateChecked(10.0)))); - T combined = T.CreateChecked(10.0 * Math.Log10(double.CreateChecked(exp1 + exp2))); - return Create(combined); - } -} diff --git a/Semantics.Quantities/Acoustic/SoundPower.cs b/Semantics.Quantities/Acoustic/SoundPower.cs deleted file mode 100644 index 2795b2c..0000000 --- a/Semantics.Quantities/Acoustic/SoundPower.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a sound power quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record SoundPower : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of soundpower [M L² T⁻³]. - public override PhysicalDimension Dimension => PhysicalDimensions.SoundPower; - - /// - /// Initializes a new instance of the class. - /// - public SoundPower() : base() { } - - /// - /// Creates a new SoundPower from a value in watts. - /// - /// The value in watts. - /// A new SoundPower instance. - public static SoundPower FromWatts(T watts) => Create(watts); - - /// - /// Creates a new SoundPower from a value in milliwatts. - /// - /// The value in milliwatts. - /// A new SoundPower instance. - public static SoundPower FromMilliwatts(T milliwatts) => Create(milliwatts / T.CreateChecked(1000)); - - /// - /// Creates a new SoundPower from a value in acoustic watts. - /// - /// The value in acoustic watts. - /// A new SoundPower instance. - public static SoundPower FromAcousticWatts(T acousticWatts) => Create(acousticWatts); - - /// - /// Multiplies sound intensity by area to create sound power. - /// - /// The sound intensity. - /// The area. - /// The resulting sound power. - public static SoundPower Multiply(SoundIntensity soundIntensity, Area area) - { - Ensure.NotNull(soundIntensity); - Ensure.NotNull(area); - return Create(soundIntensity.Value * area.Value); - } - - /// - /// Calculates the sound power from sound intensity and area. - /// - /// The sound intensity in watts per square meter. - /// The area in square meters. - /// The resulting sound power. - public static SoundPower FromSoundIntensityAndArea(SoundIntensity soundIntensity, Area area) - { - Ensure.NotNull(soundIntensity); - Ensure.NotNull(area); - - return Create(soundIntensity.Value * area.Value); - } - - /// - /// Calculates sound intensity from sound power and area (I = P/A). - /// - /// The sound power. - /// The area. - /// The resulting sound intensity. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static SoundIntensity operator /(SoundPower power, Area area) - { - Ensure.NotNull(power); - Ensure.NotNull(area); - - T intensityValue = power.Value / area.Value; - - return SoundIntensity.Create(intensityValue); - } -} diff --git a/Semantics.Quantities/Acoustic/SoundPowerLevel.cs b/Semantics.Quantities/Acoustic/SoundPowerLevel.cs deleted file mode 100644 index 33d296b..0000000 --- a/Semantics.Quantities/Acoustic/SoundPowerLevel.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a sound power level quantity with compile-time dimensional safety. -/// Sound power level (PWL) is a logarithmic measure of sound power relative to 10⁻¹² W. -/// -public sealed record SoundPowerLevel : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of sound power level [1]. - public override PhysicalDimension Dimension => PhysicalDimensions.SoundPowerLevel; - - /// - /// Initializes a new instance of the SoundPowerLevel class. - /// - public SoundPowerLevel() : base() { } - - /// - /// Creates a new SoundPowerLevel from a value in decibels PWL. - /// - /// The sound power level in dB PWL. - /// A new SoundPowerLevel instance. - public static SoundPowerLevel FromDecibels(T decibels) => Create(decibels); - - /// - /// Creates a SoundPowerLevel from a SoundPower value using PWL formula. - /// PWL = 10 * log₁₀(W / W₀) where W₀ = 10⁻¹² W - /// - /// The sound power. - /// The corresponding sound power level in dB PWL. - public static SoundPowerLevel FromSoundPower(SoundPower soundPower) - { - Ensure.NotNull(soundPower); - - // Reference power: 10⁻¹² W - T referencePower = T.CreateChecked(1e-12); - T ratio = soundPower.Value / referencePower; - T decibels = T.CreateChecked(10.0 * Math.Log10(double.CreateChecked(ratio))); - return Create(decibels); - } - - /// - /// Converts this SoundPowerLevel back to SoundPower. - /// W = W₀ * 10^(PWL/10) - /// - /// The corresponding sound power. - public SoundPower ToSoundPower() - { - T referencePower = T.CreateChecked(1e-12); // 10⁻¹² W - T exponent = Value / T.CreateChecked(10.0); - T power = referencePower * T.CreateChecked(Math.Pow(10.0, double.CreateChecked(exponent))); - return SoundPower.Create(power); - } - - /// - /// Calculates the combined power level of multiple sound sources. - /// PWL_total = 10 * log₁₀(10^(PWL₁/10) + 10^(PWL₂/10) + ...) - /// - /// Another sound power level. - /// The combined sound power level. - public SoundPowerLevel CombineWith(SoundPowerLevel other) - { - Ensure.NotNull(other); - - T exp1 = T.CreateChecked(Math.Pow(10.0, double.CreateChecked(Value / T.CreateChecked(10.0)))); - T exp2 = T.CreateChecked(Math.Pow(10.0, double.CreateChecked(other.Value / T.CreateChecked(10.0)))); - T combined = T.CreateChecked(10.0 * Math.Log10(double.CreateChecked(exp1 + exp2))); - return Create(combined); - } - - /// - /// Calculates directivity from sound power level and sound pressure level. - /// D = PWL - SPL + 10*log₁₀(4πr²) - /// - /// The measured sound pressure level. - /// The measurement distance. - /// The directivity factor in dB. - public T CalculateDirectivity(SoundPressureLevel soundPressureLevel, Length distance) - { - Ensure.NotNull(soundPressureLevel); - Ensure.NotNull(distance); - - T sphericalSpreading = T.CreateChecked(10.0 * Math.Log10(4.0 * Math.PI * double.CreateChecked(distance.Value * distance.Value))); - return Value - soundPressureLevel.Value + sphericalSpreading; - } -} diff --git a/Semantics.Quantities/Acoustic/SoundPressure.cs b/Semantics.Quantities/Acoustic/SoundPressure.cs deleted file mode 100644 index e590528..0000000 --- a/Semantics.Quantities/Acoustic/SoundPressure.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a sound pressure quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record SoundPressure : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of soundpressure [M L⁻¹ T⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.SoundPressure; - - /// - /// Initializes a new instance of the class. - /// - public SoundPressure() : base() { } - - /// - /// Creates a new SoundPressure from a value in pascals. - /// - /// The value in pascals. - /// A new SoundPressure instance. - public static SoundPressure FromPascals(T pascals) => Create(pascals); - - /// - /// Creates a new SoundPressure from a value in micropascals. - /// - /// The value in micropascals. - /// A new SoundPressure instance. - public static SoundPressure FromMicropascals(T micropascals) => Create(micropascals / T.CreateChecked(1_000_000)); - - /// - /// Creates a new SoundPressure from a value in bars. - /// - /// The value in bars. - /// A new SoundPressure instance. - public static SoundPressure FromBars(T bars) => Create(bars * T.CreateChecked(100_000)); - - /// - /// Squares sound pressure for intensity calculations. - /// - /// The squared sound pressure. - public T Squared() => Value * Value; -} diff --git a/Semantics.Quantities/Acoustic/SoundPressureLevel.cs b/Semantics.Quantities/Acoustic/SoundPressureLevel.cs deleted file mode 100644 index 6d4bfb0..0000000 --- a/Semantics.Quantities/Acoustic/SoundPressureLevel.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a sound pressure level quantity with compile-time dimensional safety. -/// Sound pressure level (SPL) is a logarithmic measure of sound pressure relative to 20 μPa. -/// -public sealed record SoundPressureLevel : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of sound pressure level [1]. - public override PhysicalDimension Dimension => PhysicalDimensions.SoundPressureLevel; - - /// - /// Initializes a new instance of the SoundPressureLevel class. - /// - public SoundPressureLevel() : base() { } - - /// - /// Creates a new SoundPressureLevel from a value in decibels SPL. - /// - /// The sound pressure level in dB SPL. - /// A new SoundPressureLevel instance. - public static SoundPressureLevel FromDecibels(T decibels) => Create(decibels); - - /// - /// Creates a SoundPressureLevel from a SoundPressure value using SPL formula. - /// SPL = 20 * log₁₀(p / p₀) where p₀ = 20 μPa - /// - /// The sound pressure. - /// The corresponding sound pressure level in dB SPL. - public static SoundPressureLevel FromSoundPressure(SoundPressure soundPressure) - { - Ensure.NotNull(soundPressure); - T referencePressure = PhysicalConstants.Generic.ReferenceSoundPressure(); - T ratio = soundPressure.Value / referencePressure; - T decibels = T.CreateChecked(20.0 * Math.Log10(double.CreateChecked(ratio))); - return FromDecibels(decibels); - } - - /// - /// Converts this SoundPressureLevel back to SoundPressure. - /// p = p₀ * 10^(SPL/20) - /// - /// The corresponding sound pressure. - public SoundPressure ToSoundPressure() - { - T referencePressure = PhysicalConstants.Generic.ReferenceSoundPressure(); - T exponent = Value / T.CreateChecked(20.0); - T pressure = referencePressure * T.CreateChecked(Math.Pow(10.0, double.CreateChecked(exponent))); - return SoundPressure.Create(pressure); - } - - /// - /// Gets the A-weighted sound pressure level (common for human hearing assessment). - /// - /// The A-weighted SPL (approximation). - public SoundPressureLevel AWeighted() => FromDecibels(Value); // Simplified - actual A-weighting is frequency-dependent - - /// - /// Calculates the equivalent sound level for intermittent sounds. - /// - /// Duration of measurement. - /// Total time period. - /// The equivalent continuous sound level. - public SoundPressureLevel EquivalentLevel(Time duration, Time totalTime) - { - Ensure.NotNull(duration); - Ensure.NotNull(totalTime); - T ratio = duration.Value / totalTime.Value; - T correction = T.CreateChecked(10.0 * Math.Log10(double.CreateChecked(ratio))); - return FromDecibels(Value + correction); - } -} diff --git a/Semantics.Quantities/Acoustic/SoundSpeed.cs b/Semantics.Quantities/Acoustic/SoundSpeed.cs deleted file mode 100644 index 018a482..0000000 --- a/Semantics.Quantities/Acoustic/SoundSpeed.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a sound speed quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record SoundSpeed : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of soundspeed [L T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.SoundSpeed; - - /// - /// Initializes a new instance of the class. - /// - public SoundSpeed() : base() { } - - /// - /// Creates a new SoundSpeed from a value in meters per second. - /// - /// The value in meters per second. - /// A new SoundSpeed instance. - public static SoundSpeed FromMetersPerSecond(T metersPerSecond) => Create(metersPerSecond); - - /// - /// Creates a new SoundSpeed from a value in feet per second. - /// - /// The value in feet per second. - /// A new SoundSpeed instance. - public static SoundSpeed FromFeetPerSecond(T feetPerSecond) => Create(feetPerSecond * PhysicalConstants.Generic.FeetToMeters()); - - /// - /// Multiplies wavelength by frequency to create sound speed. - /// - /// The wavelength. - /// The frequency. - /// The resulting sound speed. - public static SoundSpeed Multiply(Wavelength wavelength, Frequency frequency) - { - Ensure.NotNull(wavelength); - Ensure.NotNull(frequency); - return Create(wavelength.Value * frequency.Value); - } - - /// - /// Converts SoundSpeed to Velocity. - /// - /// The equivalent velocity. - public Velocity ToVelocity() => Velocity.Create(Value); -} diff --git a/Semantics.Quantities/Acoustic/SoundTransmissionClass.cs b/Semantics.Quantities/Acoustic/SoundTransmissionClass.cs deleted file mode 100644 index b8c57b7..0000000 --- a/Semantics.Quantities/Acoustic/SoundTransmissionClass.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a sound transmission class quantity with compile-time dimensional safety. -/// STC is a rating of how well a building partition blocks airborne sound. -/// -public sealed record SoundTransmissionClass : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of sound transmission class [1]. - public override PhysicalDimension Dimension => PhysicalDimensions.SoundTransmissionClass; - - /// - /// Initializes a new instance of the SoundTransmissionClass class. - /// - public SoundTransmissionClass() : base() { } - - /// - /// Creates a new SoundTransmissionClass from a rating value. - /// - /// The STC rating (typically 15-65). - /// A new SoundTransmissionClass instance. - public static SoundTransmissionClass FromRating(T rating) => Create(rating); - - /// - /// Gets the acoustic performance description based on STC rating. - /// - /// A string describing the acoustic performance. - public string GetPerformanceDescription() => double.CreateChecked(Value) switch - { - < 25 => "Poor - Speech clearly audible", - < 30 => "Fair - Normal speech audible", - < 35 => "Good - Loud speech audible", - < 40 => "Very Good - Shouting barely audible", - < 45 => "Excellent - Very loud sounds faintly audible", - < 50 => "Superior - Loud sounds barely audible", - < 55 => "Outstanding - Very loud sounds faintly audible", - _ => "Exceptional - Minimal sound transmission" - }; - - /// - /// Gets the typical application based on STC rating. - /// - /// A string describing typical applications. - public string GetTypicalApplication() => double.CreateChecked(Value) switch - { - < 30 => "Interior partitions in open offices", - < 35 => "Standard interior walls between rooms", - < 40 => "Walls between bedrooms and living areas", - < 45 => "Walls between apartments or hotel rooms", - < 50 => "Walls between noisy and quiet areas", - < 55 => "Recording studios, conference rooms", - _ => "Specialized acoustic environments" - }; - - /// - /// Estimates the transmission loss at a specific frequency. - /// This is a simplified approximation based on the STC contour. - /// - /// The frequency in Hz. - /// The estimated transmission loss in dB. - public T EstimateTransmissionLoss(Frequency frequency) - { - Ensure.NotNull(frequency); - - // Simplified STC contour approximation - // Real STC calculation requires detailed frequency analysis - double freq = double.CreateChecked(frequency.Value); - double stc = double.CreateChecked(Value); - - double transmissionLoss = freq switch - { - < 200 => stc - 15, // Low frequency penalty - < 500 => stc - 5, // Mid-low frequency - < 1000 => stc, // Reference frequency range - < 2000 => stc + 3, // Mid-high frequency - _ => stc + 5 // High frequency bonus - }; - - return T.CreateChecked(Math.Max(0, transmissionLoss)); - } - - /// - /// Converts STC to approximate noise reduction in dB. - /// - /// The approximate noise reduction. - public T ToNoiseReduction() => Value; - - /// - /// Determines if the STC rating meets building code requirements. - /// - /// Type of building ("residential", "commercial", "industrial"). - /// True if the rating meets typical requirements. - public bool MeetsBuildingCode(string buildingType) => buildingType?.ToLower() switch - { - "residential" => double.CreateChecked(Value) >= 50, - "commercial" => double.CreateChecked(Value) >= 45, - "industrial" => double.CreateChecked(Value) >= 40, - _ => false - }; -} diff --git a/Semantics.Quantities/Acoustic/Wavelength.cs b/Semantics.Quantities/Acoustic/Wavelength.cs deleted file mode 100644 index de7a16b..0000000 --- a/Semantics.Quantities/Acoustic/Wavelength.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a wavelength quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Wavelength : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of wavelength [L]. - public override PhysicalDimension Dimension => PhysicalDimensions.Wavelength; - - /// - /// Initializes a new instance of the class. - /// - public Wavelength() : base() { } - - /// - /// Creates a new Wavelength from a value in meters. - /// - /// The value in meters. - /// A new Wavelength instance. - public static Wavelength FromMeters(T meters) => Create(meters); - - /// - /// Creates a new Wavelength from a value in millimeters. - /// - /// The value in millimeters. - /// A new Wavelength instance. - public static Wavelength FromMillimeters(T millimeters) => Create(millimeters / T.CreateChecked(1000)); - - /// - /// Creates a new Wavelength from a value in micrometers. - /// - /// The value in micrometers. - /// A new Wavelength instance. - public static Wavelength FromMicrometers(T micrometers) => Create(micrometers / T.CreateChecked(1_000_000)); - - /// - /// Divides speed by frequency to create wavelength. - /// - /// The wave speed. - /// The frequency. - /// The resulting wavelength. - public static Wavelength Divide(Velocity speed, Frequency frequency) - { - Ensure.NotNull(speed); - Ensure.NotNull(frequency); - return Create(speed.Value / frequency.Value); - } - - /// - /// Calculates the wave speed from wavelength and frequency (v = f × λ). - /// - /// The wavelength. - /// The frequency. - /// The resulting wave speed. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static SoundSpeed operator *(Wavelength wavelength, Frequency frequency) - { - Ensure.NotNull(wavelength); - Ensure.NotNull(frequency); - - T speedValue = wavelength.Value * frequency.Value; - - return SoundSpeed.Create(speedValue); - } - - /// - /// Calculates the frequency from wavelength and wave speed (f = v/λ). - /// - /// The wave speed. - /// The wavelength. - /// The resulting frequency. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Frequency operator /(SoundSpeed speed, Wavelength wavelength) - { - Ensure.NotNull(speed); - Ensure.NotNull(wavelength); - - T frequencyValue = speed.Value / wavelength.Value; - - return Frequency.Create(frequencyValue); - } -} diff --git a/Semantics.Quantities/Acoustics/DirectionalityIndex.cs b/Semantics.Quantities/Acoustics/DirectionalityIndex.cs new file mode 100644 index 0000000..b42f5fe --- /dev/null +++ b/Semantics.Quantities/Acoustics/DirectionalityIndex.cs @@ -0,0 +1,18 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Bespoke members of ; the logarithmic core +/// is generated from logarithmic.json. +/// +public readonly partial record struct DirectionalityIndex + where T : struct, INumber +{ + /// Gets the index of an omnidirectional source (0 dB). + public static DirectionalityIndex Omnidirectional => new(T.Zero); +} diff --git a/Semantics.Quantities/AudioEngineering/Cents.cs b/Semantics.Quantities/AudioEngineering/Cents.cs index a53a905..3eac5c8 100644 --- a/Semantics.Quantities/AudioEngineering/Cents.cs +++ b/Semantics.Quantities/AudioEngineering/Cents.cs @@ -2,121 +2,30 @@ // All rights reserved. // Licensed under the MIT license. -namespace ktsu.Semantics; +namespace ktsu.Semantics.Quantities; -using System.Globalization; using System.Numerics; /// -/// Represents a fine musical pitch interval measured in cents. +/// Bespoke members of ; the logarithmic core (including the +/// frequency- conversions) is generated from logarithmic.json. /// -/// -/// One cent is one hundredth of a semitone, so 1200 cents make one -/// octave. Cents are the conventional unit for fine detune and tuning parameters. -/// -/// The floating-point storage type. -/// The interval in cents. -public readonly record struct Cents(T Value) : IComparable> +public readonly partial record struct Cents where T : struct, INumber { - /// Gets a unison interval (zero cents). + /// Gets the interval of zero cents (unison). public static Cents Unison => new(T.Zero); /// - /// Creates an interval from a raw cents value. - /// - /// The interval in cents. - /// A new . - public static Cents Create(T value) => new(value); - - /// - /// Creates an interval from semitones (1 semitone → 100 cents). + /// Creates an interval from semitones (one semitone is 100 cents). /// /// The interval in semitones. /// A new . public static Cents FromSemitones(Semitones semitones) => semitones.ToCents(); /// - /// Creates an interval from a frequency ratio using cents = 1200·log2(ratio). - /// - /// The frequency ratio. - /// A new . - public static Cents FromFrequencyRatio(Ratio ratio) - { - double r = double.CreateChecked(ratio.Value); - double cents = 1200.0 * Math.Log2(r); - return new(T.CreateChecked(cents)); - } - - /// - /// Converts this interval to semitones (100 cents → 1 semitone). + /// Converts this interval to semitones (100 cents is one semitone). /// /// The equivalent . public Semitones ToSemitones() => new(Value / T.CreateChecked(100)); - - /// - /// Converts this interval to a frequency ratio using ratio = 2^(cents/1200). - /// - /// The equivalent frequency . - public Ratio ToFrequencyRatio() - { - double cents = double.CreateChecked(Value); - double ratio = Math.Pow(2.0, cents / 1200.0); - return new(T.CreateChecked(ratio)); - } - - /// Adds two intervals. - /// The first interval. - /// The second interval. - /// The combined interval. - public static Cents operator +(Cents left, Cents right) => new(left.Value + right.Value); - - /// Subtracts one interval from another. - /// The interval to subtract from. - /// The interval to subtract. - /// The difference interval. - public static Cents operator -(Cents left, Cents right) => new(left.Value - right.Value); - - /// Adds two intervals (friendly alternate for operator +). - /// The first interval. - /// The second interval. - /// The combined interval. - public static Cents Add(Cents left, Cents right) => left + right; - - /// Subtracts one interval from another (friendly alternate for operator -). - /// The interval to subtract from. - /// The interval to subtract. - /// The difference interval. - public static Cents Subtract(Cents left, Cents right) => left - right; - - /// - public int CompareTo(Cents other) => Value.CompareTo(other.Value); - - /// Determines whether one interval is less than another. - /// The left interval. - /// The right interval. - /// if is less than . - public static bool operator <(Cents left, Cents right) => left.CompareTo(right) < 0; - - /// Determines whether one interval is greater than another. - /// The left interval. - /// The right interval. - /// if is greater than . - public static bool operator >(Cents left, Cents right) => left.CompareTo(right) > 0; - - /// Determines whether one interval is less than or equal to another. - /// The left interval. - /// The right interval. - /// if is less than or equal to . - public static bool operator <=(Cents left, Cents right) => left.CompareTo(right) <= 0; - - /// Determines whether one interval is greater than or equal to another. - /// The left interval. - /// The right interval. - /// if is greater than or equal to . - public static bool operator >=(Cents left, Cents right) => left.CompareTo(right) >= 0; - - /// Returns a culture-invariant string representation of this interval. - /// The interval formatted with a ct suffix. - public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"{Value} ct"); } diff --git a/Semantics.Quantities/AudioEngineering/Decibels.cs b/Semantics.Quantities/AudioEngineering/Decibels.cs index 4217d44..4505655 100644 --- a/Semantics.Quantities/AudioEngineering/Decibels.cs +++ b/Semantics.Quantities/AudioEngineering/Decibels.cs @@ -2,143 +2,40 @@ // All rights reserved. // Licensed under the MIT license. -namespace ktsu.Semantics; +namespace ktsu.Semantics.Quantities; -using System.Globalization; using System.Numerics; /// -/// Represents a logarithmic level in decibels (dB). +/// Bespoke members of ; the logarithmic core (including the +/// and power- conversions) is generated +/// from logarithmic.json. /// -/// -/// Decibels express ratios on a logarithmic scale. Two conventions are supported: -/// -/// Amplitude / field quantities (voltage, sample value): dB = 20·log10(ratio). -/// Power quantities (energy, intensity): dB = 10·log10(ratio). -/// -/// Use / for gains applied to samples, and -/// / for power ratios. A level of 0 dB is unity. -/// -/// The floating-point storage type. -/// The level in decibels. -public readonly record struct Decibels(T Value) : IComparable> +public readonly partial record struct Decibels where T : struct, INumber { /// Gets a level of zero decibels (unity). public static Decibels Unity => new(T.Zero); /// - /// Creates a level from a raw decibel value. - /// - /// The level in decibels. - /// A new . - public static Decibels Create(T value) => new(value); - - /// - /// Creates a level from a linear amplitude ratio using dB = 20·log10(ratio). + /// Creates a level from a raw linear amplitude ratio using dB = 20·log10(ratio). /// /// The linear amplitude ratio. /// A new . An amplitude of zero maps to negative infinity. public static Decibels FromAmplitude(T amplitude) { double linear = double.CreateChecked(amplitude); - double db = 20.0 * Math.Log10(linear); - return new(T.CreateChecked(db)); + return new(T.CreateChecked(20.0 * Math.Log10(linear))); } /// - /// Creates a level from a linear power ratio using dB = 10·log10(ratio). + /// Creates a level from a raw linear power ratio using dB = 10·log10(ratio). /// /// The linear power ratio. /// A new . A power of zero maps to negative infinity. public static Decibels FromPower(T power) { double linear = double.CreateChecked(power); - double db = 10.0 * Math.Log10(linear); - return new(T.CreateChecked(db)); - } - - /// - /// Creates a level from an amplitude . - /// - /// The linear amplitude gain. - /// A new . - public static Decibels FromGain(Gain gain) => FromAmplitude(gain.Value); - - /// - /// Converts this level to a linear amplitude gain using gain = 10^(dB/20). - /// - /// The equivalent amplitude . - public Gain ToAmplitude() - { - double db = double.CreateChecked(Value); - double linear = Math.Pow(10.0, db / 20.0); - return new(T.CreateChecked(linear)); - } - - /// - /// Converts this level to a linear power ratio using ratio = 10^(dB/10). - /// - /// The equivalent power . - public Ratio ToPower() - { - double db = double.CreateChecked(Value); - double linear = Math.Pow(10.0, db / 10.0); - return new(T.CreateChecked(linear)); + return new(T.CreateChecked(10.0 * Math.Log10(linear))); } - - /// Adds two decibel levels (cascading two stages multiplies their linear gains). - /// The first level. - /// The second level. - /// The summed level. - public static Decibels operator +(Decibels left, Decibels right) => new(left.Value + right.Value); - - /// Subtracts one decibel level from another. - /// The level to subtract from. - /// The level to subtract. - /// The difference level. - public static Decibels operator -(Decibels left, Decibels right) => new(left.Value - right.Value); - - /// Adds two decibel levels (friendly alternate for operator +). - /// The first level. - /// The second level. - /// The summed level. - public static Decibels Add(Decibels left, Decibels right) => left + right; - - /// Subtracts one decibel level from another (friendly alternate for operator -). - /// The level to subtract from. - /// The level to subtract. - /// The difference level. - public static Decibels Subtract(Decibels left, Decibels right) => left - right; - - /// - public int CompareTo(Decibels other) => Value.CompareTo(other.Value); - - /// Determines whether one level is less than another. - /// The left level. - /// The right level. - /// if is less than . - public static bool operator <(Decibels left, Decibels right) => left.CompareTo(right) < 0; - - /// Determines whether one level is greater than another. - /// The left level. - /// The right level. - /// if is greater than . - public static bool operator >(Decibels left, Decibels right) => left.CompareTo(right) > 0; - - /// Determines whether one level is less than or equal to another. - /// The left level. - /// The right level. - /// if is less than or equal to . - public static bool operator <=(Decibels left, Decibels right) => left.CompareTo(right) <= 0; - - /// Determines whether one level is greater than or equal to another. - /// The left level. - /// The right level. - /// if is greater than or equal to . - public static bool operator >=(Decibels left, Decibels right) => left.CompareTo(right) >= 0; - - /// Returns a culture-invariant string representation of this level. - /// The level formatted with a dB suffix. - public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"{Value} dB"); } diff --git a/Semantics.Quantities/AudioEngineering/Gain.cs b/Semantics.Quantities/AudioEngineering/Gain.cs index a09b7d2..bb9dab5 100644 --- a/Semantics.Quantities/AudioEngineering/Gain.cs +++ b/Semantics.Quantities/AudioEngineering/Gain.cs @@ -2,37 +2,29 @@ // All rights reserved. // Licensed under the MIT license. -namespace ktsu.Semantics; +namespace ktsu.Semantics.Quantities; -using System.Globalization; using System.Numerics; /// -/// Represents a linear amplitude gain factor. +/// Bespoke members of ; the quantity itself is generated from +/// dimensions.json as a semantic overload of the Dimensionless . /// /// -/// A gain of 1 is unity (no change), 0 is silence, and 2 doubles the amplitude -/// (≈ +6.02 dB). Gain is the value you multiply a sample by on the audio path; -/// is its logarithmic counterpart for display and user input. Conversions use the amplitude (field) -/// convention dB = 20·log10(gain). +/// A gain of 1 is unity (no change), 0 is silence, and 2 doubles the +/// amplitude (≈ +6.02 dB). Gain is the value you multiply a sample by on the audio path; +/// is its logarithmic counterpart for display and user input, +/// using the amplitude (field) convention dB = 20·log10(gain). As a Vector0 +/// magnitude, gain is non-negative — polarity inversion is a separate concern. /// -/// The floating-point storage type. -/// The linear gain factor. -public readonly record struct Gain(T Value) : IComparable> +public partial record Gain where T : struct, INumber { /// Gets unity gain (a factor of one). - public static Gain Unity => new(T.One); + public static Gain Unity => Create(T.One); /// Gets silence (a factor of zero). - public static Gain Silence => new(T.Zero); - - /// - /// Creates a gain from a raw linear factor. - /// - /// The linear gain factor (1 = unity). - /// A new . - public static Gain Create(T value) => new(value); + public static Gain Silence => Create(T.Zero); /// /// Creates a gain from a level in decibels using the amplitude convention gain = 10^(dB/20). @@ -45,48 +37,25 @@ public readonly record struct Gain(T Value) : IComparable> /// Converts this gain to a level in decibels using the amplitude convention dB = 20·log10(gain). /// /// The equivalent . Silence maps to negative infinity. - public Decibels ToDecibels() => Decibels.FromAmplitude(Value); + public Decibels ToDecibels() => Decibels.FromGain(this); /// Multiplies two gains (cascading two stages). /// The first gain. /// The second gain. /// The combined gain. - public static Gain operator *(Gain left, Gain right) => new(left.Value * right.Value); + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Usage", "CA2225:Operator overloads have named alternates", + Justification = "Multiply is provided as the named alternate.")] + public static Gain operator *(Gain left, Gain right) + { + ArgumentNullException.ThrowIfNull(left); + ArgumentNullException.ThrowIfNull(right); + return Create(left.Value * right.Value); + } /// Multiplies two gains (friendly alternate for operator *). /// The first gain. /// The second gain. /// The combined gain. public static Gain Multiply(Gain left, Gain right) => left * right; - - /// - public int CompareTo(Gain other) => Value.CompareTo(other.Value); - - /// Determines whether one gain is less than another. - /// The left gain. - /// The right gain. - /// if is less than . - public static bool operator <(Gain left, Gain right) => left.CompareTo(right) < 0; - - /// Determines whether one gain is greater than another. - /// The left gain. - /// The right gain. - /// if is greater than . - public static bool operator >(Gain left, Gain right) => left.CompareTo(right) > 0; - - /// Determines whether one gain is less than or equal to another. - /// The left gain. - /// The right gain. - /// if is less than or equal to . - public static bool operator <=(Gain left, Gain right) => left.CompareTo(right) <= 0; - - /// Determines whether one gain is greater than or equal to another. - /// The left gain. - /// The right gain. - /// if is greater than or equal to . - public static bool operator >=(Gain left, Gain right) => left.CompareTo(right) >= 0; - - /// Returns a culture-invariant string representation of this gain. - /// The gain formatted with an x suffix. - public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"{Value}x"); } diff --git a/Semantics.Quantities/AudioEngineering/NormalizedParameter.cs b/Semantics.Quantities/AudioEngineering/NormalizedParameter.cs index 6b01f06..adc60ed 100644 --- a/Semantics.Quantities/AudioEngineering/NormalizedParameter.cs +++ b/Semantics.Quantities/AudioEngineering/NormalizedParameter.cs @@ -2,7 +2,7 @@ // All rights reserved. // Licensed under the MIT license. -namespace ktsu.Semantics; +namespace ktsu.Semantics.Quantities; using System.Numerics; diff --git a/Semantics.Quantities/AudioEngineering/ParameterTaper.cs b/Semantics.Quantities/AudioEngineering/ParameterTaper.cs index 35cea40..14e87f9 100644 --- a/Semantics.Quantities/AudioEngineering/ParameterTaper.cs +++ b/Semantics.Quantities/AudioEngineering/ParameterTaper.cs @@ -2,7 +2,7 @@ // All rights reserved. // Licensed under the MIT license. -namespace ktsu.Semantics; +namespace ktsu.Semantics.Quantities; /// /// Describes how a maps a host-normalized position in diff --git a/Semantics.Quantities/AudioEngineering/Percent.cs b/Semantics.Quantities/AudioEngineering/Percent.cs deleted file mode 100644 index f26fd47..0000000 --- a/Semantics.Quantities/AudioEngineering/Percent.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Globalization; -using System.Numerics; - -/// -/// Represents a percentage (a ratio scaled by 100). -/// -/// -/// 100 percent corresponds to a of one. This type exists so that -/// user-facing parameters (mix, depth, drive) can be expressed in the units a musician expects while -/// still round-tripping losslessly to and from a normalized ratio. -/// -/// The floating-point storage type. -/// The percentage value. -public readonly record struct Percent(T Value) : IComparable> - where T : struct, INumber -{ - /// - /// Creates a percentage from a raw value. - /// - /// The percentage value (100 = unity). - /// A new . - public static Percent Create(T value) => new(value); - - /// - /// Creates a percentage from a fraction in the range [0, 1] (0.5 → 50%). - /// - /// The fraction to scale by 100. - /// A new . - public static Percent FromFraction(T fraction) => new(fraction * T.CreateChecked(100)); - - /// - /// Creates a percentage from a ratio (1 → 100%). - /// - /// The ratio to convert. - /// A new . - public static Percent FromRatio(Ratio ratio) => ratio.ToPercent(); - - /// - /// Converts this percentage to a fraction in the range [0, 1] (50% → 0.5). - /// - /// The fractional value. - public T ToFraction() => Value / T.CreateChecked(100); - - /// - /// Converts this percentage to a ratio (100% → 1). - /// - /// The equivalent . - public Ratio ToRatio() => new(ToFraction()); - - /// - public int CompareTo(Percent other) => Value.CompareTo(other.Value); - - /// Determines whether one percentage is less than another. - /// The left percentage. - /// The right percentage. - /// if is less than . - public static bool operator <(Percent left, Percent right) => left.CompareTo(right) < 0; - - /// Determines whether one percentage is greater than another. - /// The left percentage. - /// The right percentage. - /// if is greater than . - public static bool operator >(Percent left, Percent right) => left.CompareTo(right) > 0; - - /// Determines whether one percentage is less than or equal to another. - /// The left percentage. - /// The right percentage. - /// if is less than or equal to . - public static bool operator <=(Percent left, Percent right) => left.CompareTo(right) <= 0; - - /// Determines whether one percentage is greater than or equal to another. - /// The left percentage. - /// The right percentage. - /// if is greater than or equal to . - public static bool operator >=(Percent left, Percent right) => left.CompareTo(right) >= 0; - - /// Returns a culture-invariant string representation of this percentage. - /// The percentage formatted with a % suffix. - public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"{Value}%"); -} diff --git a/Semantics.Quantities/AudioEngineering/QFactor.cs b/Semantics.Quantities/AudioEngineering/QFactor.cs index b5954c5..034d598 100644 --- a/Semantics.Quantities/AudioEngineering/QFactor.cs +++ b/Semantics.Quantities/AudioEngineering/QFactor.cs @@ -2,7 +2,7 @@ // All rights reserved. // Licensed under the MIT license. -namespace ktsu.Semantics; +namespace ktsu.Semantics.Quantities; using System.Globalization; using System.Numerics; diff --git a/Semantics.Quantities/AudioEngineering/Ratio.cs b/Semantics.Quantities/AudioEngineering/Ratio.cs deleted file mode 100644 index 320ee66..0000000 --- a/Semantics.Quantities/AudioEngineering/Ratio.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Globalization; -using System.Numerics; - -/// -/// Represents a dimensionless ratio between two like quantities. -/// -/// -/// A ratio of 1 means the two quantities are equal. Ratios are the natural currency of audio -/// engineering parameters (gain, mix, feedback, depth) and convert cleanly to and from -/// . -/// -/// The floating-point storage type. -/// The ratio value. -public readonly record struct Ratio(T Value) : IComparable> - where T : struct, INumber -{ - /// Gets a ratio of one (unity). - public static Ratio Unity => new(T.One); - - /// Gets a ratio of zero. - public static Ratio Zero => new(T.Zero); - - /// - /// Creates a ratio from a raw value. - /// - /// The ratio value (1 = unity). - /// A new . - public static Ratio Create(T value) => new(value); - - /// - /// Creates a ratio from a percentage. - /// - /// The percentage (100% = unity). - /// A new . - public static Ratio FromPercent(Percent percent) => percent.ToRatio(); - - /// - /// Converts this ratio to a percentage (1 → 100%). - /// - /// The equivalent . - public Percent ToPercent() => new(Value * T.CreateChecked(100)); - - /// Multiplies two ratios. - /// The left ratio. - /// The right ratio. - /// The product ratio. - public static Ratio operator *(Ratio left, Ratio right) => new(left.Value * right.Value); - - /// Divides two ratios. - /// The numerator ratio. - /// The denominator ratio. - /// The quotient ratio. - public static Ratio operator /(Ratio left, Ratio right) => new(left.Value / right.Value); - - /// Multiplies two ratios (friendly alternate for operator *). - /// The left ratio. - /// The right ratio. - /// The product ratio. - public static Ratio Multiply(Ratio left, Ratio right) => left * right; - - /// Divides two ratios (friendly alternate for operator /). - /// The numerator ratio. - /// The denominator ratio. - /// The quotient ratio. - public static Ratio Divide(Ratio left, Ratio right) => left / right; - - /// - public int CompareTo(Ratio other) => Value.CompareTo(other.Value); - - /// Determines whether one ratio is less than another. - /// The left ratio. - /// The right ratio. - /// if is less than . - public static bool operator <(Ratio left, Ratio right) => left.CompareTo(right) < 0; - - /// Determines whether one ratio is greater than another. - /// The left ratio. - /// The right ratio. - /// if is greater than . - public static bool operator >(Ratio left, Ratio right) => left.CompareTo(right) > 0; - - /// Determines whether one ratio is less than or equal to another. - /// The left ratio. - /// The right ratio. - /// if is less than or equal to . - public static bool operator <=(Ratio left, Ratio right) => left.CompareTo(right) <= 0; - - /// Determines whether one ratio is greater than or equal to another. - /// The left ratio. - /// The right ratio. - /// if is greater than or equal to . - public static bool operator >=(Ratio left, Ratio right) => left.CompareTo(right) >= 0; - - /// Returns a culture-invariant string representation of this ratio. - /// The ratio formatted with an x suffix. - public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"{Value}x"); -} diff --git a/Semantics.Quantities/AudioEngineering/Semitones.cs b/Semantics.Quantities/AudioEngineering/Semitones.cs index f94725d..5086949 100644 --- a/Semantics.Quantities/AudioEngineering/Semitones.cs +++ b/Semantics.Quantities/AudioEngineering/Semitones.cs @@ -2,125 +2,33 @@ // All rights reserved. // Licensed under the MIT license. -namespace ktsu.Semantics; +namespace ktsu.Semantics.Quantities; -using System.Globalization; using System.Numerics; /// -/// Represents a musical pitch interval measured in semitones (twelve-tone equal temperament). +/// Bespoke members of ; the logarithmic core (including the +/// frequency- conversions) is generated from logarithmic.json. /// -/// -/// One semitone is a frequency ratio of 2^(1/12); twelve semitones make one octave (a ratio of -/// two). Semitones are the natural unit for pitch-shift, detune, and transpose parameters and convert -/// to and from (1 semitone = 100 cents) and frequency ratios. -/// -/// The floating-point storage type. -/// The interval in semitones. -public readonly record struct Semitones(T Value) : IComparable> +public readonly partial record struct Semitones where T : struct, INumber { - /// Gets a unison interval (zero semitones). + /// Gets the interval of zero semitones (unison). public static Semitones Unison => new(T.Zero); - /// Gets an octave interval (twelve semitones). + /// Gets the interval of one octave (12 semitones). public static Semitones Octave => new(T.CreateChecked(12)); /// - /// Creates an interval from a raw semitone value. - /// - /// The interval in semitones. - /// A new . - public static Semitones Create(T value) => new(value); - - /// - /// Creates an interval from cents (100 cents → 1 semitone). + /// Creates an interval from cents (100 cents is one semitone). /// /// The interval in cents. /// A new . public static Semitones FromCents(Cents cents) => cents.ToSemitones(); /// - /// Creates an interval from a frequency ratio using semitones = 12·log2(ratio). - /// - /// The frequency ratio. - /// A new . - public static Semitones FromFrequencyRatio(Ratio ratio) - { - double r = double.CreateChecked(ratio.Value); - double semitones = 12.0 * Math.Log2(r); - return new(T.CreateChecked(semitones)); - } - - /// - /// Converts this interval to cents (1 semitone → 100 cents). + /// Converts this interval to cents (one semitone is 100 cents). /// /// The equivalent . public Cents ToCents() => new(Value * T.CreateChecked(100)); - - /// - /// Converts this interval to a frequency ratio using ratio = 2^(semitones/12). - /// - /// The equivalent frequency . - public Ratio ToFrequencyRatio() - { - double semitones = double.CreateChecked(Value); - double ratio = Math.Pow(2.0, semitones / 12.0); - return new(T.CreateChecked(ratio)); - } - - /// Adds two intervals. - /// The first interval. - /// The second interval. - /// The combined interval. - public static Semitones operator +(Semitones left, Semitones right) => new(left.Value + right.Value); - - /// Subtracts one interval from another. - /// The interval to subtract from. - /// The interval to subtract. - /// The difference interval. - public static Semitones operator -(Semitones left, Semitones right) => new(left.Value - right.Value); - - /// Adds two intervals (friendly alternate for operator +). - /// The first interval. - /// The second interval. - /// The combined interval. - public static Semitones Add(Semitones left, Semitones right) => left + right; - - /// Subtracts one interval from another (friendly alternate for operator -). - /// The interval to subtract from. - /// The interval to subtract. - /// The difference interval. - public static Semitones Subtract(Semitones left, Semitones right) => left - right; - - /// - public int CompareTo(Semitones other) => Value.CompareTo(other.Value); - - /// Determines whether one interval is less than another. - /// The left interval. - /// The right interval. - /// if is less than . - public static bool operator <(Semitones left, Semitones right) => left.CompareTo(right) < 0; - - /// Determines whether one interval is greater than another. - /// The left interval. - /// The right interval. - /// if is greater than . - public static bool operator >(Semitones left, Semitones right) => left.CompareTo(right) > 0; - - /// Determines whether one interval is less than or equal to another. - /// The left interval. - /// The right interval. - /// if is less than or equal to . - public static bool operator <=(Semitones left, Semitones right) => left.CompareTo(right) <= 0; - - /// Determines whether one interval is greater than or equal to another. - /// The left interval. - /// The right interval. - /// if is greater than or equal to . - public static bool operator >=(Semitones left, Semitones right) => left.CompareTo(right) >= 0; - - /// Returns a culture-invariant string representation of this interval. - /// The interval formatted with an st suffix. - public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"{Value} st"); } diff --git a/Semantics.Quantities/Chemical/ActivationEnergy.cs b/Semantics.Quantities/Chemical/ActivationEnergy.cs deleted file mode 100644 index 3d30f6e..0000000 --- a/Semantics.Quantities/Chemical/ActivationEnergy.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents activation energy with a specific unit of measurement. -/// Activation energy is the minimum energy required for a reaction to occur. -/// -/// The numeric type for the activation energy value. -public sealed record ActivationEnergy : PhysicalQuantity, T> - where T : struct, INumber, IFloatingPoint -{ - /// Gets the physical dimension of activation energy [M L² T⁻² N⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.ActivationEnergy; - - /// Initializes a new instance of the class. - public ActivationEnergy() : base() { } - - /// Calculates activation energy from rate constants at two temperatures using Arrhenius equation. - /// Rate constant at temperature 1. - /// Rate constant at temperature 2. - /// Temperature 1. - /// Temperature 2. - /// The activation energy. - public static ActivationEnergy FromArrheniusPlot(RateConstant rateConstant1, RateConstant rateConstant2, - Temperature temperature1, Temperature temperature2) - { - Ensure.NotNull(rateConstant1); - Ensure.NotNull(rateConstant2); - Ensure.NotNull(temperature1); - Ensure.NotNull(temperature2); - - T k1 = rateConstant1.In(Units.PerSecond); - T k2 = rateConstant2.In(Units.PerSecond); - T t1 = temperature1.In(Units.Kelvin); - T t2 = temperature2.In(Units.Kelvin); - T gasConstant = PhysicalConstants.Generic.GasConstant(); - - T lnRatio = T.CreateChecked(Math.Log(double.CreateChecked(k2 / k1))); - T tempDifference = (T.One / t1) - (T.One / t2); - T ea = -gasConstant * lnRatio / tempDifference; - return Create(ea); - } - - /// Common activation energies for various processes. - public static class CommonValues - { - /// Water self-ionization: ~55 kJ/mol. - public static ActivationEnergy WaterSelfIonization => Create(T.CreateChecked(55000)); - - /// DNA denaturation: ~150 kJ/mol. - public static ActivationEnergy DNADenaturation => Create(T.CreateChecked(150000)); - - /// Protein denaturation: ~200 kJ/mol. - public static ActivationEnergy ProteinDenaturation => Create(T.CreateChecked(200000)); - - /// Ester hydrolysis: ~70 kJ/mol. - public static ActivationEnergy EsterHydrolysis => Create(T.CreateChecked(70000)); - - /// Simple nucleophilic substitution: ~80 kJ/mol. - public static ActivationEnergy NucleophilicSubstitution => Create(T.CreateChecked(80000)); - - /// Enzyme-catalyzed reaction: ~50 kJ/mol (lowered from uncatalyzed). - public static ActivationEnergy EnzymeCatalyzed => Create(T.CreateChecked(50000)); - } - - /// Calculates the ratio of rate constants at two temperatures. - /// Initial temperature. - /// Final temperature. - /// The ratio k2/k1. - public T CalculateRateRatio(Temperature temperature1, Temperature temperature2) - { - Ensure.NotNull(temperature1); - Ensure.NotNull(temperature2); - - T ea = In(Units.JoulesPerMole); - T t1 = temperature1.In(Units.Kelvin); - T t2 = temperature2.In(Units.Kelvin); - T gasConstant = PhysicalConstants.Generic.GasConstant(); - - T exponent = ea / gasConstant * ((T.One / t1) - (T.One / t2)); - return T.CreateChecked(Math.Exp(double.CreateChecked(exponent))); - } -} diff --git a/Semantics.Quantities/Chemical/AmountOfSubstance.cs b/Semantics.Quantities/Chemical/AmountOfSubstance.cs deleted file mode 100644 index 7b892d0..0000000 --- a/Semantics.Quantities/Chemical/AmountOfSubstance.cs +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an amount of substance with a specific unit of measurement. -/// Amount of substance is a fundamental SI quantity measured in moles. -/// -/// The numeric type for the amount value. -public sealed record AmountOfSubstance : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of amount of substance [N]. - public override PhysicalDimension Dimension => PhysicalDimensions.AmountOfSubstance; - - /// Initializes a new instance of the class. - public AmountOfSubstance() : base() { } - - /// Creates an amount from the number of entities and Avogadro's number. - /// Number of entities (atoms, molecules, ions). - /// The amount of substance in moles. - public static AmountOfSubstance FromEntities(T numberOfEntities) - { - T avogadroNumber = PhysicalConstants.Generic.AvogadroNumber(); - T moles = numberOfEntities / avogadroNumber; - return Create(moles); - } - - /// Calculates the number of entities from amount of substance. - /// The number of entities (atoms, molecules, ions). - public T GetNumberOfEntities() - { - T avogadroNumber = PhysicalConstants.Generic.AvogadroNumber(); - T moles = In(Units.Mole); - return moles * avogadroNumber; - } - - /// Calculates mass from amount of substance and molar mass. - /// The molar mass of the substance. - /// The mass of the substance. - public Mass CalculateMass(MolarMass molarMass) - { - Ensure.NotNull(molarMass); - T molesValue = In(Units.Mole); - T molarMassValue = molarMass.In(Units.GramPerMole); - T massInGrams = molesValue * molarMassValue; - return Mass.Create(massInGrams); - } - - /// Calculates volume from amount of substance at STP. - /// The molar volume at STP (22.414 L/mol). - public Volume GetMolarVolumeAtSTP() - { - T molarVolumeSTP = PhysicalConstants.Generic.MolarVolumeSTP(); - T moles = In(Units.Mole); - T volumeInLiters = moles * molarVolumeSTP; - return Volume.Create(volumeInLiters); - } - - /// - /// Calculates the concentration from amount of substance and volume (C = n/V). - /// - /// The amount of substance. - /// The volume. - /// The resulting concentration. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Concentration operator /(AmountOfSubstance amount, Volume volume) - { - Ensure.NotNull(amount); - Ensure.NotNull(volume); - - T concentrationValue = amount.Value / volume.Value; - - return Concentration.Create(concentrationValue); - } - - /// - /// Calculates the mass from amount of substance and molar mass (m = n × M). - /// - /// The amount of substance. - /// The molar mass. - /// The resulting mass. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Mass operator *(AmountOfSubstance amount, MolarMass molarMass) - { - Ensure.NotNull(amount); - Ensure.NotNull(molarMass); - - T massValue = amount.Value * molarMass.Value; - - return Mass.Create(massValue); - } - - /// - /// Creates a new AmountOfSubstance from a value in moles. - /// - /// The value in moles. - /// A new AmountOfSubstance instance. - public static AmountOfSubstance FromMoles(T moles) => Create(moles); - - /// - /// Calculates amount of substance using ideal gas law (n = PV/RT). - /// - /// The pressure. - /// The volume of the gas. - /// The absolute temperature. - /// The resulting amount of substance in moles. - public static AmountOfSubstance FromIdealGasLaw(Pressure pressure, Volume volume, Temperature temperature) - { - Ensure.NotNull(pressure); - Ensure.NotNull(volume); - Ensure.NotNull(temperature); - - T gasConstant = PhysicalConstants.Generic.GasConstant(); - T amountValue = pressure.Value * volume.Value / (gasConstant * temperature.Value); - - return Create(amountValue); - } - - /// - /// Calculates pressure using ideal gas law (P = nRT/V). - /// - /// The amount of substance. - /// The absolute temperature. - /// The volume of the gas. - /// The resulting pressure. - public static Pressure CalculatePressureFromIdealGas(AmountOfSubstance amount, Temperature temperature, Volume volume) - { - Ensure.NotNull(amount); - Ensure.NotNull(temperature); - Ensure.NotNull(volume); - - T gasConstant = PhysicalConstants.Generic.GasConstant(); - T pressureValue = amount.Value * gasConstant * temperature.Value / volume.Value; - - return Pressure.Create(pressureValue); - } - - /// - /// Calculates volume using ideal gas law (V = nRT/P). - /// - /// The amount of substance. - /// The absolute temperature. - /// The pressure. - /// The resulting volume. - public static Volume CalculateVolumeFromIdealGas(AmountOfSubstance amount, Temperature temperature, Pressure pressure) - { - Ensure.NotNull(amount); - Ensure.NotNull(temperature); - Ensure.NotNull(pressure); - - T gasConstant = PhysicalConstants.Generic.GasConstant(); - T volumeValue = amount.Value * gasConstant * temperature.Value / pressure.Value; - - return Volume.Create(volumeValue); - } - - /// - /// Calculates temperature using ideal gas law (T = PV/nR). - /// - /// The pressure. - /// The volume of the gas. - /// The amount of substance. - /// The resulting absolute temperature. - public static Temperature CalculateTemperatureFromIdealGas(Pressure pressure, Volume volume, AmountOfSubstance amount) - { - Ensure.NotNull(pressure); - Ensure.NotNull(volume); - Ensure.NotNull(amount); - - T gasConstant = PhysicalConstants.Generic.GasConstant(); - T temperatureValue = pressure.Value * volume.Value / (amount.Value * gasConstant); - - return Temperature.Create(temperatureValue); - } -} diff --git a/Semantics.Quantities/Chemical/Concentration.cs b/Semantics.Quantities/Chemical/Concentration.cs deleted file mode 100644 index be572c5..0000000 --- a/Semantics.Quantities/Chemical/Concentration.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents concentration with a specific unit of measurement. -/// Concentration describes the amount of substance per unit volume. -/// -/// The numeric type for the concentration value. -public sealed record Concentration : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of concentration [N L⁻³]. - public override PhysicalDimension Dimension => PhysicalDimensions.Concentration; - - /// Initializes a new instance of the class. - public Concentration() : base() { } - - /// Calculates molarity from amount of substance and volume. - /// The amount of solute. - /// The volume of solution. - /// The molarity (mol/L). - public static Concentration FromMolarity(AmountOfSubstance amountOfSubstance, Volume volume) - { - Ensure.NotNull(amountOfSubstance); - Ensure.NotNull(volume); - T moles = amountOfSubstance.In(Units.Mole); - T liters = volume.In(Units.Liter); - T molarity = moles / liters; - return Create(molarity); - } - - /// Calculates parts per million concentration. - /// Mass of solute. - /// Total mass of solution. - /// The concentration in ppm. - public static Concentration FromPartsPerMillion(Mass soluteMass, Mass solutionMass) - { - Ensure.NotNull(soluteMass); - Ensure.NotNull(solutionMass); - T soluteGrams = soluteMass.In(Units.Gram); - T solutionGrams = solutionMass.In(Units.Gram); - T ratio = soluteGrams / solutionGrams; - T ppm = ratio * T.CreateChecked(1e6); - return Create(ppm); - } - - /// Calculates weight/volume percent concentration. - /// Mass of solute. - /// Volume of solution. - /// The concentration in % w/v. - public static Concentration FromWeightVolumePercent(Mass soluteMass, Volume solutionVolume) - { - Ensure.NotNull(soluteMass); - Ensure.NotNull(solutionVolume); - T massGrams = soluteMass.In(Units.Gram); - T volumeML = solutionVolume.In(Units.Milliliter); - T ratio = massGrams / volumeML; - T percentWV = ratio * T.CreateChecked(100); - return Create(percentWV); - } - - /// Dilution calculation: C1V1 = C2V2. - /// Initial volume. - /// Final volume after dilution. - /// The final concentration after dilution. - public Concentration Dilute(Volume initialVolume, Volume finalVolume) - { - Ensure.NotNull(initialVolume); - Ensure.NotNull(finalVolume); - T c1 = In(Units.Molar); - T v1 = initialVolume.In(Units.Liter); - T v2 = finalVolume.In(Units.Liter); - T c2 = c1 * v1 / v2; - return Create(c2); - } - - /// - /// Calculates the amount of substance from concentration and volume (n = C × V). - /// - /// The concentration. - /// The volume. - /// The resulting amount of substance. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static AmountOfSubstance operator *(Concentration concentration, Volume volume) - { - Ensure.NotNull(concentration); - Ensure.NotNull(volume); - - T amountValue = concentration.Value * volume.Value; - - return AmountOfSubstance.Create(amountValue); - } - - /// - /// Calculates the volume from concentration and amount of substance (V = n/C). - /// - /// The amount of substance. - /// The concentration. - /// The resulting volume. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Volume operator /(AmountOfSubstance amount, Concentration concentration) - { - Ensure.NotNull(amount); - Ensure.NotNull(concentration); - - T volumeValue = amount.Value / concentration.Value; - - return Volume.Create(volumeValue); - } -} diff --git a/Semantics.Quantities/Chemical/DynamicViscosity.cs b/Semantics.Quantities/Chemical/DynamicViscosity.cs deleted file mode 100644 index 00dc6e5..0000000 --- a/Semantics.Quantities/Chemical/DynamicViscosity.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents dynamic viscosity with a specific unit of measurement. -/// Dynamic viscosity measures a fluid's resistance to flow. -/// -/// The numeric type for the dynamic viscosity value. -public sealed record DynamicViscosity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of dynamic viscosity [M L⁻¹ T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.DynamicViscosity; - - /// Initializes a new instance of the class. - public DynamicViscosity() : base() { } - - /// Calculates dynamic viscosity from shear stress and shear rate. - /// Shear stress in the fluid. - /// Shear rate (velocity gradient). - /// The dynamic viscosity. - public static DynamicViscosity FromShearStressAndRate(Pressure shearStress, T shearRate) - { - Ensure.NotNull(shearStress); - - T stress = shearStress.In(Units.Pascal); - T viscosity = stress / shearRate; - return Create(viscosity); - } - - /// Calculates kinematic viscosity from dynamic viscosity and density. - /// Fluid density. - /// The kinematic viscosity. - public T CalculateKinematicViscosity(Density density) - { - Ensure.NotNull(density); - - T dynamicVisc = In(Units.PascalSecond); - T rho = density.In(Units.Gram); // kg/m³ - return dynamicVisc / rho; - } - - /// Common dynamic viscosity values for various fluids. - public static class CommonValues - { - /// Water at 20°C: 1.002 × 10⁻³ Pa·s. - public static DynamicViscosity Water => Create(T.CreateChecked(1.002e-3)); - - /// Air at 20°C: 1.82 × 10⁻⁵ Pa·s. - public static DynamicViscosity Air => Create(T.CreateChecked(1.82e-5)); - - /// Honey: ~10 Pa·s. - public static DynamicViscosity Honey => Create(T.CreateChecked(10)); - - /// Motor oil SAE 30: ~0.2 Pa·s. - public static DynamicViscosity MotorOil => Create(T.CreateChecked(0.2)); - - /// Glycerol at 20°C: 1.41 Pa·s. - public static DynamicViscosity Glycerol => Create(T.CreateChecked(1.41)); - - /// Blood at 37°C: ~4 × 10⁻³ Pa·s. - public static DynamicViscosity Blood => Create(T.CreateChecked(4e-3)); - - /// Mercury at 20°C: 1.55 × 10⁻³ Pa·s. - public static DynamicViscosity Mercury => Create(T.CreateChecked(1.55e-3)); - } - - /// Calculates Reynolds number for flow characterization. - /// Fluid density. - /// Flow velocity. - /// Characteristic length. - /// Reynolds number (dimensionless). - public T CalculateReynoldsNumber(Density density, Velocity velocity, Length characteristicLength) - { - Ensure.NotNull(density); - Ensure.NotNull(velocity); - Ensure.NotNull(characteristicLength); - - T rho = density.In(Units.Gram); // kg/m³ - T v = velocity.In(Units.MetersPerSecond); - T l = characteristicLength.In(Units.Meter); - T mu = In(Units.PascalSecond); - - return rho * v * l / mu; - } -} diff --git a/Semantics.Quantities/Chemical/EnzymeActivity.cs b/Semantics.Quantities/Chemical/EnzymeActivity.cs deleted file mode 100644 index 00d6afe..0000000 --- a/Semantics.Quantities/Chemical/EnzymeActivity.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents enzyme activity with a specific unit of measurement. -/// Enzyme activity measures the rate of substrate conversion by an enzyme. -/// -/// The numeric type for the enzyme activity value. -public sealed record EnzymeActivity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of enzyme activity [N T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.EnzymeActivity; - - /// Initializes a new instance of the class. - public EnzymeActivity() : base() { } - - /// Calculates enzyme activity from substrate conversion rate. - /// Amount of substrate converted. - /// Time interval. - /// The enzyme activity. - public static EnzymeActivity FromSubstrateConversion(AmountOfSubstance substrateConverted, Time timeInterval) - { - Ensure.NotNull(substrateConverted); - Ensure.NotNull(timeInterval); - - T moles = substrateConverted.In(Units.Mole); - T seconds = timeInterval.In(Units.Second); - T activity = moles / seconds; - return Create(activity); - } - - /// Calculates specific activity (activity per unit mass of enzyme). - /// Mass of enzyme. - /// Specific activity in units per gram. - public T CalculateSpecificActivity(Mass enzymeMass) - { - Ensure.NotNull(enzymeMass); - - T activity = In(Units.Katal); - T massInGrams = enzymeMass.In(Units.Gram); - return activity / massInGrams; - } - - /// Michaelis-Menten kinetics: v = (Vmax * [S]) / (Km + [S]). - /// Maximum velocity (Vmax). - /// Substrate concentration. - /// Michaelis constant (Km). - /// The reaction velocity. - public static EnzymeActivity MichaelisMenten(EnzymeActivity maxVelocity, - Concentration substrateConcentration, Concentration michaelisConstant) - { - Ensure.NotNull(maxVelocity); - Ensure.NotNull(substrateConcentration); - Ensure.NotNull(michaelisConstant); - - T vmax = maxVelocity.In(Units.Katal); - T s = substrateConcentration.In(Units.Molar); - T km = michaelisConstant.In(Units.Molar); - - T velocity = vmax * s / (km + s); - return Create(velocity); - } - - /// Calculates turnover number (kcat) from Vmax and enzyme concentration. - /// Maximum velocity. - /// Total enzyme concentration. - /// Turnover number in s⁻¹. - public static T CalculateTurnoverNumber(EnzymeActivity maxVelocity, Concentration enzymeConcentration) - { - Ensure.NotNull(maxVelocity); - Ensure.NotNull(enzymeConcentration); - - T vmax = maxVelocity.In(Units.Katal); - T et = enzymeConcentration.In(Units.Molar); - return vmax / et; - } - - /// Common enzyme activities for reference. - public static class CommonValues - { - /// Typical enzyme activity: ~1 μmol/min. - public static EnzymeActivity TypicalEnzyme => Create(T.CreateChecked(1e-6 / 60)); // 1 μmol/min in mol/s - - /// High activity enzyme: ~1000 μmol/min. - public static EnzymeActivity HighActivity => Create(T.CreateChecked(1e-3 / 60)); // 1000 μmol/min in mol/s - - /// Catalase (very fast): ~6 × 10⁶ μmol/min. - public static EnzymeActivity Catalase => Create(T.CreateChecked(6e3 / 60)); // 6 × 10⁶ μmol/min in mol/s - - /// Carbonic anhydrase: ~1 × 10⁶ μmol/min. - public static EnzymeActivity CarbonicAnhydrase => Create(T.CreateChecked(1e3 / 60)); // 1 × 10⁶ μmol/min in mol/s - } -} diff --git a/Semantics.Quantities/Chemical/MolarMass.cs b/Semantics.Quantities/Chemical/MolarMass.cs deleted file mode 100644 index 6590198..0000000 --- a/Semantics.Quantities/Chemical/MolarMass.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents molar mass with a specific unit of measurement. -/// Molar mass is the mass of one mole of a substance. -/// -/// The numeric type for the molar mass value. -public sealed record MolarMass : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of molar mass [M N⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.MolarMass; - - /// Initializes a new instance of the class. - public MolarMass() : base() { } - - /// Calculates molar mass from atomic masses and composition. - /// Total mass of the compound. - /// Amount of substance. - /// The molar mass. - public static MolarMass FromMassAndAmount(Mass totalMass, AmountOfSubstance amountOfSubstance) - { - Ensure.NotNull(totalMass); - Ensure.NotNull(amountOfSubstance); - - T massInGrams = totalMass.In(Units.Gram); - T moles = amountOfSubstance.In(Units.Mole); - T molarMass = massInGrams / moles; - return Create(molarMass); - } - - /// Common molecular weights of chemical compounds. - public static class CommonValues - { - /// Water (H₂O) molar mass: 18.015 g/mol. - public static MolarMass Water => Create(T.CreateChecked(18.015)); - - /// Carbon dioxide (CO₂) molar mass: 44.010 g/mol. - public static MolarMass CarbonDioxide => Create(T.CreateChecked(44.010)); - - /// Methane (CH₄) molar mass: 16.043 g/mol. - public static MolarMass Methane => Create(T.CreateChecked(16.043)); - - /// Glucose (C₆H₁₂O₆) molar mass: 180.156 g/mol. - public static MolarMass Glucose => Create(T.CreateChecked(180.156)); - - /// Sodium chloride (NaCl) molar mass: 58.440 g/mol. - public static MolarMass SodiumChloride => Create(T.CreateChecked(58.440)); - - /// Ethanol (C₂H₆O) molar mass: 46.069 g/mol. - public static MolarMass Ethanol => Create(T.CreateChecked(46.069)); - } - - /// Calculates the mass of a given amount of this substance. - /// The amount of substance. - /// The total mass. - public Mass CalculateMass(AmountOfSubstance amount) - { - Ensure.NotNull(amount); - - T molarMassValue = In(Units.GramPerMole); - T moles = amount.In(Units.Mole); - T massInGrams = molarMassValue * moles; - return Mass.Create(massInGrams); - } -} diff --git a/Semantics.Quantities/Chemical/RateConstant.cs b/Semantics.Quantities/Chemical/RateConstant.cs deleted file mode 100644 index bf14034..0000000 --- a/Semantics.Quantities/Chemical/RateConstant.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a rate constant with a specific unit of measurement. -/// Rate constant determines the speed of a chemical reaction. -/// -/// The numeric type for the rate constant value. -public sealed record RateConstant : PhysicalQuantity, T> - where T : struct, INumber, IFloatingPoint -{ - /// Gets the physical dimension of rate constant [T⁻¹] for first-order reactions. - public override PhysicalDimension Dimension => PhysicalDimensions.RateConstant; - - /// Initializes a new instance of the class. - public RateConstant() : base() { } - - /// Calculates rate constant from Arrhenius equation: k = A * e^(-Ea/RT). - /// Pre-exponential factor A. - /// Activation energy. - /// Temperature. - /// The rate constant. - public static RateConstant FromArrheniusEquation(T preExponentialFactor, - ActivationEnergy activationEnergy, Temperature temperature) - { - Ensure.NotNull(activationEnergy); - Ensure.NotNull(temperature); - - T ea = activationEnergy.In(Units.JoulesPerMole); - T temp = temperature.In(Units.Kelvin); - T gasConstant = PhysicalConstants.Generic.GasConstant(); - - T exponent = -ea / (gasConstant * temp); - T k = preExponentialFactor * T.CreateChecked(Math.Exp(double.CreateChecked(exponent))); - return Create(k); - } - - /// Calculates temperature dependence ratio: k2/k1 = exp((Ea/R) * (1/T1 - 1/T2)). - /// Initial temperature. - /// Final temperature. - /// Activation energy. - /// The rate constant at temperature2. - public RateConstant AtTemperature(Temperature temperature1, Temperature temperature2, - ActivationEnergy activationEnergy) - { - Ensure.NotNull(temperature1); - Ensure.NotNull(temperature2); - Ensure.NotNull(activationEnergy); - - T k1 = In(Units.PerSecond); - T t1 = temperature1.In(Units.Kelvin); - T t2 = temperature2.In(Units.Kelvin); - T ea = activationEnergy.In(Units.JoulesPerMole); - T gasConstant = PhysicalConstants.Generic.GasConstant(); - - T exponent = ea / gasConstant * ((T.One / t1) - (T.One / t2)); - T ratio = T.CreateChecked(Math.Exp(double.CreateChecked(exponent))); - T k2 = k1 * ratio; - return Create(k2); - } - - /// Common rate constant values for reference reactions. - public static class CommonValues - { - /// Typical enzyme turnover (kcat): ~1000 s⁻¹. - public static RateConstant TypicalEnzyme => Create(T.CreateChecked(1000)); - - /// Fast protein folding: ~10⁶ s⁻¹. - public static RateConstant FastProteinFolding => Create(T.CreateChecked(1e6)); - - /// DNA replication: ~1000 s⁻¹. - public static RateConstant DNAReplication => Create(T.CreateChecked(1000)); - - /// Slow metabolic process: ~0.01 s⁻¹. - public static RateConstant SlowMetabolic => Create(T.CreateChecked(0.01)); - } -} diff --git a/Semantics.Quantities/Chemical/ReactionRate.cs b/Semantics.Quantities/Chemical/ReactionRate.cs deleted file mode 100644 index 270a637..0000000 --- a/Semantics.Quantities/Chemical/ReactionRate.cs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents reaction rate with a specific unit of measurement. -/// Reaction rate measures the speed of a chemical reaction. -/// -/// The numeric type for the reaction rate value. -public sealed record ReactionRate : PhysicalQuantity, T> - where T : struct, INumber, IFloatingPoint -{ - /// Gets the physical dimension of reaction rate [N L⁻³ T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.ReactionRate; - - /// Initializes a new instance of the class. - public ReactionRate() : base() { } - - /// Calculates reaction rate from concentration change over time. - /// Change in concentration. - /// Time interval for the change. - /// The reaction rate. - public static ReactionRate FromConcentrationChange(Concentration concentrationChange, Time timeInterval) - { - Ensure.NotNull(concentrationChange); - Ensure.NotNull(timeInterval); - - T deltaConcentration = concentrationChange.In(Units.Molar); - T deltaTime = timeInterval.In(Units.Second); - T rate = deltaConcentration / deltaTime; - return Create(rate); - } - - /// Calculates reaction rate using rate law: rate = k[A]^m[B]^n. - /// Rate constant. - /// Concentration of reactant A. - /// Reaction order for A. - /// Concentration of reactant B. - /// Reaction order for B. - /// The reaction rate. - public static ReactionRate FromRateLaw(RateConstant rateConstant, - Concentration concentrationA, T orderA, - Concentration concentrationB, T orderB) - { - Ensure.NotNull(rateConstant); - Ensure.NotNull(concentrationA); - Ensure.NotNull(concentrationB); - - T k = rateConstant.In(Units.PerSecond); - T cA = concentrationA.In(Units.Molar); - T cB = concentrationB.In(Units.Molar); - - T rateValue = k * T.CreateChecked(Math.Pow(double.CreateChecked(cA), double.CreateChecked(orderA))) * T.CreateChecked(Math.Pow(double.CreateChecked(cB), double.CreateChecked(orderB))); - return Create(rateValue); - } - - /// Calculates rate constant from initial rate and concentrations. - /// Initial concentration of A. - /// Initial concentration of B. - /// Reaction order for A. - /// Reaction order for B. - /// The rate constant. - public RateConstant CalculateRateConstant(Concentration concentrationA, Concentration concentrationB, - T orderA, T orderB) - { - Ensure.NotNull(concentrationA); - Ensure.NotNull(concentrationB); - - T rate = In(Units.MolesPerSecond); - T cA = concentrationA.In(Units.Molar); - T cB = concentrationB.In(Units.Molar); - - T denominator = T.CreateChecked(Math.Pow(double.CreateChecked(cA), double.CreateChecked(orderA))) * T.CreateChecked(Math.Pow(double.CreateChecked(cB), double.CreateChecked(orderB))); - T k = rate / denominator; - return RateConstant.Create(k); - } - - /// Calculates half-life for first-order reaction. - /// First-order rate constant. - /// The half-life time. - public static Time CalculateHalfLife(RateConstant rateConstant) - { - Ensure.NotNull(rateConstant); - - T k = rateConstant.In(Units.PerSecond); - T ln2 = PhysicalConstants.Generic.Ln2(); - T halfLife = ln2 / k; - return Time.Create(halfLife); - } -} diff --git a/Semantics.Quantities/Chemical/SurfaceTension.cs b/Semantics.Quantities/Chemical/SurfaceTension.cs deleted file mode 100644 index 4df9d9d..0000000 --- a/Semantics.Quantities/Chemical/SurfaceTension.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents surface tension with a specific unit of measurement. -/// Surface tension is the force per unit length acting on the surface of a liquid. -/// -/// The numeric type for the surface tension value. -public sealed record SurfaceTension : PhysicalQuantity, T> - where T : struct, INumber, IFloatingPoint -{ - /// Gets the physical dimension of surface tension [M T⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.SurfaceTension; - - /// Initializes a new instance of the class. - public SurfaceTension() : base() { } - - /// Calculates surface tension from force and length. - /// Force acting on the surface. - /// Length over which the force acts. - /// The surface tension. - public static SurfaceTension FromForceAndLength(Force force, Length length) - { - Ensure.NotNull(force); - Ensure.NotNull(length); - - T forceValue = force.In(Units.Newton); - T lengthValue = length.In(Units.Meter); - T tension = forceValue / lengthValue; - return Create(tension); - } - - /// Calculates surface energy from surface tension and area change. - /// Change in surface area. - /// The surface energy. - public Energy CalculateSurfaceEnergy(Area areaChange) - { - Ensure.NotNull(areaChange); - - T tensionValue = In(Units.NewtonPerMeter); - T areaValue = areaChange.In(Units.SquareMeter); - T energy = tensionValue * areaValue; - return Energy.Create(energy); - } - - /// Common surface tension values for various liquids. - public static class CommonValues - { - /// Water at 20°C: 0.0728 N/m. - public static SurfaceTension Water => Create(T.CreateChecked(0.0728)); - - /// Mercury at 20°C: 0.486 N/m. - public static SurfaceTension Mercury => Create(T.CreateChecked(0.486)); - - /// Ethanol at 20°C: 0.0223 N/m. - public static SurfaceTension Ethanol => Create(T.CreateChecked(0.0223)); - - /// Benzene at 20°C: 0.0289 N/m. - public static SurfaceTension Benzene => Create(T.CreateChecked(0.0289)); - - /// Olive oil at 20°C: 0.032 N/m. - public static SurfaceTension OliveOil => Create(T.CreateChecked(0.032)); - - /// Soap solution: ~0.025 N/m (reduced from water). - public static SurfaceTension SoapSolution => Create(T.CreateChecked(0.025)); - } - - /// Calculates capillary rise height using Young-Laplace equation. - /// Contact angle with the surface. - /// Radius of the capillary tube. - /// Density of the liquid. - /// Gravitational acceleration. - /// Height of capillary rise. - public Length CalculateCapillaryRise(T contactAngle, Length tubeRadius, - Density liquidDensity, Acceleration gravity) - { - Ensure.NotNull(tubeRadius); - Ensure.NotNull(liquidDensity); - Ensure.NotNull(gravity); - - T gamma = In(Units.NewtonPerMeter); - T cosTheta = T.CreateChecked(Math.Cos(double.CreateChecked(contactAngle))); - T r = tubeRadius.In(Units.Meter); - T rho = liquidDensity.In(Units.Gram); // kg/m³ - T g = gravity.In(Units.MetersPerSecondSquared); - - T height = T.CreateChecked(2) * gamma * cosTheta / (rho * g * r); - return Length.Create(height); - } -} diff --git a/Semantics.Quantities/Chemical/pH.cs b/Semantics.Quantities/Chemical/pH.cs deleted file mode 100644 index 5181efc..0000000 --- a/Semantics.Quantities/Chemical/pH.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents pH (potential of Hydrogen) with logarithmic scale. -/// pH is a measure of acidity or alkalinity on a scale from 0-14. -/// -/// The numeric type for the pH value. -public sealed record PH : PhysicalQuantity, T> - where T : struct, INumber, IFloatingPoint -{ - /// Gets the physical dimension of pH [1] (dimensionless). - public override PhysicalDimension Dimension => PhysicalDimensions.pH; - - /// Initializes a new instance of the class. - public PH() : base() { } - - /// Calculates pH from hydrogen ion concentration. - /// Hydrogen ion concentration in mol/L. - /// The pH value. - public static PH FromHydrogenConcentration(Concentration hydrogenConcentration) - { - Ensure.NotNull(hydrogenConcentration); - - T concentration = hydrogenConcentration.In(Units.Molar); - T logValue = T.CreateChecked(Math.Log10(double.CreateChecked(concentration))); - T pHValue = -logValue; - return Create(pHValue); - } - - /// Calculates hydrogen ion concentration from pH. - /// The hydrogen ion concentration in mol/L. - public Concentration ToHydrogenConcentration() - { - T pHValue = In(Units.Radian); // dimensionless - T concentration = T.CreateChecked(Math.Pow(10.0, double.CreateChecked(-pHValue))); - return Concentration.Create(concentration); - } - - /// Calculates pOH from pH: pH + pOH = 14 at 25°C. - /// The pOH value. - public PH ToPOH() - { - T pHValue = In(Units.Radian); - T kw = PhysicalConstants.Generic.WaterIonProduct(); - T pOHValue = kw - pHValue; - return Create(pOHValue); - } - - /// Common pH values for reference. - public static class CommonValues - { - /// Pure water at 25°C: pH 7.0 (neutral). - public static PH NeutralWater => Create(PhysicalConstants.Generic.NeutralPH()); - - /// Battery acid: pH ~0.0 (extremely acidic). - public static PH BatteryAcid => Create(T.CreateChecked(0.0)); - - /// Lemon juice: pH ~2.0 (very acidic). - public static PH LemonJuice => Create(T.CreateChecked(2.0)); - - /// Coffee: pH ~5.0 (acidic). - public static PH Coffee => Create(T.CreateChecked(5.0)); - - /// Baking soda: pH ~9.0 (basic). - public static PH BakingSoda => Create(T.CreateChecked(9.0)); - - /// Household ammonia: pH ~11.0 (very basic). - public static PH Ammonia => Create(T.CreateChecked(11.0)); - - /// Household bleach: pH ~12.0 (very basic). - public static PH Bleach => Create(T.CreateChecked(12.0)); - - /// Drain cleaner: pH ~14.0 (extremely basic). - public static PH DrainCleaner => Create(T.CreateChecked(14.0)); - } - - /// Determines if the solution is acidic (pH < 7). - /// True if acidic, false otherwise. - public bool IsAcidic() - { - T pHValue = In(Units.Radian); - T neutral = PhysicalConstants.Generic.NeutralPH(); - return pHValue < neutral; - } - - /// Determines if the solution is basic/alkaline (pH > 7). - /// True if basic, false otherwise. - public bool IsBasic() - { - T pHValue = In(Units.Radian); - T neutral = PhysicalConstants.Generic.NeutralPH(); - return pHValue > neutral; - } - - /// Determines if the solution is neutral (pH = 7). - /// True if neutral, false otherwise. - public bool IsNeutral() - { - T pHValue = In(Units.Radian); - T neutral = PhysicalConstants.Generic.NeutralPH(); - T tolerance = T.CreateChecked(0.01); - return T.Abs(pHValue - neutral) < tolerance; - } -} diff --git a/Semantics.Quantities/Chemistry/PH.cs b/Semantics.Quantities/Chemistry/PH.cs new file mode 100644 index 0000000..6d1d82f --- /dev/null +++ b/Semantics.Quantities/Chemistry/PH.cs @@ -0,0 +1,30 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Bespoke members of ; the logarithmic core (including the +/// conversions) is generated from logarithmic.json. +/// +public readonly partial record struct PH + where T : struct, INumber +{ + /// Gets the pH of pure water at 25 °C (7.0). + public static PH Neutral => new(PhysicalConstants.Generic.NeutralPH()); + + /// + /// Gets the complementary pOH at 25 °C using pOH = 14 − pH. + /// + /// The pOH as a scale value. + public PH ToPOH() => new(T.CreateChecked(14) - Value); + + /// Gets whether this value describes an acid (pH below 7). + public bool IsAcidic => Value < PhysicalConstants.Generic.NeutralPH(); + + /// Gets whether this value describes a base (pH above 7). + public bool IsBasic => Value > PhysicalConstants.Generic.NeutralPH(); +} diff --git a/Semantics.Quantities/Core/BootstrapUnit.cs b/Semantics.Quantities/Core/BootstrapUnit.cs deleted file mode 100644 index f760e69..0000000 --- a/Semantics.Quantities/Core/BootstrapUnit.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -/// -/// A bootstrap unit used during initialization to break circular dependencies. -/// This unit is replaced with the actual unit after the Units class is initialized. -/// -/// The name of the unit. -/// The symbol of the unit. -internal sealed class BootstrapUnit(string name, string symbol) : IUnit -{ - /// - public string Name { get; } = name; - - /// - public string Symbol { get; } = symbol; - - /// - public PhysicalDimension Dimension => default; // Will be set properly later - - /// - public UnitSystem System => UnitSystem.SIDerived; - - /// - public double ToBaseFactor => 1.0; - - /// - public double ToBaseOffset => 0.0; - - /// - public bool Equals(IUnit? other) => ReferenceEquals(this, other); - - /// - public override bool Equals(object? obj) => obj is IUnit other && Equals(other); - - /// - public override int GetHashCode() => HashCode.Combine(Name, Symbol); -} diff --git a/Semantics.Quantities/Core/BootstrapUnits.cs b/Semantics.Quantities/Core/BootstrapUnits.cs deleted file mode 100644 index 7c644b5..0000000 --- a/Semantics.Quantities/Core/BootstrapUnits.cs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -/// -/// Internal registry of bootstrap units used during initialization to break circular dependencies. -/// These units are replaced with actual units after the Units class is initialized. -/// -internal static class BootstrapUnits -{ - #region Fundamental SI Base Units - /// Bootstrap unit for radian angle measurement. - internal static readonly BootstrapUnit Radian = new("radian", "rad"); - - /// Bootstrap unit for the SI base unit of length. - internal static readonly BootstrapUnit Meter = new("meter", "m"); - - /// Bootstrap unit for the SI base unit of mass. - internal static readonly BootstrapUnit Kilogram = new("kilogram", "kg"); - - /// Bootstrap unit for the SI base unit of time. - internal static readonly BootstrapUnit Second = new("second", "s"); - - /// Bootstrap unit for the SI base unit of electric current. - internal static readonly BootstrapUnit Ampere = new("ampere", "A"); - - /// Bootstrap unit for the SI base unit of temperature. - internal static readonly BootstrapUnit Kelvin = new("kelvin", "K"); - - /// Bootstrap unit for the SI base unit of amount of substance. - internal static readonly BootstrapUnit Mole = new("mole", "mol"); - - /// Bootstrap unit for the SI base unit of luminous intensity. - internal static readonly BootstrapUnit Candela = new("candela", "cd"); - #endregion - - #region Dimensionless and Special Units - /// Bootstrap unit for dimensionless quantities. - internal static readonly BootstrapUnit Dimensionless = new("dimensionless", "1"); - - /// Bootstrap unit for decibel measurements. - internal static readonly BootstrapUnit Decibel = new("decibel", "dB"); - - /// Bootstrap unit for pH scale measurements. - internal static readonly BootstrapUnit PH = new("pH", "pH"); - - /// Bootstrap unit for coefficient measurements. - internal static readonly BootstrapUnit Coefficient = new("coefficient", "coeff"); - #endregion - - #region Additional Bootstrap Units for Circular Dependencies - /// Bootstrap unit for mass in grams. - internal static readonly BootstrapUnit Gram = new("gram", "g"); - - /// Bootstrap unit for molar concentration. - internal static readonly BootstrapUnit Molar = new("molar", "M"); - - /// Bootstrap unit for luminous flux. - internal static readonly BootstrapUnit Lumen = new("lumen", "lm"); - - /// Bootstrap unit for illuminance. - internal static readonly BootstrapUnit Lux = new("lux", "lx"); - - /// Bootstrap unit for radioactive activity. - internal static readonly BootstrapUnit Becquerel = new("becquerel", "Bq"); - - /// Bootstrap unit for absorbed dose. - internal static readonly BootstrapUnit Gray = new("gray", "Gy"); - - /// Bootstrap unit for equivalent dose. - internal static readonly BootstrapUnit Sievert = new("sievert", "Sv"); - - /// Bootstrap unit for nuclear cross section. - internal static readonly BootstrapUnit Barn = new("barn", "b"); - - /// Bootstrap unit for optical power. - internal static readonly BootstrapUnit Diopter = new("diopter", "D"); - #endregion - - #region Derived SI Units Bootstrap - /// Bootstrap unit for area. - internal static readonly BootstrapUnit SquareMeter = new("square meter", "m²"); - - /// Bootstrap unit for volume. - internal static readonly BootstrapUnit CubicMeter = new("cubic meter", "m³"); - - /// Bootstrap unit for velocity. - internal static readonly BootstrapUnit MetersPerSecond = new("meters per second", "m/s"); - - /// Bootstrap unit for acceleration. - internal static readonly BootstrapUnit MetersPerSecondSquared = new("meters per second squared", "m/s²"); - - /// Bootstrap unit for force. - internal static readonly BootstrapUnit Newton = new("newton", "N"); - - /// Bootstrap unit for pressure. - internal static readonly BootstrapUnit Pascal = new("pascal", "Pa"); - - /// Bootstrap unit for energy. - internal static readonly BootstrapUnit Joule = new("joule", "J"); - - /// Bootstrap unit for power. - internal static readonly BootstrapUnit Watt = new("watt", "W"); - - /// Bootstrap unit for electric potential. - internal static readonly BootstrapUnit Volt = new("volt", "V"); - - /// Bootstrap unit for electric resistance. - internal static readonly BootstrapUnit Ohm = new("ohm", "Ω"); - - /// Bootstrap unit for electric charge. - internal static readonly BootstrapUnit Coulomb = new("coulomb", "C"); - - /// Bootstrap unit for electric capacitance. - internal static readonly BootstrapUnit Farad = new("farad", "F"); - - /// Bootstrap unit for frequency. - internal static readonly BootstrapUnit Hertz = new("hertz", "Hz"); - #endregion -} diff --git a/Semantics.Quantities/Core/IPhysicalQuantity.cs b/Semantics.Quantities/Core/IPhysicalQuantity.cs deleted file mode 100644 index 41d880a..0000000 --- a/Semantics.Quantities/Core/IPhysicalQuantity.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Base interface for all physical quantities with compile-time type safety and dimensional validation. -/// -/// The storage type for the quantity value (e.g., double, float, decimal). -public interface IPhysicalQuantity : ISemanticQuantity, IEquatable>, IComparable> - where T : struct, INumber -{ - /// Gets the physical dimension of this quantity. - public PhysicalDimension Dimension { get; } - - /// Converts the quantity value to the specified unit. - /// The target unit for conversion. - /// The value in the target unit. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1716:Identifiers should not match keywords", Justification = "")] - public T In(IUnit targetUnit); - - /// Gets whether this quantity satisfies physical constraints (e.g., positive temperature above absolute zero). - public bool IsPhysicallyValid { get; } -} diff --git a/Semantics.Quantities/Core/IUnit.cs b/Semantics.Quantities/Core/IUnit.cs deleted file mode 100644 index e338e98..0000000 --- a/Semantics.Quantities/Core/IUnit.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -/// -/// Interface for all physical units. -/// -public interface IUnit -{ - /// Gets the full name of the unit. - public string Name { get; } - - /// Gets the symbol/abbreviation of the unit. - public string Symbol { get; } - - /// Gets the physical dimension this unit measures. - public PhysicalDimension Dimension { get; } - - /// Gets the unit system this unit belongs to. - public UnitSystem System { get; } - - /// Gets the multiplication factor to convert to the base unit. - public double ToBaseFactor { get; } - - /// Gets the offset to add when converting to the base unit (0.0 for linear units). - public double ToBaseOffset { get; } -} diff --git a/Semantics.Quantities/Core/MetricMagnitudes.cs b/Semantics.Quantities/Core/MetricMagnitudes.cs deleted file mode 100644 index 92aa0d4..0000000 --- a/Semantics.Quantities/Core/MetricMagnitudes.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -/// -/// Standard metric magnitude prefixes. -/// -public static class MetricMagnitudes -{ - /// Yotta (10²⁴) - public const double Yotta = 1e24; - /// Zetta (10²¹) - public const double Zetta = 1e21; - /// Exa (10¹⁸) - public const double Exa = 1e18; - /// Peta (10¹⁵) - public const double Peta = 1e15; - /// Tera (10¹²) - public const double Tera = 1e12; - /// Giga (10⁹) - public const double Giga = 1e9; - /// Mega (10⁶) - public const double Mega = 1e6; - /// Kilo (10³) - public const double Kilo = 1e3; - /// Hecto (10²) - public const double Hecto = 1e2; - /// Deca (10¹) - public const double Deca = 1e1; - /// Deci (10⁻¹) - public const double Deci = 1e-1; - /// Centi (10⁻²) - public const double Centi = 1e-2; - /// Milli (10⁻³) - public const double Milli = 1e-3; - /// Micro (10⁻⁶) - public const double Micro = 1e-6; - /// Nano (10⁻⁹) - public const double Nano = 1e-9; - /// Pico (10⁻¹²) - public const double Pico = 1e-12; - /// Femto (10⁻¹⁵) - public const double Femto = 1e-15; - /// Atto (10⁻¹⁸) - public const double Atto = 1e-18; - /// Zepto (10⁻²¹) - public const double Zepto = 1e-21; - /// Yocto (10⁻²⁴) - public const double Yocto = 1e-24; -} diff --git a/Semantics.Quantities/Core/PhysicalConstants.cs b/Semantics.Quantities/Core/PhysicalConstants.cs deleted file mode 100644 index c8185a3..0000000 --- a/Semantics.Quantities/Core/PhysicalConstants.cs +++ /dev/null @@ -1,504 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Provides fundamental physical constants used throughout the Semantics library. -/// All values are based on the 2019 redefinition of SI base units and CODATA 2018 values. -/// -public static class PhysicalConstants -{ - /// - /// Fundamental constants - /// - public static class Fundamental - { - /// Avogadro's number: 6.02214076 × 10²³ entities/mol (exact, SI defining constant) - public const double AvogadroNumber = 6.02214076e23; - - /// Gas constant: 8.31446261815324 J/(mol·K) (exact, derived from Avogadro and Boltzmann constants) - public const double GasConstant = 8.31446261815324; - - /// Elementary charge: 1.602176634 × 10⁻¹⁹ C (exact, SI defining constant) - public const double ElementaryCharge = 1.602176634e-19; - - /// Speed of light in vacuum: 299,792,458 m/s (exact, SI defining constant) - public const double SpeedOfLight = 299792458.0; - - /// Planck constant: 6.62607015 × 10⁻³⁴ J·s (exact, SI defining constant) - public const double PlanckConstant = 6.62607015e-34; - - /// Boltzmann constant: 1.380649 × 10⁻²³ J/K (exact, SI defining constant) - public const double BoltzmannConstant = 1.380649e-23; - } - - /// - /// Temperature-related constants - /// - public static class Temperature - { - /// Absolute zero in Celsius: 273.15 K (exact by definition) - public const double AbsoluteZeroInCelsius = 273.15; - - /// Water triple point: 273.16 K (exact by definition) - public const double WaterTriplePoint = 273.16; - - /// Standard temperature (STP): 273.15 K (0°C) - public const double StandardTemperature = 273.15; - - /// Water boiling point at 1 atm: 373.15 K (100°C) - public const double WaterBoilingPoint = 373.15; - } - - /// - /// Chemical constants - /// - public static class Chemical - { - /// Water ion product (Kw) at 25°C: 1.0 × 10⁻¹⁴ (pKw = 14.0) - public const double WaterIonProduct = 14.0; // pKw value - - /// Molar volume of ideal gas at STP: 22.413969545014137 L/mol (calculated from R*T/P at 273.15K, 101325Pa) - public const double MolarVolumeSTP = 22.413969545014137; - - /// Natural logarithm of 2: 0.6931471805599453 - public const double Ln2 = 0.6931471805599453; - - /// Neutral pH value at 25°C: 7.0 - public const double NeutralPH = 7.0; - } - - /// - /// Mechanical constants - /// - public static class Mechanical - { - /// Standard gravitational acceleration: 9.80665 m/s² (exact by definition) - public const double StandardGravity = 9.80665; - - /// Standard atmospheric pressure: 101,325 Pa (exact by definition) - public const double StandardAtmosphericPressure = 101325.0; - - /// Pound mass to kilogram: 0.453592 kg/lb (exact) - public const double PoundMassToKilogram = 0.453592; - } - - /// - /// Optical constants - /// - public static class Optical - { - /// Luminous efficacy of monochromatic radiation at 540 THz: 683 lm/W (exact by definition) - public const double LuminousEfficacy = 683.0; - } - - /// - /// Nuclear constants - /// - public static class Nuclear - { - /// Atomic mass unit: 1.66053906660×10⁻²⁷ kg (2018 CODATA) - public const double AtomicMassUnit = 1.66053906660e-27; - - /// Nuclear magneton: 5.0507837461×10⁻²⁷ J/T (2018 CODATA) - public const double NuclearMagneton = 5.0507837461e-27; - } - - /// - /// Fluid dynamics constants - /// - public static class FluidDynamics - { - /// Standard air density at 15°C and 1 atm: 1.225 kg/m³ (ISO 2533) - public const double StandardAirDensity = 1.225; - - /// Water surface tension at 20°C: 0.0728 N/m (NIST) - public const double WaterSurfaceTension = 0.0728; - } - - /// - /// Conversion factors for unit systems - /// - public static class Conversion - { - /// Celsius to Fahrenheit slope: 9/5 = 1.8 - public const double CelsiusToFahrenheitSlope = 1.8; - - /// Fahrenheit to Celsius slope: 5/9 ≈ 0.5555555555555556 - public const double FahrenheitToCelsiusSlope = 5.0 / 9.0; - - /// Fahrenheit offset: 32°F - public const double FahrenheitOffset = 32.0; - - /// Calorie to Joule: 4.184 J/cal (thermochemical calorie) - public const double CalorieToJoule = 4.184; - - /// BTU to Joule: 1055.05585262 J/BTU (International Table BTU) - public const double BtuToJoule = 1055.05585262; - - /// Feet to meters: 0.3048 m/ft (exact) - public const double FeetToMeters = 0.3048; - - /// Inches to meters: 0.0254 m/in (exact) - public const double InchesToMeters = 0.0254; - - /// Yards to meters: 0.9144 m/yd (exact) - public const double YardsToMeters = 0.9144; - - /// Miles to meters: 1609.344 m/mi (exact) - public const double MilesToMeters = 1609.344; - - /// Nautical miles to meters: 1852 m/nmi (exact) - public const double NauticalMilesToMeters = 1852.0; - - /// Square feet to square meters: 0.092903 m²/ft² (derived from feet conversion) - public const double SquareFeetToSquareMeters = FeetToMeters * FeetToMeters; - - /// Square inches to square meters: 0.00064516 m²/in² (derived from inches conversion) - public const double SquareInchesToSquareMeters = InchesToMeters * InchesToMeters; - - /// Acres to square meters: 4046.8564224 m²/ac (exact) - public const double AcresToSquareMeters = 4046.8564224; - - /// Square miles to square meters: 2589988.110336 m²/mi² (derived from miles conversion) - public const double SquareMilesToSquareMeters = MilesToMeters * MilesToMeters; - - /// Kilowatt-hour to Joule: 3,600,000 J/kWh (exact) - public const double KilowattHourToJoule = 3600000.0; - - /// BTU per hour-foot-Fahrenheit to Watts per meter-Kelvin: 1.7307 W/(m·K) per BTU/(h·ft·°F) - public const double BtuPerHourFootFahrenheitToWattsPerMeterKelvin = 1.7307; - - /// Thermal resistance: Fahrenheit-hour per BTU to Kelvin per Watt: 1.8956 K/W per °F·h/BTU - public const double FahrenheitHourPerBtuToKelvinPerWatt = 1.8956; - - /// Specific heat: BTU per pound-Fahrenheit to Joules per kilogram-Kelvin: 4186.8 J/(kg·K) per BTU/(lb·°F) - public const double BtuPerPoundFahrenheitToJoulesPerKilogramKelvin = 4186.8; - - /// Heat transfer coefficient: BTU per hour-square foot-Fahrenheit to Watts per square meter-Kelvin: 5.678 W/(m²·K) per BTU/(h·ft²·°F) - public const double BtuPerHourSquareFootFahrenheitToWattsPerSquareMeterKelvin = 5.678; - - /// Heat capacity: BTU per Fahrenheit to Joules per Kelvin: 1899.1 J/K per BTU/°F - public const double BtuPerFahrenheitToJoulesPerKelvin = 1899.1; - - /// Illuminance: foot-candles to lux: 10.764 lux per fc - public const double FootCandlesToLux = 10.764; - - /// Luminance: foot-lamberts to candelas per square meter: 3.426 cd/m² per fL - public const double FootLambertsToCandelasPerSquareMeter = 3.426; - - /// Absorbed dose: rads to grays: 0.01 Gy per rad - public const double RadsToGrays = 0.01; - - /// Equivalent dose: rems to sieverts: 0.01 Sv per rem - public const double RemsToSieverts = 0.01; - - /// Nuclear exposure: roentgens to absorbed dose in air: 0.00876 Gy per R - public const double RoentgensToGraysInAir = 0.00876; - } - - /// - /// Acoustic constants - /// - public static class Acoustic - { - /// Reference sound pressure: 20 × 10⁻⁶ Pa (threshold of hearing) - public const double ReferenceSoundPressure = 20e-6; - - /// Reference sound intensity: 1 × 10⁻¹² W/m² (threshold of hearing) - public const double ReferenceSoundIntensity = 1e-12; - - /// Reference sound power: 1 × 10⁻¹² W - public const double ReferenceSoundPower = 1e-12; - - /// Sabine reverberation constant: 0.161 m/s - public const double SabineConstant = 0.161; - } - - /// - /// Mathematical constants commonly used in physics - /// - public static class Mathematical - { - /// Natural logarithm of 10: 2.302585092994046 - public const double Ln10 = 2.302585092994046; - - /// Base-10 logarithm of e: 0.4342944819032518 - public const double Log10E = 0.4342944819032518; - - /// One half: 0.5 (commonly used in kinetic energy equations) - public const double OneHalf = 0.5; - - /// Two thirds: 2/3 ≈ 0.6666666666666666 (used in various physics equations) - public const double TwoThirds = 2.0 / 3.0; - - /// Three halves: 3/2 = 1.5 (used in kinetic theory) - public const double ThreeHalves = 1.5; - - /// Four thirds: 4/3 ≈ 1.3333333333333333 (used in sphere volume) - public const double FourThirds = 4.0 / 3.0; - } - - /// - /// Time conversion constants - /// - public static class Time - { - /// Seconds per minute: 60 s/min (exact) - public const double SecondsPerMinute = 60.0; - - /// Seconds per hour: 3600 s/h (exact) - public const double SecondsPerHour = 3600.0; - - /// Seconds per day: 86400 s/d (exact) - public const double SecondsPerDay = 86400.0; - - /// Seconds per week: 604800 s/wk (exact) - public const double SecondsPerWeek = 604800.0; - - /// Seconds per Julian year: 31557600 s/yr (365.25 days) - public const double SecondsPerJulianYear = 31557600.0; - - /// Minutes per hour: 60 min/h (exact) - public const double MinutesPerHour = 60.0; - - /// Hours per day: 24 h/d (exact) - public const double HoursPerDay = 24.0; - - /// Days per week: 7 d/wk (exact) - public const double DaysPerWeek = 7.0; - - /// Days per Julian year: 365.25 d/yr (exact) - public const double DaysPerJulianYear = 365.25; - } - - /// - /// Helper methods to get constants as generic numeric types - /// - public static class Generic - { - /// Gets Avogadro's number as type T - public static T AvogadroNumber() where T : struct, INumber => T.CreateChecked(Fundamental.AvogadroNumber); - - /// Gets gas constant as type T - public static T GasConstant() where T : struct, INumber => T.CreateChecked(Fundamental.GasConstant); - - /// Gets elementary charge as type T - public static T ElementaryCharge() where T : struct, INumber => T.CreateChecked(Fundamental.ElementaryCharge); - - /// Gets Planck constant as type T - public static T PlanckConstant() where T : struct, INumber => T.CreateChecked(Fundamental.PlanckConstant); - - /// Gets Boltzmann constant as type T - public static T BoltzmannConstant() where T : struct, INumber => T.CreateChecked(Fundamental.BoltzmannConstant); - - /// Gets absolute zero in Celsius as type T - public static T AbsoluteZeroInCelsius() where T : struct, INumber => T.CreateChecked(Temperature.AbsoluteZeroInCelsius); - - /// Gets water ion product (pKw) as type T - public static T WaterIonProduct() where T : struct, INumber => T.CreateChecked(Chemical.WaterIonProduct); - - /// Gets molar volume at STP as type T - public static T MolarVolumeSTP() where T : struct, INumber => T.CreateChecked(Chemical.MolarVolumeSTP); - - /// Gets natural logarithm of 2 as type T - public static T Ln2() where T : struct, INumber => T.CreateChecked(Chemical.Ln2); - - /// Gets neutral pH as type T - public static T NeutralPH() where T : struct, INumber => T.CreateChecked(Chemical.NeutralPH); - - /// Gets standard gravitational acceleration as type T - public static T StandardGravity() where T : struct, INumber => T.CreateChecked(Mechanical.StandardGravity); - - /// Gets standard atmospheric pressure as type T - public static T StandardAtmosphericPressure() where T : struct, INumber => T.CreateChecked(Mechanical.StandardAtmosphericPressure); - - /// Gets pound mass to kilogram conversion as type T - public static T PoundMassToKilogram() where T : struct, INumber => T.CreateChecked(Mechanical.PoundMassToKilogram); - - /// Gets Celsius to Fahrenheit slope as type T - public static T CelsiusToFahrenheitSlope() where T : struct, INumber => T.CreateChecked(Conversion.CelsiusToFahrenheitSlope); - - /// Gets Fahrenheit to Celsius slope as type T - public static T FahrenheitToCelsiusSlope() where T : struct, INumber => T.CreateChecked(Conversion.FahrenheitToCelsiusSlope); - - /// Gets Fahrenheit offset as type T - public static T FahrenheitOffset() where T : struct, INumber => T.CreateChecked(Conversion.FahrenheitOffset); - - /// Gets calorie to joule conversion as type T - public static T CalorieToJoule() where T : struct, INumber => T.CreateChecked(Conversion.CalorieToJoule); - - /// Gets BTU to joule conversion as type T - public static T BtuToJoule() where T : struct, INumber => T.CreateChecked(Conversion.BtuToJoule); - - /// Gets feet to meters conversion as type T - public static T FeetToMeters() where T : struct, INumber => T.CreateChecked(Conversion.FeetToMeters); - - /// Gets inches to meters conversion as type T - public static T InchesToMeters() where T : struct, INumber => T.CreateChecked(Conversion.InchesToMeters); - - /// Gets yards to meters conversion as type T - public static T YardsToMeters() where T : struct, INumber => T.CreateChecked(Conversion.YardsToMeters); - - /// Gets miles to meters conversion as type T - public static T MilesToMeters() where T : struct, INumber => T.CreateChecked(Conversion.MilesToMeters); - - /// Gets nautical miles to meters conversion as type T - public static T NauticalMilesToMeters() where T : struct, INumber => T.CreateChecked(Conversion.NauticalMilesToMeters); - - /// Gets square feet to square meters conversion as type T - public static T SquareFeetToSquareMeters() where T : struct, INumber => T.CreateChecked(Conversion.SquareFeetToSquareMeters); - - /// Gets square inches to square meters conversion as type T - public static T SquareInchesToSquareMeters() where T : struct, INumber => T.CreateChecked(Conversion.SquareInchesToSquareMeters); - - /// Gets acres to square meters conversion as type T - public static T AcresToSquareMeters() where T : struct, INumber => T.CreateChecked(Conversion.AcresToSquareMeters); - - /// Gets square miles to square meters conversion as type T - public static T SquareMilesToSquareMeters() where T : struct, INumber => T.CreateChecked(Conversion.SquareMilesToSquareMeters); - - /// Gets kilowatt-hour to joule conversion as type T - public static T KilowattHourToJoule() where T : struct, INumber => T.CreateChecked(Conversion.KilowattHourToJoule); - - /// Gets BTU per hour-foot-Fahrenheit to Watts per meter-Kelvin conversion as type T - public static T BtuPerHourFootFahrenheitToWattsPerMeterKelvin() where T : struct, INumber => T.CreateChecked(Conversion.BtuPerHourFootFahrenheitToWattsPerMeterKelvin); - - /// Gets water triple point temperature as type T - public static T WaterTriplePoint() where T : struct, INumber => T.CreateChecked(Temperature.WaterTriplePoint); - - /// Gets standard temperature as type T - public static T StandardTemperature() where T : struct, INumber => T.CreateChecked(Temperature.StandardTemperature); - - /// Gets water boiling point as type T - public static T WaterBoilingPoint() where T : struct, INumber => T.CreateChecked(Temperature.WaterBoilingPoint); - - /// Gets reference sound pressure as type T - public static T ReferenceSoundPressure() where T : struct, INumber => T.CreateChecked(Acoustic.ReferenceSoundPressure); - - /// Gets reference sound intensity as type T - public static T ReferenceSoundIntensity() where T : struct, INumber => T.CreateChecked(Acoustic.ReferenceSoundIntensity); - - /// Gets reference sound power as type T - public static T ReferenceSoundPower() where T : struct, INumber => T.CreateChecked(Acoustic.ReferenceSoundPower); - - /// Gets Sabine reverberation constant as type T - public static T SabineConstant() where T : struct, INumber => T.CreateChecked(Acoustic.SabineConstant); - - // === OPTICAL CONSTANTS === - - /// Gets the luminous efficacy of monochromatic radiation at 540 THz. - /// The numeric type. - /// The luminous efficacy (683 lm/W) as type T. - public static T LuminousEfficacy() where T : struct, INumber => T.CreateChecked(Optical.LuminousEfficacy); - - /// Gets the speed of light in vacuum. - /// The numeric type. - /// The speed of light (299,792,458 m/s) as type T. - public static T SpeedOfLight() where T : struct, INumber => T.CreateChecked(Fundamental.SpeedOfLight); - - // === NUCLEAR CONSTANTS === - - /// Gets the atomic mass unit. - /// The numeric type. - /// The atomic mass unit (1.66053906660×10⁻²⁷ kg) as type T. - public static T AtomicMassUnit() where T : struct, INumber => T.CreateChecked(Nuclear.AtomicMassUnit); - - /// Gets the nuclear magneton. - /// The numeric type. - /// The nuclear magneton (5.0507837461×10⁻²⁷ J/T) as type T. - public static T NuclearMagneton() where T : struct, INumber => T.CreateChecked(Nuclear.NuclearMagneton); - - // === FLUID DYNAMICS CONSTANTS === - - /// Gets the standard air density. - /// The numeric type. - /// The standard air density (1.225 kg/m³) as type T. - public static T StandardAirDensity() where T : struct, INumber => T.CreateChecked(FluidDynamics.StandardAirDensity); - - /// Gets the water surface tension at 20°C. - /// The numeric type. - /// The water surface tension (0.0728 N/m) as type T. - public static T WaterSurfaceTension() where T : struct, INumber => T.CreateChecked(FluidDynamics.WaterSurfaceTension); - - /// Gets natural logarithm of 10 as type T - public static T Ln10() where T : struct, INumber => T.CreateChecked(Mathematical.Ln10); - - /// Gets base-10 logarithm of e as type T - public static T Log10E() where T : struct, INumber => T.CreateChecked(Mathematical.Log10E); - - /// Gets one half (0.5) as type T - public static T OneHalf() where T : struct, INumber => T.CreateChecked(Mathematical.OneHalf); - - /// Gets two thirds (2/3) as type T - public static T TwoThirds() where T : struct, INumber => T.CreateChecked(Mathematical.TwoThirds); - - /// Gets three halves (3/2) as type T - public static T ThreeHalves() where T : struct, INumber => T.CreateChecked(Mathematical.ThreeHalves); - - /// Gets four thirds (4/3) as type T - public static T FourThirds() where T : struct, INumber => T.CreateChecked(Mathematical.FourThirds); - - // === ADDITIONAL CONVERSION CONSTANTS === - - /// Gets thermal resistance conversion factor as type T - public static T FahrenheitHourPerBtuToKelvinPerWatt() where T : struct, INumber => T.CreateChecked(Conversion.FahrenheitHourPerBtuToKelvinPerWatt); - - /// Gets specific heat conversion factor as type T - public static T BtuPerPoundFahrenheitToJoulesPerKilogramKelvin() where T : struct, INumber => T.CreateChecked(Conversion.BtuPerPoundFahrenheitToJoulesPerKilogramKelvin); - - /// Gets heat transfer coefficient conversion factor as type T - public static T BtuPerHourSquareFootFahrenheitToWattsPerSquareMeterKelvin() where T : struct, INumber => T.CreateChecked(Conversion.BtuPerHourSquareFootFahrenheitToWattsPerSquareMeterKelvin); - - /// Gets heat capacity conversion factor as type T - public static T BtuPerFahrenheitToJoulesPerKelvin() where T : struct, INumber => T.CreateChecked(Conversion.BtuPerFahrenheitToJoulesPerKelvin); - - /// Gets illuminance conversion factor as type T - public static T FootCandlesToLux() where T : struct, INumber => T.CreateChecked(Conversion.FootCandlesToLux); - - /// Gets luminance conversion factor as type T - public static T FootLambertsToCandelasPerSquareMeter() where T : struct, INumber => T.CreateChecked(Conversion.FootLambertsToCandelasPerSquareMeter); - - /// Gets absorbed dose conversion factor as type T - public static T RadsToGrays() where T : struct, INumber => T.CreateChecked(Conversion.RadsToGrays); - - /// Gets equivalent dose conversion factor as type T - public static T RemsToSieverts() where T : struct, INumber => T.CreateChecked(Conversion.RemsToSieverts); - - /// Gets nuclear exposure conversion factor as type T - public static T RoentgensToGraysInAir() where T : struct, INumber => T.CreateChecked(Conversion.RoentgensToGraysInAir); - - // === TIME CONVERSION CONSTANTS === - - /// Gets seconds per minute as type T - public static T SecondsPerMinute() where T : struct, INumber => T.CreateChecked(Time.SecondsPerMinute); - - /// Gets seconds per hour as type T - public static T SecondsPerHour() where T : struct, INumber => T.CreateChecked(Time.SecondsPerHour); - - /// Gets seconds per day as type T - public static T SecondsPerDay() where T : struct, INumber => T.CreateChecked(Time.SecondsPerDay); - - /// Gets seconds per week as type T - public static T SecondsPerWeek() where T : struct, INumber => T.CreateChecked(Time.SecondsPerWeek); - - /// Gets seconds per Julian year as type T - public static T SecondsPerJulianYear() where T : struct, INumber => T.CreateChecked(Time.SecondsPerJulianYear); - - /// Gets minutes per hour as type T - public static T MinutesPerHour() where T : struct, INumber => T.CreateChecked(Time.MinutesPerHour); - - /// Gets hours per day as type T - public static T HoursPerDay() where T : struct, INumber => T.CreateChecked(Time.HoursPerDay); - - /// Gets days per week as type T - public static T DaysPerWeek() where T : struct, INumber => T.CreateChecked(Time.DaysPerWeek); - - /// Gets days per Julian year as type T - public static T DaysPerJulianYear() where T : struct, INumber => T.CreateChecked(Time.DaysPerJulianYear); - } -} diff --git a/Semantics.Quantities/Core/PhysicalDimension.cs b/Semantics.Quantities/Core/PhysicalDimension.cs deleted file mode 100644 index 128aa80..0000000 --- a/Semantics.Quantities/Core/PhysicalDimension.cs +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; -/// -/// Represents a physical dimension with powers of fundamental SI base quantities. -/// Provides compile-time dimensional safety and runtime dimensional analysis. -/// -/// -/// Initializes a new instance of the PhysicalDimension struct. -/// -/// Power of length dimension. -/// Power of mass dimension. -/// Power of time dimension. -/// Power of electric current dimension. -/// Power of temperature dimension. -/// Power of amount of substance dimension. -/// Power of luminous intensity dimension. -/// The SI base unit for this dimension. -[System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S107:Methods should not have too many parameters", Justification = "")] -public readonly struct PhysicalDimension( - IUnit baseUnit, - int length = 0, - int mass = 0, - int time = 0, - int electricCurrent = 0, - int temperature = 0, - int amountOfSubstance = 0, - int luminousIntensity = 0) : IEquatable -{ - /// The power of Length [L] in this dimension. - public int Length { get; } = length; - - /// The power of Mass [M] in this dimension. - public int Mass { get; } = mass; - - /// The power of Time [T] in this dimension. - public int Time { get; } = time; - - /// The power of Electric Current [I] in this dimension. - public int ElectricCurrent { get; } = electricCurrent; - - /// The power of Thermodynamic Temperature [Θ] in this dimension. - public int Temperature { get; } = temperature; - - /// The power of Amount of Substance [N] in this dimension. - public int AmountOfSubstance { get; } = amountOfSubstance; - - /// The power of Luminous Intensity [J] in this dimension. - public int LuminousIntensity { get; } = luminousIntensity; - - /// Gets whether this dimension is dimensionless (all powers are zero). - public bool IsDimensionless => Length == 0 && Mass == 0 && Time == 0 && ElectricCurrent == 0 && Temperature == 0 && AmountOfSubstance == 0 && LuminousIntensity == 0; - - /// Gets the SI base unit for this dimension. - public IUnit BaseUnit { get; } = baseUnit ?? throw new InvalidOperationException("Base unit must be specified for physical dimensions."); - - /// - /// Determines whether this dimension is equal to another dimension. - /// - /// The other dimension to compare. - /// True if the dimensions are equal, false otherwise. - public bool Equals(PhysicalDimension other) => - Length == other.Length && - Mass == other.Mass && - Time == other.Time && - ElectricCurrent == other.ElectricCurrent && - Temperature == other.Temperature && - AmountOfSubstance == other.AmountOfSubstance && - LuminousIntensity == other.LuminousIntensity; - - /// - /// Determines whether this dimension is equal to another object. - /// - /// The object to compare. - /// True if the objects are equal, false otherwise. - public override bool Equals(object? obj) => obj is PhysicalDimension other && Equals(other); - - /// - /// Gets the hash code for this dimension. - /// - /// A hash code for this dimension. - public override int GetHashCode() => HashCode.Combine(Length, Mass, Time, ElectricCurrent, Temperature, AmountOfSubstance, LuminousIntensity); - - /// - /// Determines whether two dimensions are equal. - /// - /// The first dimension. - /// The second dimension. - /// True if the dimensions are equal, false otherwise. - public static bool operator ==(PhysicalDimension left, PhysicalDimension right) => left.Equals(right); - - /// - /// Determines whether two dimensions are not equal. - /// - /// The first dimension. - /// The second dimension. - /// True if the dimensions are not equal, false otherwise. - public static bool operator !=(PhysicalDimension left, PhysicalDimension right) => !left.Equals(right); - - /// - /// Multiplies two dimensions (adds their powers). - /// - /// The first dimension. - /// The second dimension. - /// The product dimension. - public static PhysicalDimension operator *(PhysicalDimension left, PhysicalDimension right) => - new(left.BaseUnit, - left.Length + right.Length, - left.Mass + right.Mass, - left.Time + right.Time, - left.ElectricCurrent + right.ElectricCurrent, - left.Temperature + right.Temperature, - left.AmountOfSubstance + right.AmountOfSubstance, - left.LuminousIntensity + right.LuminousIntensity); - - /// - /// Divides two dimensions (subtracts their powers). - /// - /// The numerator dimension. - /// The denominator dimension. - /// The quotient dimension. - public static PhysicalDimension operator /(PhysicalDimension left, PhysicalDimension right) => - new(left.BaseUnit, - left.Length - right.Length, - left.Mass - right.Mass, - left.Time - right.Time, - left.ElectricCurrent - right.ElectricCurrent, - left.Temperature - right.Temperature, - left.AmountOfSubstance - right.AmountOfSubstance, - left.LuminousIntensity - right.LuminousIntensity); - - /// - /// Raises a dimension to a power. - /// - /// The dimension to raise. - /// The power to raise to. - /// The dimension raised to the power. - public static PhysicalDimension Pow(PhysicalDimension dimension, int power) => - new(dimension.BaseUnit, - dimension.Length * power, - dimension.Mass * power, - dimension.Time * power, - dimension.ElectricCurrent * power, - dimension.Temperature * power, - dimension.AmountOfSubstance * power, - dimension.LuminousIntensity * power); - - /// - /// Returns a string representation of this dimension using standard notation. - /// - /// A string like "L² M T⁻¹" or "1" for dimensionless. - public override string ToString() - { - if (IsDimensionless) - { - return "1"; - } - - List parts = []; - - if (Length != 0) - { - parts.Add(FormatDimension("L", Length)); - } - - if (Mass != 0) - { - parts.Add(FormatDimension("M", Mass)); - } - - if (Time != 0) - { - parts.Add(FormatDimension("T", Time)); - } - - if (ElectricCurrent != 0) - { - parts.Add(FormatDimension("I", ElectricCurrent)); - } - - if (Temperature != 0) - { - parts.Add(FormatDimension("Θ", Temperature)); - } - - if (AmountOfSubstance != 0) - { - parts.Add(FormatDimension("N", AmountOfSubstance)); - } - - if (LuminousIntensity != 0) - { - parts.Add(FormatDimension("J", LuminousIntensity)); - } - - return string.Join(" ", parts); - } - - /// - /// Formats a single dimension with its power using Unicode superscript. - /// - /// The dimension symbol. - /// The power of the dimension. - /// Formatted dimension string. - private static string FormatDimension(string symbol, int power) - { - return power switch - { - 1 => symbol, - -1 => symbol + "⁻¹", - var p when p > 0 => symbol + ToSuperscript(p), - var p => symbol + "⁻" + ToSuperscript(-p), - }; - } - - /// - /// Converts a positive integer to Unicode superscript characters. - /// - /// The number to convert. - /// The superscript representation. - private static string ToSuperscript(int number) - { - Dictionary superscriptMap = new() - { - ['0'] = '⁰', - ['1'] = '¹', - ['2'] = '²', - ['3'] = '³', - ['4'] = '⁴', - ['5'] = '⁵', - ['6'] = '⁶', - ['7'] = '⁷', - ['8'] = '⁸', - ['9'] = '⁹' - }; - - return new string([.. number.ToString().Select(c => superscriptMap[c])]); - } - - /// - public static PhysicalDimension Multiply(PhysicalDimension left, PhysicalDimension right) => throw new NotImplementedException(); - /// - - public static PhysicalDimension Divide(PhysicalDimension left, PhysicalDimension right) => throw new NotImplementedException(); -} diff --git a/Semantics.Quantities/Core/PhysicalDimensionExtensions.cs b/Semantics.Quantities/Core/PhysicalDimensionExtensions.cs deleted file mode 100644 index c7263c0..0000000 --- a/Semantics.Quantities/Core/PhysicalDimensionExtensions.cs +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Extension methods for creating physical quantities from numeric values using the unit system. -/// -public static class PhysicalDimensionExtensions -{ - #region Length Extensions - /// - /// Creates a Length from a value in meters. - /// - /// The numeric type of the input value. - /// The value in meters. - /// A new Length instance. - public static Length Meters(this T value) where T : struct, INumber - => Length.Create(ConvertToBaseUnit(value, Units.Meter)); - - /// - /// Creates a Length from a value in kilometers. - /// - /// The numeric type of the input value. - /// The value in kilometers. - /// A new Length instance. - public static Length Kilometers(this T value) where T : struct, INumber - => Length.Create(ConvertToBaseUnit(value, Units.Kilometer)); - - /// - /// Creates a Length from a value in centimeters. - /// - /// The numeric type of the input value. - /// The value in centimeters. - /// A new Length instance. - public static Length Centimeters(this T value) where T : struct, INumber - => Length.Create(ConvertToBaseUnit(value, Units.Centimeter)); - - /// - /// Creates a Length from a value in millimeters. - /// - /// The numeric type of the input value. - /// The value in millimeters. - /// A new Length instance. - public static Length Millimeters(this T value) where T : struct, INumber - => Length.Create(ConvertToBaseUnit(value, Units.Millimeter)); - - /// - /// Creates a Length from a value in feet. - /// - /// The numeric type of the input value. - /// The value in feet. - /// A new Length instance. - public static Length Feet(this T value) where T : struct, INumber - => Length.Create(ConvertToBaseUnit(value, Units.Foot)); - - /// - /// Creates a Length from a value in inches. - /// - /// The numeric type of the input value. - /// The value in inches. - /// A new Length instance. - public static Length Inches(this T value) where T : struct, INumber - => Length.Create(ConvertToBaseUnit(value, Units.Inch)); - - /// - /// Creates a Length from a value in yards. - /// - /// The numeric type of the input value. - /// The value in yards. - /// A new Length instance. - public static Length Yards(this T value) where T : struct, INumber - => Length.Create(ConvertToBaseUnit(value, Units.Yard)); - - /// - /// Creates a Length from a value in miles. - /// - /// The numeric type of the input value. - /// The value in miles. - /// A new Length instance. - public static Length Miles(this T value) where T : struct, INumber - => Length.Create(ConvertToBaseUnit(value, Units.Mile)); - #endregion - - #region Mass Extensions - /// - /// Creates a Mass from a value in kilograms. - /// - /// The numeric type of the input value. - /// The value in kilograms. - /// A new Mass instance. - public static Mass Kilograms(this T value) where T : struct, INumber - => Mass.Create(ConvertToBaseUnit(value, Units.Kilogram)); - - /// - /// Creates a Mass from a value in grams. - /// - /// The numeric type of the input value. - /// The value in grams. - /// A new Mass instance. - public static Mass Grams(this T value) where T : struct, INumber - => Mass.Create(ConvertToBaseUnit(value, Units.Gram)); - - /// - /// Creates a Mass from a value in pounds. - /// - /// The numeric type of the input value. - /// The value in pounds. - /// A new Mass instance. - public static Mass Pounds(this T value) where T : struct, INumber - => Mass.Create(ConvertToBaseUnit(value, Units.Pound)); - - /// - /// Creates a Mass from a value in ounces. - /// - /// The numeric type of the input value. - /// The value in ounces. - /// A new Mass instance. - public static Mass Ounces(this T value) where T : struct, INumber - => Mass.Create(ConvertToBaseUnit(value, Units.Ounce)); - - /// - /// Creates a Mass from a value in stones. - /// - /// The numeric type of the input value. - /// The value in stones. - /// A new Mass instance. - public static Mass Stones(this T value) where T : struct, INumber - => Mass.Create(ConvertToBaseUnit(value, Units.Stone)); - #endregion - - #region Time Extensions - /// - /// Creates a Time from a value in seconds. - /// - /// The numeric type of the input value. - /// The value in seconds. - /// A new Time instance. - public static Time Seconds(this T value) where T : struct, INumber - => Time.Create(ConvertToBaseUnit(value, Units.Second)); - - /// - /// Creates a Time from a value in minutes. - /// - /// The numeric type of the input value. - /// The value in minutes. - /// A new Time instance. - public static Time Minutes(this T value) where T : struct, INumber - => Time.Create(ConvertToBaseUnit(value, Units.Minute)); - - /// - /// Creates a Time from a value in hours. - /// - /// The numeric type of the input value. - /// The value in hours. - /// A new Time instance. - public static Time Hours(this T value) where T : struct, INumber - => Time.Create(ConvertToBaseUnit(value, Units.Hour)); - - /// - /// Creates a Time from a value in days. - /// - /// The numeric type of the input value. - /// The value in days. - /// A new Time instance. - public static Time Days(this T value) where T : struct, INumber - => Time.Create(ConvertToBaseUnit(value, Units.Day)); - - /// - /// Creates a Time from a value in milliseconds. - /// - /// The numeric type of the input value. - /// The value in milliseconds. - /// A new Time instance. - public static Time Milliseconds(this T value) where T : struct, INumber - => Time.Create(ConvertToBaseUnit(value, Units.Millisecond)); - #endregion - - #region Area Extensions - /// - /// Creates an Area from a value in square meters. - /// - /// The numeric type of the input value. - /// The value in square meters. - /// A new Area instance. - public static Area SquareMeters(this T value) where T : struct, INumber - => Area.Create(ConvertToBaseUnit(value, Units.SquareMeter)); - - /// - /// Creates an Area from a value in square kilometers. - /// - /// The numeric type of the input value. - /// The value in square kilometers. - /// A new Area instance. - public static Area SquareKilometers(this T value) where T : struct, INumber - => Area.Create(ConvertToBaseUnit(value, Units.SquareKilometer)); - - /// - /// Creates an Area from a value in square feet. - /// - /// The numeric type of the input value. - /// The value in square feet. - /// A new Area instance. - public static Area SquareFeet(this T value) where T : struct, INumber - => Area.Create(ConvertToBaseUnit(value, Units.SquareFoot)); - - /// - /// Creates an Area from a value in acres. - /// - /// The numeric type of the input value. - /// The value in acres. - /// A new Area instance. - public static Area Acres(this T value) where T : struct, INumber - => Area.Create(ConvertToBaseUnit(value, Units.Acre)); - #endregion - - #region Volume Extensions - /// - /// Creates a Volume from a value in cubic meters. - /// - /// The numeric type of the input value. - /// The value in cubic meters. - /// A new Volume instance. - public static Volume CubicMeters(this T value) where T : struct, INumber - => Volume.Create(ConvertToBaseUnit(value, Units.CubicMeter)); - - /// - /// Creates a Volume from a value in liters. - /// - /// The numeric type of the input value. - /// The value in liters. - /// A new Volume instance. - public static Volume Liters(this T value) where T : struct, INumber - => Volume.Create(ConvertToBaseUnit(value, Units.Liter)); - - /// - /// Creates a Volume from a value in milliliters. - /// - /// The numeric type of the input value. - /// The value in milliliters. - /// A new Volume instance. - public static Volume Milliliters(this T value) where T : struct, INumber - => Volume.Create(ConvertToBaseUnit(value, Units.Milliliter)); - - /// - /// Creates a Volume from a value in cubic feet. - /// - /// The numeric type of the input value. - /// The value in cubic feet. - /// A new Volume instance. - public static Volume CubicFeet(this T value) where T : struct, INumber - => Volume.Create(ConvertToBaseUnit(value, Units.CubicFoot)); - - /// - /// Creates a Volume from a value in US gallons. - /// - /// The numeric type of the input value. - /// The value in US gallons. - /// A new Volume instance. - public static Volume USGallons(this T value) where T : struct, INumber - => Volume.Create(ConvertToBaseUnit(value, Units.USGallon)); - #endregion - - #region Helper Methods - /// - /// Converts a value from a specific unit to the base unit of that dimension. - /// - /// The numeric type of the value. - /// The value in the source unit. - /// The source unit to convert from. - /// The value converted to the base unit. - private static T ConvertToBaseUnit(T value, IUnit sourceUnit) where T : struct, INumber - { - Ensure.NotNull(sourceUnit); - - // Handle offset units (like temperature conversions) - if (Math.Abs(sourceUnit.ToBaseOffset) > 1e-10) - { - T factor = T.CreateChecked(sourceUnit.ToBaseFactor); - T offset = T.CreateChecked(sourceUnit.ToBaseOffset); - return (value * factor) + offset; - } - - // Handle linear units (most common case) - T conversionFactor = T.CreateChecked(sourceUnit.ToBaseFactor); - return value * conversionFactor; - } - #endregion - - //TODO: #region Thermal Extensions - // Temperature: .Celsius(), .Fahrenheit(), .Rankine() - // Heat: .Joules(), .Calories(), .BTU() - // Thermal Conductivity: .WattsPerMeterKelvin() - // Heat Capacity: .JoulesPerKelvin() - // Specific Heat: .JoulesPerKilogramKelvin() - // #endregion - - //TODO: #region Fluid Dynamics Extensions - // Flow Rate: .CubicMetersPerSecond(), .LitersPerMinute(), .GallonsPerMinute() - // Viscosity: .PascalSeconds(), .Poise(), .Centipoise() - // Density: .KilogramsPerCubicMeter(), .GramsPerCubicCentimeter() - // Surface Tension: .NewtonsPerMeter() - // #endregion - - //TODO: #region Electromagnetic Extensions - // Magnetic Field: .Tesla(), .Gauss(), .Webers() - // Inductance: .Henries(), .Millihenries(), .Microhenries() - // Electric Field: .VoltsPerMeter(), .NewtonsPerCoulomb() - // Magnetic Flux: .Webers(), .Maxwells() - // #endregion - - //TODO: #region Materials Science Extensions - // Stress/Pressure: .Pascals(), .Megapascals(), .Gigapascals(), .PSI() - // Strain: .Percent(), .Dimensionless() - // Elastic Modulus: .Gigapascals(), .MegapascalsPerStrain() - // Hardness: .Brinell(), .Rockwell(), .Vickers() - // #endregion - - //TODO: #region Nuclear & Radiation Extensions - // Radioactivity: .Becquerels(), .Curies() - // Absorbed Dose: .Grays(), .Rads() - // Dose Equivalent: .Sieverts(), .Rems() - // Cross Section: .Barns(), .SquareMeters() - // #endregion - - //TODO: #region Optics Extensions - // Wavelength: .Nanometers(), .Angstroms(), .Micrometers() - // Frequency: .Terahertz(), .PetaHertz() - // Illuminance: .Lux(), .FootCandles() - // Luminance: .CandelasPerSquareMeter(), .Nits() - // Optical Power: .Diopters() - // #endregion - - //TODO: #region Astronomy Extensions - // Distance: .AstronomicalUnits(), .LightYears(), .Parsecs() - // Mass: .SolarMasses(), .EarthMasses(), .JupiterMasses() - // Angular Resolution: .Arcseconds(), .Milliarcseconds() - // Flux Density: .Janskys() - // #endregion - - //TODO: #region Geophysics Extensions - // Seismic Velocity: .KilometersPerSecond() - // Gravity: .Gals(), .MilliGals() - // Atmospheric Pressure: .Millibars(), .InchesOfMercury() - // Magnetic Field: .Nanoteslas(), .Gammas() - // #endregion - - //TODO: #region Acoustics Extensions - // Sound Pressure: .Micropascals(), .Decibels() - // Sound Intensity: .WattsPerSquareMeter() - // Frequency: .Hertz(), .Kilohertz(), .Megahertz() - // Acoustic Power: .AcousticWatts() - // #endregion - - //TODO: #region Biochemistry Extensions - // Concentration: .Molar(), .Millimolar(), .Micromolar() - // Enzyme Activity: .Katals(), .EnzymeUnits() - // pH: .pHUnits() - // Molecular Weight: .Daltons(), .KiloDaltons() - // #endregion -} diff --git a/Semantics.Quantities/Core/PhysicalDimensions.cs b/Semantics.Quantities/Core/PhysicalDimensions.cs deleted file mode 100644 index 41d93af..0000000 --- a/Semantics.Quantities/Core/PhysicalDimensions.cs +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Collections.Generic; - -/// -public static class PhysicalDimensions -{ - /// Dimensionless quantity [1]. - public static readonly PhysicalDimension Dimensionless = new(baseUnit: BootstrapUnits.Dimensionless); - - /// Length dimension [L]. - public static readonly PhysicalDimension Length = new(baseUnit: BootstrapUnits.Meter, length: 1); - - /// Mass dimension [M]. - public static readonly PhysicalDimension Mass = new(baseUnit: BootstrapUnits.Kilogram, mass: 1); - - /// Time dimension [T]. - public static readonly PhysicalDimension Time = new(baseUnit: BootstrapUnits.Second, time: 1); - - /// Electric current dimension [I]. - public static readonly PhysicalDimension ElectricCurrent = new(baseUnit: BootstrapUnits.Ampere, electricCurrent: 1); - - /// Temperature dimension [Θ]. - public static readonly PhysicalDimension Temperature = new(baseUnit: BootstrapUnits.Kelvin, temperature: 1); - - /// Amount of substance dimension [N]. - public static readonly PhysicalDimension AmountOfSubstance = new(baseUnit: BootstrapUnits.Mole, amountOfSubstance: 1); - - /// Luminous intensity dimension [J]. - public static readonly PhysicalDimension LuminousIntensity = new(baseUnit: BootstrapUnits.Candela, luminousIntensity: 1); - - /// Area dimension [L²]. - public static readonly PhysicalDimension Area = new(baseUnit: BootstrapUnits.SquareMeter, length: 2); - - /// Volume dimension [L³]. - public static readonly PhysicalDimension Volume = new(baseUnit: BootstrapUnits.CubicMeter, length: 3); - - /// Velocity dimension [L T⁻¹]. - public static readonly PhysicalDimension Velocity = new(baseUnit: BootstrapUnits.MetersPerSecond, length: 1, time: -1); - - /// Acceleration dimension [L T⁻²]. - public static readonly PhysicalDimension Acceleration = new(baseUnit: BootstrapUnits.MetersPerSecondSquared, length: 1, time: -2); - - /// Force dimension [M L T⁻²]. - public static readonly PhysicalDimension Force = new(mass: 1, length: 1, time: -2, baseUnit: BootstrapUnits.Newton); - - /// Pressure dimension [M L⁻¹ T⁻²]. - public static readonly PhysicalDimension Pressure = new(mass: 1, length: -1, time: -2, baseUnit: BootstrapUnits.Pascal); - - /// Energy dimension [M L² T⁻²]. - public static readonly PhysicalDimension Energy = new(mass: 1, length: 2, time: -2, baseUnit: BootstrapUnits.Joule); - - /// Power dimension [M L² T⁻³]. - public static readonly PhysicalDimension Power = new(mass: 1, length: 2, time: -3, baseUnit: BootstrapUnits.Watt); - - /// Electric potential dimension [M L² T⁻³ I⁻¹]. - public static readonly PhysicalDimension ElectricPotential = new(mass: 1, length: 2, time: -3, electricCurrent: -1, baseUnit: BootstrapUnits.Volt); - - /// Electric resistance dimension [M L² T⁻³ I⁻²]. - public static readonly PhysicalDimension ElectricResistance = new(mass: 1, length: 2, time: -3, electricCurrent: -2, baseUnit: BootstrapUnits.Ohm); - - /// Electric charge dimension [I T]. - public static readonly PhysicalDimension ElectricCharge = new(electricCurrent: 1, time: 1, baseUnit: BootstrapUnits.Coulomb); - - /// Electric capacitance dimension [M⁻¹ L⁻² T⁴ I²]. - public static readonly PhysicalDimension ElectricCapacitance = new(mass: -1, length: -2, time: 4, electricCurrent: 2, baseUnit: BootstrapUnits.Farad); - - /// Frequency dimension [T⁻¹]. - public static readonly PhysicalDimension Frequency = new(baseUnit: BootstrapUnits.Hertz, time: -1); - - /// Momentum dimension [M L T⁻¹]. - public static readonly PhysicalDimension Momentum = new(mass: 1, length: 1, time: -1, baseUnit: BootstrapUnits.Newton); - - /// Angular velocity dimension [T⁻¹]. - public static readonly PhysicalDimension AngularVelocity = new(baseUnit: BootstrapUnits.Radian, time: -1); - - /// Angular acceleration dimension [T⁻²]. - public static readonly PhysicalDimension AngularAcceleration = new(baseUnit: BootstrapUnits.Radian, time: -2); - - /// Torque dimension [M L² T⁻²]. - public static readonly PhysicalDimension Torque = new(mass: 1, length: 2, time: -2, baseUnit: BootstrapUnits.Newton); - - /// Moment of inertia dimension [M L²]. - public static readonly PhysicalDimension MomentOfInertia = new(mass: 1, length: 2, baseUnit: BootstrapUnits.Kilogram); - - /// Density dimension [M L⁻³]. - public static readonly PhysicalDimension Density = new(mass: 1, length: -3, baseUnit: BootstrapUnits.Kilogram); - - /// Concentration dimension [N L⁻³]. - public static readonly PhysicalDimension Concentration = new(amountOfSubstance: 1, length: -3, baseUnit: BootstrapUnits.Molar); - - /// Electric field dimension [M L T⁻³ I⁻¹]. - public static readonly PhysicalDimension ElectricField = new(mass: 1, length: 1, time: -3, electricCurrent: -1, baseUnit: BootstrapUnits.Volt); - - /// Electric flux dimension [M L³ T⁻³ I⁻¹]. - public static readonly PhysicalDimension ElectricFlux = new(mass: 1, length: 3, time: -3, electricCurrent: -1, baseUnit: BootstrapUnits.Volt); - - /// Permittivity dimension [M⁻¹ L⁻³ T⁴ I²]. - public static readonly PhysicalDimension Permittivity = new(mass: -1, length: -3, time: 4, electricCurrent: 2, baseUnit: BootstrapUnits.Farad); - - /// Electric conductivity dimension [M⁻¹ L⁻³ T³ I²]. - public static readonly PhysicalDimension ElectricConductivity = new(mass: -1, length: -3, time: 3, electricCurrent: 2, baseUnit: BootstrapUnits.Ohm); - - /// Electric power density dimension [M L⁻¹ T⁻³]. - public static readonly PhysicalDimension ElectricPowerDensity = new(mass: 1, length: -1, time: -3, baseUnit: BootstrapUnits.Watt); - - /// AC impedance dimension [M L² T⁻³ I⁻²]. - public static readonly PhysicalDimension ImpedanceAC = new(mass: 1, length: 2, time: -3, electricCurrent: -2, baseUnit: BootstrapUnits.Ohm); - - /// Sound pressure dimension [M L⁻¹ T⁻²]. - public static readonly PhysicalDimension SoundPressure = new(mass: 1, length: -1, time: -2, baseUnit: BootstrapUnits.Pascal); - - /// Sound intensity dimension [M T⁻³]. - public static readonly PhysicalDimension SoundIntensity = new(mass: 1, time: -3, baseUnit: BootstrapUnits.Watt); - - /// Sound power dimension [M L² T⁻³]. - public static readonly PhysicalDimension SoundPower = new(mass: 1, length: 2, time: -3, baseUnit: BootstrapUnits.Watt); - - /// Acoustic impedance dimension [M L⁻² T⁻¹]. - public static readonly PhysicalDimension AcousticImpedance = new(mass: 1, length: -2, time: -1, baseUnit: BootstrapUnits.Pascal); - - /// Sound speed dimension [L T⁻¹]. - public static readonly PhysicalDimension SoundSpeed = new(length: 1, time: -1, baseUnit: BootstrapUnits.MetersPerSecond); - - /// Sound absorption coefficient dimension [1] (dimensionless). - public static readonly PhysicalDimension SoundAbsorption = new(baseUnit: BootstrapUnits.Coefficient); - - /// Reverberation time dimension [T]. - public static readonly PhysicalDimension ReverberationTime = new(time: 1, baseUnit: BootstrapUnits.Second); - - /// Wavelength dimension [L]. - public static readonly PhysicalDimension Wavelength = new(length: 1, baseUnit: BootstrapUnits.Meter); - - /// Wave number dimension [L⁻¹]. - public static readonly PhysicalDimension WaveNumber = new(length: -1, baseUnit: BootstrapUnits.Meter); - - /// Sound pressure level dimension [1] (dB SPL). - public static readonly PhysicalDimension SoundPressureLevel = new(baseUnit: BootstrapUnits.Decibel); - - /// Sound intensity level dimension [1] (dB IL). - public static readonly PhysicalDimension SoundIntensityLevel = new(baseUnit: BootstrapUnits.Decibel); - - /// Sound power level dimension [1] (dB PWL). - public static readonly PhysicalDimension SoundPowerLevel = new(baseUnit: BootstrapUnits.Decibel); - - /// Reflection coefficient dimension [1] (dimensionless). - public static readonly PhysicalDimension ReflectionCoefficient = new(baseUnit: BootstrapUnits.Coefficient); - - /// Noise reduction coefficient dimension [1] (dimensionless). - public static readonly PhysicalDimension NoiseReductionCoefficient = new(baseUnit: BootstrapUnits.Coefficient); - - /// Sound transmission class dimension [1] (dimensionless). - public static readonly PhysicalDimension SoundTransmissionClass = new(baseUnit: BootstrapUnits.Dimensionless); - - /// Loudness dimension [1] (sones - dimensionless). - public static readonly PhysicalDimension Loudness = new(baseUnit: BootstrapUnits.Dimensionless); - - /// Pitch dimension [T⁻¹] (frequency-based). - public static readonly PhysicalDimension Pitch = new(time: -1, baseUnit: BootstrapUnits.Hertz); - - /// Sharpness dimension [1] (acum - dimensionless). - public static readonly PhysicalDimension Sharpness = new(baseUnit: BootstrapUnits.Dimensionless); - - /// Sensitivity dimension [M⁻¹ L⁻¹ T² I] (V/Pa for microphones). - public static readonly PhysicalDimension Sensitivity = new(mass: -1, length: -1, time: 2, electricCurrent: 1, baseUnit: BootstrapUnits.Volt); - - /// Directionality index dimension [1] (dB). - public static readonly PhysicalDimension DirectionalityIndex = new(baseUnit: BootstrapUnits.Decibel); - - /// Heat dimension [M L² T⁻²]. - public static readonly PhysicalDimension Heat = new(mass: 1, length: 2, time: -2, baseUnit: BootstrapUnits.Joule); - - /// Entropy dimension [M L² T⁻² Θ⁻¹]. - public static readonly PhysicalDimension Entropy = new(mass: 1, length: 2, time: -2, temperature: -1, baseUnit: BootstrapUnits.Joule); - - /// Thermal conductivity dimension [M L T⁻³ Θ⁻¹]. - public static readonly PhysicalDimension ThermalConductivity = new(mass: 1, length: 1, time: -3, temperature: -1, baseUnit: BootstrapUnits.Watt); - - /// Heat capacity dimension [M L² T⁻² Θ⁻¹]. - public static readonly PhysicalDimension HeatCapacity = new(mass: 1, length: 2, time: -2, temperature: -1, baseUnit: BootstrapUnits.Joule); - - /// Specific heat dimension [L² T⁻² Θ⁻¹]. - public static readonly PhysicalDimension SpecificHeat = new(length: 2, time: -2, temperature: -1, baseUnit: BootstrapUnits.Joule); - - /// Heat transfer coefficient dimension [M T⁻³ Θ⁻¹]. - public static readonly PhysicalDimension HeatTransferCoefficient = new(mass: 1, time: -3, temperature: -1, baseUnit: BootstrapUnits.Watt); - - /// Thermal expansion coefficient dimension [Θ⁻¹]. - public static readonly PhysicalDimension ThermalExpansion = new(temperature: -1, baseUnit: BootstrapUnits.Kelvin); - - /// Thermal diffusivity dimension [L² T⁻¹]. - public static readonly PhysicalDimension ThermalDiffusivity = new(length: 2, time: -1, baseUnit: BootstrapUnits.Meter); - - /// Thermal resistance dimension [Θ T³ M⁻¹ L⁻²]. - public static readonly PhysicalDimension ThermalResistance = new(temperature: 1, time: 3, mass: -1, length: -2, baseUnit: BootstrapUnits.Kelvin); - - /// Molar mass dimension [M N⁻¹]. - public static readonly PhysicalDimension MolarMass = new(mass: 1, amountOfSubstance: -1, baseUnit: BootstrapUnits.Gram); - - /// pH dimension [1] (logarithmic acidity scale). - public static readonly PhysicalDimension pH = new(baseUnit: BootstrapUnits.PH); - - /// Reaction rate dimension [N L⁻³ T⁻¹]. - public static readonly PhysicalDimension ReactionRate = new(amountOfSubstance: 1, length: -3, time: -1, baseUnit: BootstrapUnits.Molar); - - /// Activation energy dimension [M L² T⁻² N⁻¹]. - public static readonly PhysicalDimension ActivationEnergy = new(mass: 1, length: 2, time: -2, amountOfSubstance: -1, baseUnit: BootstrapUnits.Joule); - - /// Rate constant dimension [varies by reaction order]. - public static readonly PhysicalDimension RateConstant = new(baseUnit: BootstrapUnits.Second); // First-order default - - /// Enzyme activity dimension [N T⁻¹]. - public static readonly PhysicalDimension EnzymeActivity = new(amountOfSubstance: 1, time: -1, baseUnit: BootstrapUnits.Mole); - - /// Solubility dimension [N L⁻³]. - public static readonly PhysicalDimension Solubility = new(amountOfSubstance: 1, length: -3, baseUnit: BootstrapUnits.Molar); - - /// Surface tension dimension [M T⁻²]. - public static readonly PhysicalDimension SurfaceTension = new(mass: 1, time: -2, baseUnit: BootstrapUnits.Newton); - - /// Vapor pressure dimension [M L⁻¹ T⁻²]. - public static readonly PhysicalDimension VaporPressure = new(mass: 1, length: -1, time: -2, baseUnit: BootstrapUnits.Pascal); - - /// Dynamic viscosity dimension [M L⁻¹ T⁻¹]. - public static readonly PhysicalDimension DynamicViscosity = new(mass: 1, length: -1, time: -1, baseUnit: BootstrapUnits.Pascal); - - /// Luminous flux dimension [J] (cd⋅sr). - public static readonly PhysicalDimension LuminousFlux = new(luminousIntensity: 1, baseUnit: BootstrapUnits.Lumen); - - /// Illuminance dimension [J L⁻²] (lm/m²). - public static readonly PhysicalDimension Illuminance = new(luminousIntensity: 1, length: -2, baseUnit: BootstrapUnits.Lux); - - /// Luminance dimension [J L⁻²] (cd/m²). - public static readonly PhysicalDimension Luminance = new(luminousIntensity: 1, length: -2, baseUnit: BootstrapUnits.Candela); - - /// Refractive index dimension [1] (dimensionless optical ratio). - public static readonly PhysicalDimension RefractiveIndex = new(baseUnit: BootstrapUnits.Dimensionless); - - /// Optical power dimension [L⁻¹] (diopters). - public static readonly PhysicalDimension OpticalPower = new(length: -1, baseUnit: BootstrapUnits.Diopter); - - /// Radioactive activity dimension [T⁻¹] (becquerels). - public static readonly PhysicalDimension RadioactiveActivity = new(time: -1, baseUnit: BootstrapUnits.Becquerel); - - /// Absorbed dose dimension [L² T⁻²] (grays). - public static readonly PhysicalDimension AbsorbedDose = new(length: 2, time: -2, baseUnit: BootstrapUnits.Gray); - - /// Equivalent dose dimension [L² T⁻²] (sieverts). - public static readonly PhysicalDimension EquivalentDose = new(length: 2, time: -2, baseUnit: BootstrapUnits.Sievert); - - /// Exposure dimension [M⁻¹ T I] (coulombs per kilogram). - public static readonly PhysicalDimension Exposure = new(mass: -1, time: 1, electricCurrent: 1, baseUnit: BootstrapUnits.Coulomb); - - /// Nuclear cross section dimension [L²] (barns). - public static readonly PhysicalDimension NuclearCrossSection = new(length: 2, baseUnit: BootstrapUnits.Barn); - - /// Kinematic viscosity dimension [L² T⁻¹] (m²/s). - public static readonly PhysicalDimension KinematicViscosity = new(length: 2, time: -1, baseUnit: BootstrapUnits.Meter); - - /// Bulk modulus dimension [M L⁻¹ T⁻²] (Pa). - public static readonly PhysicalDimension BulkModulus = new(mass: 1, length: -1, time: -2, baseUnit: BootstrapUnits.Pascal); - - /// Volumetric flow rate dimension [L³ T⁻¹] (m³/s). - public static readonly PhysicalDimension VolumetricFlowRate = new(length: 3, time: -1, baseUnit: BootstrapUnits.CubicMeter); - - /// Mass flow rate dimension [M T⁻¹] (kg/s). - public static readonly PhysicalDimension MassFlowRate = new(mass: 1, time: -1, baseUnit: BootstrapUnits.Kilogram); - - /// Reynolds number dimension [1] (dimensionless fluid dynamics ratio). - public static readonly PhysicalDimension ReynoldsNumber = new(baseUnit: BootstrapUnits.Dimensionless); - - /// Gets a read-only collection of all standard physical dimensions. - public static IReadOnlySet All { get; } = new HashSet - { - Dimensionless, - Length, - Mass, - Time, - ElectricCurrent, - Temperature, - AmountOfSubstance, - LuminousIntensity, - Area, - Volume, - Velocity, - Acceleration, - Force, - Pressure, - Energy, - Power, - ElectricPotential, - ElectricResistance, - ElectricCharge, - ElectricCapacitance, - Frequency, - Momentum, - AngularVelocity, - AngularAcceleration, - Torque, - MomentOfInertia, - Density, - Concentration, - ElectricField, - ElectricFlux, - Permittivity, - ElectricConductivity, - ElectricPowerDensity, - ImpedanceAC, - SoundPressure, - SoundIntensity, - SoundPower, - AcousticImpedance, - SoundSpeed, - SoundAbsorption, - ReverberationTime, - Wavelength, - WaveNumber, - SoundPressureLevel, - SoundIntensityLevel, - SoundPowerLevel, - ReflectionCoefficient, - NoiseReductionCoefficient, - SoundTransmissionClass, - Loudness, - Pitch, - Sharpness, - Sensitivity, - DirectionalityIndex, - Heat, - Entropy, - ThermalConductivity, - HeatCapacity, - SpecificHeat, - HeatTransferCoefficient, - ThermalExpansion, - ThermalDiffusivity, - ThermalResistance, - MolarMass, - pH, - ReactionRate, - ActivationEnergy, - RateConstant, - EnzymeActivity, - Solubility, - SurfaceTension, - VaporPressure, - DynamicViscosity, - LuminousFlux, - Illuminance, - Luminance, - RefractiveIndex, - OpticalPower, - RadioactiveActivity, - AbsorbedDose, - EquivalentDose, - Exposure, - NuclearCrossSection, - KinematicViscosity, - BulkModulus, - VolumetricFlowRate, - MassFlowRate, - ReynoldsNumber - }; -} diff --git a/Semantics.Quantities/Core/PhysicalQuantity.cs b/Semantics.Quantities/Core/PhysicalQuantity.cs deleted file mode 100644 index 3e2da9e..0000000 --- a/Semantics.Quantities/Core/PhysicalQuantity.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Generic PhysicalQuantity with configurable storage type. -/// -/// The derived physical quantity type. -/// The storage type for the quantity value. -public abstract record PhysicalQuantity - : SemanticQuantity - , IPhysicalQuantity - where TSelf : PhysicalQuantity, new() - where T : struct, INumber -{ - /// Gets the physical dimension of this quantity. - public abstract PhysicalDimension Dimension { get; } - - /// Gets the value stored in this quantity. - public T Value => Quantity; - - /// Gets whether this quantity satisfies physical constraints. - public virtual bool IsPhysicallyValid => !T.IsNaN(Value) && T.IsFinite(Value); - - /// - /// Initializes a new instance of the PhysicalQuantity class. - /// - protected PhysicalQuantity() : base() { } - - /// - /// Creates a new instance with the specified value. - /// - /// The value for the quantity. - /// A new instance of the quantity. - public static new TSelf Create(T value) => new TSelf() with { Quantity = value }; - - /// - /// Converts the quantity value to the specified unit. - /// - /// The target unit for conversion. - /// The value in the target unit. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1716:Identifiers should not match keywords", Justification = "")] - public virtual T In(IUnit targetUnit) - { - Ensure.NotNull(targetUnit); - - if (!targetUnit.Dimension.Equals(Dimension)) - { - throw new UnitConversionException(Dimension.BaseUnit, targetUnit, $"Cannot convert {Dimension} to {targetUnit.Dimension}"); - } - - // Handle offset units (like temperature conversions) - if (Math.Abs(targetUnit.ToBaseOffset) > 1e-10) - { - T factor = T.CreateChecked(targetUnit.ToBaseFactor); - T offset = T.CreateChecked(targetUnit.ToBaseOffset); - return (Value - offset) / factor; - } - - // Handle linear units (most common case) - T conversionFactor = T.CreateChecked(targetUnit.ToBaseFactor); - return Value / conversionFactor; - } - - /// - /// Compares this quantity to another quantity. - /// - /// The other quantity to compare to. - /// A value indicating the relative order of the quantities. - public int CompareTo(TSelf? other) => other is null ? 1 : Value.CompareTo(other.Value); - - /// - /// Compares this quantity to another physical quantity. - /// - /// The other quantity to compare to. - /// A value indicating the relative order of the quantities. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S3358:Ternary operators should not be nested", Justification = "")] - public int CompareTo(IPhysicalQuantity? other) - { - return other is null - ? 1 - : other is not TSelf typedOther - ? throw new ArgumentException($"Cannot compare {GetType().Name} to {other.GetType().Name}") - : CompareTo(typedOther); - } - - /// - /// Determines whether this quantity is equal to another physical quantity. - /// - /// The other quantity to compare to. - /// True if the quantities are equal; otherwise, false. - public virtual bool Equals(IPhysicalQuantity? other) => other is TSelf typedOther && Equals(typedOther); - - /// - /// Returns a string representation of this quantity. - /// - /// A string representation including the value and unit symbol. - public override string ToString() => $"{Value} {Dimension.BaseUnit.Symbol}"; - /// - public static bool operator <(PhysicalQuantity left, PhysicalQuantity right) => left is null ? right is not null : left.CompareTo(right) < 0; - - /// - public static bool operator <=(PhysicalQuantity left, PhysicalQuantity right) => left is null || left.CompareTo(right) <= 0; - - /// - public static bool operator >(PhysicalQuantity left, PhysicalQuantity right) => left is not null && left.CompareTo(right) > 0; - - /// - public static bool operator >=(PhysicalQuantity left, PhysicalQuantity right) => left is null ? right is null : left.CompareTo(right) >= 0; -} diff --git a/Semantics.Quantities/Core/Unit.cs b/Semantics.Quantities/Core/Unit.cs deleted file mode 100644 index 6acaee6..0000000 --- a/Semantics.Quantities/Core/Unit.cs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -/// -/// Unified implementation for all physical units. -/// -/// -/// Initializes a new instance of the Unit struct. -/// -/// The full name of the unit. -/// The symbol/abbreviation of the unit. -/// The physical dimension this unit measures. -/// The multiplication factor to convert to the base unit. -/// The offset to add when converting to the base unit (default: 0.0). -/// The unit system this unit belongs to (default: SI_Derived). -public readonly struct Unit( - string name, - string symbol, - PhysicalDimension dimension, - UnitSystem system, - double toBaseFactor = 1.0, - double toBaseOffset = 0.0) : IUnit, IEquatable -{ - /// Gets the full name of the unit. - public string Name { get; } = string.Intern(name); - - /// Gets the symbol/abbreviation of the unit. - public string Symbol { get; } = string.Intern(symbol); - - /// Gets the physical dimension this unit measures. - public PhysicalDimension Dimension { get; } = dimension; - - /// Gets the multiplication factor to convert to the base unit. - public double ToBaseFactor { get; } = toBaseFactor; - - /// Gets the offset to add when converting to the base unit (0.0 for linear units). - public double ToBaseOffset { get; } = toBaseOffset; - - /// Gets the unit system this unit belongs to. - public UnitSystem System { get; } = system; - - /// - /// Determines whether this unit is equal to another unit. - /// - /// The other unit to compare. - /// True if the units are equal, false otherwise. - public bool Equals(Unit other) => - ReferenceEquals(Name, other.Name) && - ReferenceEquals(Symbol, other.Symbol) && - Dimension.Equals(other.Dimension) && - Math.Abs(ToBaseFactor - other.ToBaseFactor) < 1e-10 && - Math.Abs(ToBaseOffset - other.ToBaseOffset) < 1e-10 && - System == other.System; - - /// - /// Determines whether this unit is equal to another object. - /// - /// The object to compare. - /// True if the objects are equal, false otherwise. - public override bool Equals(object? obj) => obj is Unit other && Equals(other); - - /// - /// Gets the hash code for this unit. - /// - /// A hash code for this unit. - public override int GetHashCode() => HashCode.Combine(Name, Symbol, Dimension, ToBaseFactor, ToBaseOffset, System); - - /// - /// Determines whether two units are equal. - /// - /// The first unit. - /// The second unit. - /// True if the units are equal, false otherwise. - public static bool operator ==(Unit left, Unit right) => left.Equals(right); - - /// - /// Determines whether two units are not equal. - /// - /// The first unit. - /// The second unit. - /// True if the units are not equal, false otherwise. - public static bool operator !=(Unit left, Unit right) => !left.Equals(right); - - /// - /// Returns a string representation of this unit. - /// - /// The unit's symbol. - public override string ToString() => Symbol; -} diff --git a/Semantics.Quantities/Core/UnitConversionException.cs b/Semantics.Quantities/Core/UnitConversionException.cs deleted file mode 100644 index 966ec53..0000000 --- a/Semantics.Quantities/Core/UnitConversionException.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -/// -/// Exception thrown when unit conversion fails. -/// -public class UnitConversionException : Exception -{ - /// Gets the source unit. - public IUnit? SourceUnit { get; } - - /// Gets the target unit. - public IUnit? TargetUnit { get; } - - /// - /// Initializes a new instance of the UnitConversionException class. - /// - public UnitConversionException() - { - } - - /// - /// Initializes a new instance of the UnitConversionException class with a specified error message. - /// - /// The message that describes the error. - public UnitConversionException(string message) : base(message) - { - } - - /// - /// Initializes a new instance of the UnitConversionException class with a specified error message and a reference to the inner exception that is the cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. - public UnitConversionException(string message, Exception innerException) : base(message, innerException) - { - } - - /// - /// Initializes a new instance of the UnitConversionException class with source and target units and reason. - /// - /// The source unit. - /// The target unit. - /// The reason for the conversion failure. - public UnitConversionException(IUnit source, IUnit target, string reason) : base($"Cannot convert from {source?.Symbol} to {target?.Symbol}: {reason}") - { - SourceUnit = source; - TargetUnit = target; - } -} diff --git a/Semantics.Quantities/Core/UnitExtensions.cs b/Semantics.Quantities/Core/UnitExtensions.cs deleted file mode 100644 index 1f5a685..0000000 --- a/Semantics.Quantities/Core/UnitExtensions.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System; - -/// -/// Extension methods for units. -/// -public static class UnitExtensions -{ - /// - /// Determines if this unit is a base unit (fundamental unit in any system). - /// - /// The unit to check. - /// True if this is a base unit in its system, false otherwise. - public static bool IsBaseUnit(this IUnit unit) - { - Ensure.NotNull(unit); - - return unit.System switch - { - UnitSystem.SIBase => true, - UnitSystem.CGS => Math.Abs(unit.ToBaseFactor - 1.0) < 1e-10, - UnitSystem.SIDerived => throw new NotImplementedException(), - UnitSystem.Metric => throw new NotImplementedException(), - UnitSystem.Imperial => throw new NotImplementedException(), - UnitSystem.USCustomary => throw new NotImplementedException(), - UnitSystem.Atomic => throw new NotImplementedException(), - UnitSystem.Natural => throw new NotImplementedException(), - UnitSystem.Planck => throw new NotImplementedException(), - UnitSystem.Other => throw new NotImplementedException(), - _ => false - }; - } - - /// - /// Determines if this unit belongs to the SI system (either base or derived). - /// - /// The unit to check. - /// True if this is an SI unit, false otherwise. - public static bool IsSI(this IUnit unit) - { - Ensure.NotNull(unit); - return unit.System is UnitSystem.SIBase or UnitSystem.SIDerived; - } - - /// - /// Determines if this unit is metric (SI or other metric systems). - /// - /// The unit to check. - /// True if this is a metric unit, false otherwise. - public static bool IsMetric(this IUnit unit) - { - Ensure.NotNull(unit); - return unit.System is UnitSystem.SIBase or UnitSystem.SIDerived or UnitSystem.Metric or UnitSystem.CGS; - } - - /// - /// Determines if this unit belongs to Imperial or US Customary systems. - /// - /// The unit to check. - /// True if this is an Imperial or US Customary unit, false otherwise. - public static bool IsImperial(this IUnit unit) - { - Ensure.NotNull(unit); - return unit.System is UnitSystem.Imperial or UnitSystem.USCustomary; - } -} diff --git a/Semantics.Quantities/Core/Units.cs b/Semantics.Quantities/Core/Units.cs deleted file mode 100644 index aa0e828..0000000 --- a/Semantics.Quantities/Core/Units.cs +++ /dev/null @@ -1,462 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -/// -/// Static registry of commonly used units organized by physical domain. -/// -public static class Units -{ - /// Meter - SI base unit of length. - public static readonly IUnit Meter = new Unit("meter", "m", PhysicalDimensions.Length, UnitSystem.SIBase); - - /// Kilogram - SI base unit of mass. - public static readonly IUnit Kilogram = new Unit("kilogram", "kg", PhysicalDimensions.Mass, UnitSystem.SIBase); - - /// Second - SI base unit of time. - public static readonly IUnit Second = new Unit("second", "s", PhysicalDimensions.Time, UnitSystem.SIBase); - - /// Ampere - SI base unit of electric current. - public static readonly IUnit Ampere = new Unit("ampere", "A", PhysicalDimensions.ElectricCurrent, UnitSystem.SIBase); - - /// Kelvin - SI base unit of thermodynamic temperature. - public static readonly IUnit Kelvin = new Unit("kelvin", "K", PhysicalDimensions.Temperature, UnitSystem.SIBase); - - /// Mole - SI base unit of amount of substance. - public static readonly IUnit Mole = new Unit("mole", "mol", PhysicalDimensions.AmountOfSubstance, UnitSystem.SIBase); - - /// Candela - SI base unit of luminous intensity. - public static readonly IUnit Candela = new Unit("candela", "cd", PhysicalDimensions.LuminousIntensity, UnitSystem.SIBase); - - /// Kilometer - 1000 meters. - public static readonly IUnit Kilometer = new Unit("kilometer", "km", PhysicalDimensions.Length, UnitSystem.SIDerived, MetricMagnitudes.Kilo); - - /// Centimeter - 0.01 meters. - public static readonly IUnit Centimeter = new Unit("centimeter", "cm", PhysicalDimensions.Length, UnitSystem.SIDerived, MetricMagnitudes.Centi); - - /// Millimeter - 0.001 meters. - public static readonly IUnit Millimeter = new Unit("millimeter", "mm", PhysicalDimensions.Length, UnitSystem.SIDerived, MetricMagnitudes.Milli); - - /// Micrometer - 0.000001 meters. - public static readonly IUnit Micrometer = new Unit("micrometer", "μm", PhysicalDimensions.Length, UnitSystem.SIDerived, MetricMagnitudes.Micro); - - /// Nanometer - 0.000000001 meters. - public static readonly IUnit Nanometer = new Unit("nanometer", "nm", PhysicalDimensions.Length, UnitSystem.SIDerived, MetricMagnitudes.Nano); - - /// Foot - Imperial unit of length. - public static readonly IUnit Foot = new Unit("foot", "ft", PhysicalDimensions.Length, UnitSystem.Imperial, PhysicalConstants.Conversion.FeetToMeters); - - /// Inch - Imperial unit of length. - public static readonly IUnit Inch = new Unit("inch", "in", PhysicalDimensions.Length, UnitSystem.Imperial, PhysicalConstants.Conversion.InchesToMeters); - - /// Yard - Imperial unit of length. - public static readonly IUnit Yard = new Unit("yard", "yd", PhysicalDimensions.Length, UnitSystem.Imperial, PhysicalConstants.Conversion.YardsToMeters); - - /// Mile - Imperial unit of length. - public static readonly IUnit Mile = new Unit("mile", "mi", PhysicalDimensions.Length, UnitSystem.Imperial, PhysicalConstants.Conversion.MilesToMeters); - - /// Nautical mile - International nautical mile. - public static readonly IUnit NauticalMile = new Unit("nautical mile", "nmi", PhysicalDimensions.Length, UnitSystem.Other, PhysicalConstants.Conversion.NauticalMilesToMeters); - - /// Ängström - Common unit in atomic physics. - public static readonly IUnit Angstrom = new Unit("ångström", "Å", PhysicalDimensions.Length, UnitSystem.Other, 1e-10); - - /// Square meter - SI derived unit of area. - public static readonly IUnit SquareMeter = new Unit("square meter", "m²", PhysicalDimensions.Area, UnitSystem.SIDerived); - - /// Square kilometer - 1,000,000 square meters. - public static readonly IUnit SquareKilometer = new Unit("square kilometer", "km²", PhysicalDimensions.Area, UnitSystem.SIDerived, MetricMagnitudes.Kilo * MetricMagnitudes.Kilo); - - /// Square centimeter - 0.0001 square meters. - public static readonly IUnit SquareCentimeter = new Unit("square centimeter", "cm²", PhysicalDimensions.Area, UnitSystem.SIDerived, MetricMagnitudes.Centi * MetricMagnitudes.Centi); - - /// Hectare - 10,000 square meters. - public static readonly IUnit Hectare = new Unit("hectare", "ha", PhysicalDimensions.Area, UnitSystem.Metric, 10000.0); - - /// Square foot - Imperial unit of area. - public static readonly IUnit SquareFoot = new Unit("square foot", "ft²", PhysicalDimensions.Area, UnitSystem.Imperial, PhysicalConstants.Conversion.SquareFeetToSquareMeters); - - /// Square inch - Imperial unit of area. - public static readonly IUnit SquareInch = new Unit("square inch", "in²", PhysicalDimensions.Area, UnitSystem.Imperial, PhysicalConstants.Conversion.SquareInchesToSquareMeters); - - /// Acre - Imperial unit of area. - public static readonly IUnit Acre = new Unit("acre", "ac", PhysicalDimensions.Area, UnitSystem.Imperial, PhysicalConstants.Conversion.AcresToSquareMeters); - - /// Square mile - Imperial unit of area. - public static readonly IUnit SquareMile = new Unit("square mile", "mi²", PhysicalDimensions.Area, UnitSystem.Imperial, PhysicalConstants.Conversion.SquareMilesToSquareMeters); - - /// Cubic meter - SI derived unit of volume. - public static readonly IUnit CubicMeter = new Unit("cubic meter", "m³", PhysicalDimensions.Volume, UnitSystem.SIDerived); - - /// Liter - Common metric unit of volume. - public static readonly IUnit Liter = new Unit("liter", "L", PhysicalDimensions.Volume, UnitSystem.Metric, MetricMagnitudes.Milli); - - /// Milliliter - 0.001 liters. - public static readonly IUnit Milliliter = new Unit("milliliter", "mL", PhysicalDimensions.Volume, UnitSystem.Metric, MetricMagnitudes.Milli * MetricMagnitudes.Milli); - - /// Cubic centimeter - 0.000001 cubic meters. - public static readonly IUnit CubicCentimeter = new Unit("cubic centimeter", "cm³", PhysicalDimensions.Volume, UnitSystem.SIDerived, MetricMagnitudes.Centi * MetricMagnitudes.Centi * MetricMagnitudes.Centi); - - /// Cubic foot - Imperial unit of volume. - public static readonly IUnit CubicFoot = new Unit("cubic foot", "ft³", PhysicalDimensions.Volume, UnitSystem.Imperial, 0.0283168); - - /// Cubic inch - Imperial unit of volume. - public static readonly IUnit CubicInch = new Unit("cubic inch", "in³", PhysicalDimensions.Volume, UnitSystem.Imperial, 0.0000163871); - - /// US gallon - US customary unit of volume. - public static readonly IUnit USGallon = new Unit("US gallon", "gal", PhysicalDimensions.Volume, UnitSystem.USCustomary, 0.00378541); - - /// Imperial gallon - Imperial unit of volume. - public static readonly IUnit ImperialGallon = new Unit("imperial gallon", "imp gal", PhysicalDimensions.Volume, UnitSystem.Imperial, 0.00454609); - - /// US quart - US customary unit of volume. - public static readonly IUnit USQuart = new Unit("US quart", "qt", PhysicalDimensions.Volume, UnitSystem.USCustomary, 0.000946353); - - /// US pint - US customary unit of volume. - public static readonly IUnit USPint = new Unit("US pint", "pt", PhysicalDimensions.Volume, UnitSystem.USCustomary, 0.000473176); - - /// US fluid ounce - US customary unit of volume. - public static readonly IUnit USFluidOunce = new Unit("US fluid ounce", "fl oz", PhysicalDimensions.Volume, UnitSystem.USCustomary, 0.0000295735); - - /// Gram - 0.001 kilograms. - public static readonly IUnit Gram = new Unit("gram", "g", PhysicalDimensions.Mass, UnitSystem.SIDerived, MetricMagnitudes.Milli); - - /// Metric ton - 1000 kilograms. - public static readonly IUnit MetricTon = new Unit("metric ton", "t", PhysicalDimensions.Mass, UnitSystem.SIDerived, MetricMagnitudes.Kilo); - - /// Pound - Imperial unit of mass. - public static readonly IUnit Pound = new Unit("pound", "lb", PhysicalDimensions.Mass, UnitSystem.Imperial, PhysicalConstants.Mechanical.PoundMassToKilogram); - - /// Ounce - Imperial unit of mass. - public static readonly IUnit Ounce = new Unit("ounce", "oz", PhysicalDimensions.Mass, UnitSystem.Imperial, 0.0283495); - - /// Stone - Imperial unit of mass. - public static readonly IUnit Stone = new Unit("stone", "st", PhysicalDimensions.Mass, UnitSystem.Imperial, 6.35029); - - /// Short ton - US customary unit of mass. - public static readonly IUnit ShortTon = new Unit("short ton", "ton", PhysicalDimensions.Mass, UnitSystem.USCustomary, 907.185); - - /// Atomic mass unit - Used in atomic physics. - public static readonly IUnit AtomicMassUnit = new Unit("atomic mass unit", "u", PhysicalDimensions.Mass, UnitSystem.Atomic, PhysicalConstants.Nuclear.AtomicMassUnit); - - /// Minute - 60 seconds. - public static readonly IUnit Minute = new Unit("minute", "min", PhysicalDimensions.Time, UnitSystem.SIDerived, PhysicalConstants.Time.SecondsPerMinute); - - /// Hour - 3600 seconds. - public static readonly IUnit Hour = new Unit("hour", "h", PhysicalDimensions.Time, UnitSystem.SIDerived, PhysicalConstants.Time.SecondsPerHour); - - /// Day - 86400 seconds. - public static readonly IUnit Day = new Unit("day", "d", PhysicalDimensions.Time, UnitSystem.SIDerived, PhysicalConstants.Time.SecondsPerDay); - - /// Week - 604800 seconds. - public static readonly IUnit Week = new Unit("week", "wk", PhysicalDimensions.Time, UnitSystem.SIDerived, PhysicalConstants.Time.SecondsPerWeek); - - /// Year - 365.25 days (Julian year). - public static readonly IUnit Year = new Unit("year", "yr", PhysicalDimensions.Time, UnitSystem.SIDerived, PhysicalConstants.Time.SecondsPerJulianYear); - - /// Millisecond - 0.001 seconds. - public static readonly IUnit Millisecond = new Unit("millisecond", "ms", PhysicalDimensions.Time, UnitSystem.SIDerived, MetricMagnitudes.Milli); - - /// Microsecond - 0.000001 seconds. - public static readonly IUnit Microsecond = new Unit("microsecond", "μs", PhysicalDimensions.Time, UnitSystem.SIDerived, MetricMagnitudes.Micro); - - /// Nanosecond - 0.000000001 seconds. - public static readonly IUnit Nanosecond = new Unit("nanosecond", "ns", PhysicalDimensions.Time, UnitSystem.SIDerived, MetricMagnitudes.Nano); - - /// Radian - SI derived unit of plane angle. - public static readonly IUnit Radian = new Unit("radian", "rad", PhysicalDimensions.Dimensionless, UnitSystem.SIDerived); - - /// Degree - Common angle unit. - public static readonly IUnit Degree = new Unit("degree", "°", PhysicalDimensions.Dimensionless, UnitSystem.Other, Math.PI / 180.0); - - /// Gradian - Alternative angle unit. - public static readonly IUnit Gradian = new Unit("gradian", "grad", PhysicalDimensions.Dimensionless, UnitSystem.Other, Math.PI / 200.0); - - /// Revolution - Full rotation angle unit. - public static readonly IUnit Revolution = new Unit("revolution", "rev", PhysicalDimensions.Dimensionless, UnitSystem.Other, 2.0 * Math.PI); - - /// Milliradian - 0.001 radians. - public static readonly IUnit Milliradian = new Unit("milliradian", "mrad", PhysicalDimensions.Dimensionless, UnitSystem.SIDerived, 0.001); - - /// Meters per second - SI derived unit of velocity. - public static readonly IUnit MetersPerSecond = new Unit("meters per second", "m/s", PhysicalDimensions.Velocity, UnitSystem.SIDerived); - - /// Kilometers per hour - Common velocity unit. - public static readonly IUnit KilometersPerHour = new Unit("kilometers per hour", "km/h", PhysicalDimensions.Velocity, UnitSystem.SIDerived, 1000.0 / PhysicalConstants.Time.SecondsPerHour); - - /// Miles per hour - Imperial velocity unit. - public static readonly IUnit MilesPerHour = new Unit("miles per hour", "mph", PhysicalDimensions.Velocity, UnitSystem.Imperial, PhysicalConstants.Conversion.MilesToMeters / PhysicalConstants.Time.SecondsPerHour); - - /// Knot - Nautical velocity unit. - public static readonly IUnit Knot = new Unit("knot", "kn", PhysicalDimensions.Velocity, UnitSystem.Other, PhysicalConstants.Conversion.NauticalMilesToMeters / PhysicalConstants.Time.SecondsPerHour); - - /// Feet per second - Imperial velocity unit. - public static readonly IUnit FeetPerSecond = new Unit("feet per second", "ft/s", PhysicalDimensions.Velocity, UnitSystem.Imperial, PhysicalConstants.Conversion.FeetToMeters); - - /// Meters per second squared - SI derived unit of acceleration. - public static readonly IUnit MetersPerSecondSquared = new Unit("meters per second squared", "m/s²", PhysicalDimensions.Acceleration, UnitSystem.SIDerived); - - /// Standard gravity - Earth's gravitational acceleration. - public static readonly IUnit StandardGravity = new Unit("standard gravity", "g", PhysicalDimensions.Acceleration, UnitSystem.Other, PhysicalConstants.Mechanical.StandardGravity); - - /// Newton - SI derived unit of force. - public static readonly IUnit Newton = new Unit("newton", "N", PhysicalDimensions.Force, UnitSystem.SIDerived); - - /// Kilonewton - 1000 newtons. - public static readonly IUnit Kilonewton = new Unit("kilonewton", "kN", PhysicalDimensions.Force, UnitSystem.SIDerived, 1000.0); - - /// Pound-force - Imperial unit of force. - public static readonly IUnit PoundForce = new Unit("pound-force", "lbf", PhysicalDimensions.Force, UnitSystem.Imperial, PhysicalConstants.Mechanical.PoundMassToKilogram * PhysicalConstants.Mechanical.StandardGravity); - - /// Dyne - CGS unit of force. - public static readonly IUnit Dyne = new Unit("dyne", "dyn", PhysicalDimensions.Force, UnitSystem.CGS, 1e-5); - - /// Pascal - SI derived unit of pressure. - public static readonly IUnit Pascal = new Unit("pascal", "Pa", PhysicalDimensions.Pressure, UnitSystem.SIDerived); - - /// Kilopascal - 1000 pascals. - public static readonly IUnit Kilopascal = new Unit("kilopascal", "kPa", PhysicalDimensions.Pressure, UnitSystem.SIDerived, 1000.0); - - /// Bar - Metric unit of pressure. - public static readonly IUnit Bar = new Unit("bar", "bar", PhysicalDimensions.Pressure, UnitSystem.Metric, 100000.0); - - /// Atmosphere - Standard atmospheric pressure. - public static readonly IUnit Atmosphere = new Unit("atmosphere", "atm", PhysicalDimensions.Pressure, UnitSystem.Other, PhysicalConstants.Mechanical.StandardAtmosphericPressure); - - /// Pounds per square inch - Imperial pressure unit. - public static readonly IUnit PSI = new Unit("pounds per square inch", "psi", PhysicalDimensions.Pressure, UnitSystem.Imperial, 6894.76); - - /// Torr - Pressure unit based on mercury column. - public static readonly IUnit Torr = new Unit("torr", "Torr", PhysicalDimensions.Pressure, UnitSystem.Other, 133.322); - - /// Joule - SI derived unit of energy. - public static readonly IUnit Joule = new Unit("joule", "J", PhysicalDimensions.Energy, UnitSystem.SIDerived); - - /// Kilojoule - 1000 joules. - public static readonly IUnit Kilojoule = new Unit("kilojoule", "kJ", PhysicalDimensions.Energy, UnitSystem.SIDerived, MetricMagnitudes.Kilo); - - /// Calorie - Common energy unit (thermochemical calorie). - public static readonly IUnit Calorie = new Unit("calorie", "cal", PhysicalDimensions.Energy, UnitSystem.Other, PhysicalConstants.Conversion.CalorieToJoule); - - /// Kilocalorie - 1000 calories (dietary calorie). - public static readonly IUnit Kilocalorie = new Unit("kilocalorie", "kcal", PhysicalDimensions.Energy, UnitSystem.Other, PhysicalConstants.Conversion.CalorieToJoule * 1000.0); - - /// Watt-hour - Energy unit. - public static readonly IUnit WattHour = new Unit("watt-hour", "Wh", PhysicalDimensions.Energy, UnitSystem.SIDerived, PhysicalConstants.Time.SecondsPerHour); - - /// Kilowatt-hour - 1000 watt-hours. - public static readonly IUnit KilowattHour = new Unit("kilowatt-hour", "kWh", PhysicalDimensions.Energy, UnitSystem.SIDerived, PhysicalConstants.Conversion.KilowattHourToJoule); - - /// Erg - CGS unit of energy. - public static readonly IUnit Erg = new Unit("erg", "erg", PhysicalDimensions.Energy, UnitSystem.CGS, 1e-7); - - /// Electron volt - Atomic unit of energy. - public static readonly IUnit ElectronVolt = new Unit("electron volt", "eV", PhysicalDimensions.Energy, UnitSystem.Atomic, PhysicalConstants.Fundamental.ElementaryCharge); - - /// British thermal unit - Imperial energy unit. - public static readonly IUnit BTU = new Unit("British thermal unit", "BTU", PhysicalDimensions.Energy, UnitSystem.Imperial, PhysicalConstants.Conversion.BtuToJoule); - - /// Watt - SI derived unit of power. - public static readonly IUnit Watt = new Unit("watt", "W", PhysicalDimensions.Power, UnitSystem.SIDerived); - - /// Kilowatt - 1000 watts. - public static readonly IUnit Kilowatt = new Unit("kilowatt", "kW", PhysicalDimensions.Power, UnitSystem.SIDerived, MetricMagnitudes.Kilo); - - /// Megawatt - 1,000,000 watts. - public static readonly IUnit Megawatt = new Unit("megawatt", "MW", PhysicalDimensions.Power, UnitSystem.SIDerived, MetricMagnitudes.Mega); - - /// Horsepower - Imperial unit of power (mechanical). - public static readonly IUnit Horsepower = new Unit("horsepower", "hp", PhysicalDimensions.Power, UnitSystem.Imperial, 745.699872); - - /// Celsius - Common temperature scale. - public static readonly IUnit Celsius = new Unit("celsius", "°C", PhysicalDimensions.Temperature, UnitSystem.SIDerived, 1.0, PhysicalConstants.Temperature.AbsoluteZeroInCelsius); - - /// Fahrenheit - Imperial temperature scale. - public static readonly IUnit Fahrenheit = new Unit("fahrenheit", "°F", PhysicalDimensions.Temperature, UnitSystem.Imperial, PhysicalConstants.Conversion.FahrenheitToCelsiusSlope, PhysicalConstants.Temperature.AbsoluteZeroInCelsius - (PhysicalConstants.Conversion.FahrenheitOffset * PhysicalConstants.Conversion.FahrenheitToCelsiusSlope)); - - /// Rankine - Absolute temperature scale based on Fahrenheit. - public static readonly IUnit Rankine = new Unit("rankine", "°R", PhysicalDimensions.Temperature, UnitSystem.Imperial, PhysicalConstants.Conversion.FahrenheitToCelsiusSlope); - - /// Milliampere - 0.001 amperes. - public static readonly IUnit Milliampere = new Unit("milliampere", "mA", PhysicalDimensions.ElectricCurrent, UnitSystem.SIDerived, MetricMagnitudes.Milli); - - /// Kiloampere - 1000 amperes. - public static readonly IUnit Kiloampere = new Unit("kiloampere", "kA", PhysicalDimensions.ElectricCurrent, UnitSystem.SIDerived, MetricMagnitudes.Kilo); - - /// Coulomb - SI derived unit of electric charge. - public static readonly IUnit Coulomb = new Unit("coulomb", "C", PhysicalDimensions.ElectricCharge, UnitSystem.SIDerived); - - /// Ampere-hour - Common unit for battery capacity. - public static readonly IUnit AmpereHour = new Unit("ampere-hour", "Ah", PhysicalDimensions.ElectricCharge, UnitSystem.SIDerived, PhysicalConstants.Time.SecondsPerHour); - - /// Volt - SI derived unit of electric potential. - public static readonly IUnit Volt = new Unit("volt", "V", PhysicalDimensions.ElectricPotential, UnitSystem.SIDerived); - - /// Kilovolt - 1000 volts. - public static readonly IUnit Kilovolt = new Unit("kilovolt", "kV", PhysicalDimensions.ElectricPotential, UnitSystem.SIDerived, MetricMagnitudes.Kilo); - - /// Ohm - SI derived unit of electric resistance. - public static readonly IUnit Ohm = new Unit("ohm", "Ω", PhysicalDimensions.ElectricResistance, UnitSystem.SIDerived); - - /// Kilohm - 1000 ohms. - public static readonly IUnit Kilohm = new Unit("kilohm", "kΩ", PhysicalDimensions.ElectricResistance, UnitSystem.SIDerived, MetricMagnitudes.Kilo); - - /// Megohm - 1,000,000 ohms. - public static readonly IUnit Megohm = new Unit("megohm", "MΩ", PhysicalDimensions.ElectricResistance, UnitSystem.SIDerived, MetricMagnitudes.Mega); - - /// Farad - SI derived unit of electric capacitance. - public static readonly IUnit Farad = new Unit("farad", "F", PhysicalDimensions.ElectricCapacitance, UnitSystem.SIDerived); - - /// Microfarad - 0.000001 farads. - public static readonly IUnit Microfarad = new Unit("microfarad", "μF", PhysicalDimensions.ElectricCapacitance, UnitSystem.SIDerived, MetricMagnitudes.Micro); - - /// Nanofarad - 0.000000001 farads. - public static readonly IUnit Nanofarad = new Unit("nanofarad", "nF", PhysicalDimensions.ElectricCapacitance, UnitSystem.SIDerived, MetricMagnitudes.Nano); - - /// Picofarad - 0.000000000001 farads. - public static readonly IUnit Picofarad = new Unit("picofarad", "pF", PhysicalDimensions.ElectricCapacitance, UnitSystem.SIDerived, MetricMagnitudes.Pico); - - /// Kilomole - 1000 moles. - public static readonly IUnit Kilomole = new Unit("kilomole", "kmol", PhysicalDimensions.AmountOfSubstance, UnitSystem.SIDerived, MetricMagnitudes.Kilo); - - /// Millimole - 0.001 moles. - public static readonly IUnit Millimole = new Unit("millimole", "mmol", PhysicalDimensions.AmountOfSubstance, UnitSystem.SIDerived, MetricMagnitudes.Milli); - - /// Molar - Moles per liter. - public static readonly IUnit Molar = new Unit("molar", "M", PhysicalDimensions.Concentration, UnitSystem.SIDerived, 1000.0); - - /// Parts per million - Dimensionless concentration. - public static readonly IUnit PartsPerMillion = new Unit("parts per million", "ppm", PhysicalDimensions.Dimensionless, UnitSystem.Other, 1e-6); - - /// Millicandela - 0.001 candelas. - public static readonly IUnit Millicandela = new Unit("millicandela", "mcd", PhysicalDimensions.LuminousIntensity, UnitSystem.SIDerived, MetricMagnitudes.Milli); - - /// Lumen - SI derived unit of luminous flux. - public static readonly IUnit Lumen = new Unit("lumen", "lm", PhysicalDimensions.LuminousFlux, UnitSystem.SIDerived); - - /// Lux - SI derived unit of illuminance. - public static readonly IUnit Lux = new Unit("lux", "lx", PhysicalDimensions.Illuminance, UnitSystem.SIDerived); - - /// Foot-candle - Imperial unit of illuminance. - public static readonly IUnit FootCandle = new Unit("foot-candle", "fc", PhysicalDimensions.Illuminance, UnitSystem.Imperial, PhysicalConstants.Conversion.FootCandlesToLux); - - /// Candela per square meter - SI unit of luminance. - public static readonly IUnit CandelaPerSquareMeter = new Unit("candela per square meter", "cd/m²", PhysicalDimensions.Luminance, UnitSystem.SIDerived); - - /// Nit - Common unit of luminance (same as cd/m²). - public static readonly IUnit Nit = new Unit("nit", "nt", PhysicalDimensions.Luminance, UnitSystem.SIDerived); - - /// Foot-lambert - Imperial unit of luminance. - public static readonly IUnit FootLambert = new Unit("foot-lambert", "fL", PhysicalDimensions.Luminance, UnitSystem.Imperial, PhysicalConstants.Conversion.FootLambertsToCandelasPerSquareMeter); - - /// Diopter - SI unit of optical power. - public static readonly IUnit Diopter = new Unit("diopter", "D", PhysicalDimensions.OpticalPower, UnitSystem.SIDerived); - - /// Becquerel - SI derived unit of radioactive activity. - public static readonly IUnit Becquerel = new Unit("becquerel", "Bq", PhysicalDimensions.RadioactiveActivity, UnitSystem.SIDerived); - - /// Curie - Traditional unit of radioactive activity. - public static readonly IUnit Curie = new Unit("curie", "Ci", PhysicalDimensions.RadioactiveActivity, UnitSystem.Other, 3.7e10); - - /// Gray - SI derived unit of absorbed dose. - public static readonly IUnit Gray = new Unit("gray", "Gy", PhysicalDimensions.AbsorbedDose, UnitSystem.SIDerived); - - /// Rad - Traditional unit of absorbed dose. - public static readonly IUnit Rad = new Unit("rad", "rad", PhysicalDimensions.AbsorbedDose, UnitSystem.Other, PhysicalConstants.Conversion.RadsToGrays); - - /// Sievert - SI derived unit of equivalent dose. - public static readonly IUnit Sievert = new Unit("sievert", "Sv", PhysicalDimensions.EquivalentDose, UnitSystem.SIDerived); - - /// Rem - Traditional unit of equivalent dose. - public static readonly IUnit Rem = new Unit("rem", "rem", PhysicalDimensions.EquivalentDose, UnitSystem.Other, PhysicalConstants.Conversion.RemsToSieverts); - - /// Coulomb per kilogram - SI unit of exposure. - public static readonly IUnit CoulombPerKilogram = new Unit("coulomb per kilogram", "C/kg", PhysicalDimensions.Exposure, UnitSystem.SIDerived); - - /// Roentgen - Traditional unit of exposure. - public static readonly IUnit Roentgen = new Unit("roentgen", "R", PhysicalDimensions.Exposure, UnitSystem.Other, PhysicalConstants.Conversion.RoentgensToGraysInAir); - - /// Barn - Unit of nuclear cross section. - public static readonly IUnit Barn = new Unit("barn", "b", PhysicalDimensions.NuclearCrossSection, UnitSystem.Other, 1e-28); - - /// Hertz - SI derived unit of frequency. - public static readonly IUnit Hertz = new Unit("hertz", "Hz", PhysicalDimensions.Frequency, UnitSystem.SIDerived); - - /// Kilohertz - 1000 hertz. - public static readonly IUnit Kilohertz = new Unit("kilohertz", "kHz", PhysicalDimensions.Frequency, UnitSystem.SIDerived, MetricMagnitudes.Kilo); - - /// Megahertz - 1,000,000 hertz. - public static readonly IUnit Megahertz = new Unit("megahertz", "MHz", PhysicalDimensions.Frequency, UnitSystem.SIDerived, MetricMagnitudes.Mega); - - // === CHEMICAL DOMAIN === - - /// Gram per mole - Common unit for molar mass. - public static readonly IUnit GramPerMole = new Unit("gram per mole", "g/mol", PhysicalDimensions.MolarMass, UnitSystem.SIDerived, 0.001); - - /// Kilogram per mole - SI base unit for molar mass. - public static readonly IUnit KilogramPerMole = new Unit("kilogram per mole", "kg/mol", PhysicalDimensions.MolarMass, UnitSystem.SIDerived); - - /// Dalton - Atomic mass unit for molecular masses. - public static readonly IUnit Dalton = new Unit("dalton", "Da", PhysicalDimensions.MolarMass, UnitSystem.Atomic, PhysicalConstants.Nuclear.AtomicMassUnit); - - /// Millimolar - 0.001 molar concentration. - public static readonly IUnit Millimolar = new Unit("millimolar", "mM", PhysicalDimensions.Concentration, UnitSystem.SIDerived, 1.0); - - /// Micromolar - 0.000001 molar concentration. - public static readonly IUnit Micromolar = new Unit("micromolar", "μM", PhysicalDimensions.Concentration, UnitSystem.SIDerived, 0.001); - - /// Parts per billion - Dimensionless concentration. - public static readonly IUnit PartsPerBillion = new Unit("parts per billion", "ppb", PhysicalDimensions.Dimensionless, UnitSystem.Other, 1e-9); - - /// Percent by weight - Dimensionless concentration. - public static readonly IUnit PercentByWeight = new Unit("percent by weight", "% w/w", PhysicalDimensions.Dimensionless, UnitSystem.Other, 0.01); - - /// Moles per second - Reaction rate unit. - public static readonly IUnit MolesPerSecond = new Unit("moles per second", "mol/s", PhysicalDimensions.ReactionRate, UnitSystem.SIDerived, 1000.0); - - /// Joules per mole - Activation energy unit. - public static readonly IUnit JoulesPerMole = new Unit("joules per mole", "J/mol", PhysicalDimensions.ActivationEnergy, UnitSystem.SIDerived); - - /// Kilojoules per mole - Common activation energy unit. - public static readonly IUnit KilojoulesPerMole = new Unit("kilojoules per mole", "kJ/mol", PhysicalDimensions.ActivationEnergy, UnitSystem.SIDerived, 1000.0); - - /// Calories per mole - Traditional activation energy unit. - public static readonly IUnit CaloriesPerMole = new Unit("calories per mole", "cal/mol", PhysicalDimensions.ActivationEnergy, UnitSystem.Other, 4.184); - - /// Per second - First-order rate constant. - public static readonly IUnit PerSecond = new Unit("per second", "s⁻¹", PhysicalDimensions.RateConstant, UnitSystem.SIDerived); - - /// Katal - SI unit of enzyme activity. - public static readonly IUnit Katal = new Unit("katal", "kat", PhysicalDimensions.EnzymeActivity, UnitSystem.SIDerived); - - /// Enzyme unit - Traditional enzyme activity unit. - public static readonly IUnit EnzymeUnit = new Unit("enzyme unit", "U", PhysicalDimensions.EnzymeActivity, UnitSystem.Other, 1.0 / 60.0e6); // μmol/min - - /// Newton per meter - Surface tension unit. - public static readonly IUnit NewtonPerMeter = new Unit("newton per meter", "N/m", PhysicalDimensions.SurfaceTension, UnitSystem.SIDerived); - - /// Dyne per centimeter - CGS surface tension unit. - public static readonly IUnit DynePerCentimeter = new Unit("dyne per centimeter", "dyn/cm", PhysicalDimensions.SurfaceTension, UnitSystem.CGS, 1e-3); - - /// Pascal second - Dynamic viscosity unit. - public static readonly IUnit PascalSecond = new Unit("pascal second", "Pa·s", PhysicalDimensions.DynamicViscosity, UnitSystem.SIDerived); - - /// Poise - CGS dynamic viscosity unit. - public static readonly IUnit Poise = new Unit("poise", "P", PhysicalDimensions.DynamicViscosity, UnitSystem.CGS, 0.1); - - /// Centipoise - Common dynamic viscosity unit. - public static readonly IUnit Centipoise = new Unit("centipoise", "cP", PhysicalDimensions.DynamicViscosity, UnitSystem.CGS, 0.001); - - // === FLUID DYNAMICS DOMAIN === - - /// Kilogram per cubic meter - SI base unit for density. - public static readonly IUnit KilogramPerCubicMeter = new Unit("kilogram per cubic meter", "kg/m³", PhysicalDimensions.Density, UnitSystem.SIDerived); - - /// Gram per cubic centimeter - Common density unit. - public static readonly IUnit GramPerCubicCentimeter = new Unit("gram per cubic centimeter", "g/cm³", PhysicalDimensions.Density, UnitSystem.SIDerived, 1000.0); - - /// Gram per liter - Common density unit for liquids. - public static readonly IUnit GramPerLiter = new Unit("gram per liter", "g/L", PhysicalDimensions.Density, UnitSystem.SIDerived, 1.0); -} diff --git a/Semantics.Quantities/Electrical/ElectricCapacitance.cs b/Semantics.Quantities/Electrical/ElectricCapacitance.cs deleted file mode 100644 index 667615a..0000000 --- a/Semantics.Quantities/Electrical/ElectricCapacitance.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an electric capacitance quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record ElectricCapacitance : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of electriccapacitance [M⁻¹ L⁻² T⁴ I²]. - public override PhysicalDimension Dimension => PhysicalDimensions.ElectricCapacitance; - - /// - /// Initializes a new instance of the class. - /// - public ElectricCapacitance() : base() { } - - /// - /// Creates a new ElectricCapacitance from a value in farads. - /// - /// The value in farads. - /// A new ElectricCapacitance instance. - public static ElectricCapacitance FromFarads(T farads) => Create(farads); - - /// - /// Calculates charge from capacitance and voltage (Q = C×V). - /// - /// The capacitance. - /// The voltage. - /// The resulting charge. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static ElectricCharge operator *(ElectricCapacitance capacitance, ElectricPotential voltage) - { - Ensure.NotNull(capacitance); - Ensure.NotNull(voltage); - - T chargeValue = capacitance.Value * voltage.Value; - - return ElectricCharge.Create(chargeValue); - } - - /// - /// Calculates voltage from capacitance and charge (V = Q/C). - /// - /// The charge. - /// The capacitance. - /// The resulting voltage. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static ElectricPotential operator /(ElectricCharge charge, ElectricCapacitance capacitance) - { - Ensure.NotNull(charge); - Ensure.NotNull(capacitance); - - T voltageValue = charge.Value / capacitance.Value; - - return ElectricPotential.Create(voltageValue); - } -} diff --git a/Semantics.Quantities/Electrical/ElectricCharge.cs b/Semantics.Quantities/Electrical/ElectricCharge.cs deleted file mode 100644 index e27e494..0000000 --- a/Semantics.Quantities/Electrical/ElectricCharge.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an electric charge quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record ElectricCharge : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of electriccharge [I T]. - public override PhysicalDimension Dimension => PhysicalDimensions.ElectricCharge; - - /// - /// Initializes a new instance of the class. - /// - public ElectricCharge() : base() { } - - /// - /// Creates a new ElectricCharge from a value in coulombs. - /// - /// The value in coulombs. - /// A new ElectricCharge instance. - public static ElectricCharge FromCoulombs(T coulombs) => Create(coulombs); - - /// - /// Calculates current from charge and time (I = Q/t). - /// - /// The electric charge. - /// The time duration. - /// The resulting electric current. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static ElectricCurrent operator /(ElectricCharge charge, Time time) - { - Ensure.NotNull(charge); - Ensure.NotNull(time); - - T currentValue = charge.Value / time.Value; - - return ElectricCurrent.Create(currentValue); - } - - /// - /// Calculates time from charge and current (t = Q/I). - /// - /// The electric charge. - /// The electric current. - /// The resulting time duration. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Time operator /(ElectricCharge charge, ElectricCurrent current) - { - Ensure.NotNull(charge); - Ensure.NotNull(current); - - T timeValue = charge.Value / current.Value; - - return Time.Create(timeValue); - } -} diff --git a/Semantics.Quantities/Electrical/ElectricConductivity.cs b/Semantics.Quantities/Electrical/ElectricConductivity.cs deleted file mode 100644 index ab1ac13..0000000 --- a/Semantics.Quantities/Electrical/ElectricConductivity.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an electric conductivity quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record ElectricConductivity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of electricconductivity [M⁻¹ L⁻³ T³ I²]. - public override PhysicalDimension Dimension => PhysicalDimensions.ElectricConductivity; - - /// - /// Initializes a new instance of the class. - /// - public ElectricConductivity() : base() { } - - /// - /// Creates a new ElectricConductivity from a value in siemens per meter. - /// - /// The value in siemens per meter. - /// A new ElectricConductivity instance. - public static ElectricConductivity FromSiemensPerMeter(T siemensPerMeter) => Create(siemensPerMeter); - - /// - /// Divides electric current density by electric field to get conductivity. - /// - /// The electric current density (amperes per square meter). - /// The electric field. - /// The resulting electric conductivity. - public static ElectricConductivity Divide(T currentDensity, ElectricField electricField) - { - Ensure.NotNull(electricField); - return Create(currentDensity / electricField.Value); - } -} diff --git a/Semantics.Quantities/Electrical/ElectricCurrent.cs b/Semantics.Quantities/Electrical/ElectricCurrent.cs deleted file mode 100644 index f95eb78..0000000 --- a/Semantics.Quantities/Electrical/ElectricCurrent.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an electric current quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record ElectricCurrent : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of electriccurrent [I]. - public override PhysicalDimension Dimension => PhysicalDimensions.ElectricCurrent; - - /// - /// Initializes a new instance of the class. - /// - public ElectricCurrent() : base() { } - - /// - /// Creates a new ElectricCurrent from a value in amperes. - /// - /// The value in amperes. - /// A new ElectricCurrent instance. - public static ElectricCurrent FromAmperes(T amperes) => Create(amperes); - - /// - /// Calculates electric charge from current and time (Q = I×t). - /// - /// The electric current. - /// The time duration. - /// The resulting electric charge. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static ElectricCharge operator *(ElectricCurrent current, Time time) - { - Ensure.NotNull(current); - Ensure.NotNull(time); - - T chargeValue = current.Value * time.Value; - - return ElectricCharge.Create(chargeValue); - } - - /// - /// Calculates electric charge from time and current (Q = I×t). - /// - /// The time duration. - /// The electric current. - /// The resulting electric charge. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static ElectricCharge operator *(Time time, ElectricCurrent current) => current * time; - - /// - /// Calculates electric potential from current and resistance using Ohm's law (V = I×R). - /// - /// The electric current. - /// The electric resistance. - /// The resulting electric potential. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static ElectricPotential operator *(ElectricCurrent current, ElectricResistance resistance) - { - Ensure.NotNull(current); - Ensure.NotNull(resistance); - - T voltageValue = current.Value * resistance.Value; - - return ElectricPotential.Create(voltageValue); - } - - /// - /// Calculates resistance from current and voltage (R = V/I). - /// - /// The electric potential. - /// The electric current. - /// The resulting electric resistance. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static ElectricResistance operator /(ElectricPotential voltage, ElectricCurrent current) - { - Ensure.NotNull(voltage); - Ensure.NotNull(current); - - T resistanceValue = voltage.Value / current.Value; - - return ElectricResistance.Create(resistanceValue); - } -} diff --git a/Semantics.Quantities/Electrical/ElectricField.cs b/Semantics.Quantities/Electrical/ElectricField.cs deleted file mode 100644 index a578661..0000000 --- a/Semantics.Quantities/Electrical/ElectricField.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an electric field quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record ElectricField : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of electricfield [M L T⁻³ I⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.ElectricField; - - /// - /// Initializes a new instance of the class. - /// - public ElectricField() : base() { } - - /// - /// Creates a new ElectricField from a value in volts per meter. - /// - /// The value in volts per meter. - /// A new ElectricField instance. - public static ElectricField FromVoltsPerMeter(T voltsPerMeter) => Create(voltsPerMeter); - - /// - /// Divides electric potential by length to create electric field. - /// - /// The electric potential. - /// The length. - /// The resulting electric field. - public static ElectricField Divide(ElectricPotential potential, Length length) - { - Ensure.NotNull(potential); - Ensure.NotNull(length); - return Create(potential.Value / length.Value); - } - - /// - /// Multiplies electric field by length to get electric potential. - /// - /// The electric field. - /// The length. - /// The resulting electric potential. - public static ElectricPotential Multiply(ElectricField field, Length length) - { - Ensure.NotNull(field); - Ensure.NotNull(length); - return ElectricPotential.Create(field.Value * length.Value); - } -} diff --git a/Semantics.Quantities/Electrical/ElectricFlux.cs b/Semantics.Quantities/Electrical/ElectricFlux.cs deleted file mode 100644 index 131d672..0000000 --- a/Semantics.Quantities/Electrical/ElectricFlux.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an electric flux quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record ElectricFlux : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of electricflux [M L³ T⁻³ I⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.ElectricFlux; - - /// - /// Initializes a new instance of the class. - /// - public ElectricFlux() : base() { } - - /// - /// Creates a new ElectricFlux from a value in volt-meters. - /// - /// The value in volt-meters. - /// A new ElectricFlux instance. - public static ElectricFlux FromVoltMeters(T voltMeters) => Create(voltMeters); - - /// - /// Multiplies electric field by area to create electric flux. - /// - /// The electric field. - /// The area. - /// The resulting electric flux. - public static ElectricFlux Multiply(ElectricField field, Area area) - { - Ensure.NotNull(field); - Ensure.NotNull(area); - return Create(field.Value * area.Value); - } - - /// - /// Divides electric flux by area to get electric field. - /// - /// The electric flux. - /// The area. - /// The resulting electric field. - public static ElectricField Divide(ElectricFlux flux, Area area) - { - Ensure.NotNull(flux); - Ensure.NotNull(area); - return ElectricField.Create(flux.Value / area.Value); - } -} diff --git a/Semantics.Quantities/Electrical/ElectricPotential.cs b/Semantics.Quantities/Electrical/ElectricPotential.cs deleted file mode 100644 index 3b2718a..0000000 --- a/Semantics.Quantities/Electrical/ElectricPotential.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an electric potential quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record ElectricPotential : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of electricpotential [M L² T⁻³ I⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.ElectricPotential; - - /// - /// Initializes a new instance of the class. - /// - public ElectricPotential() : base() { } - - /// - /// Creates a new ElectricPotential from a value in volts. - /// - /// The value in volts. - /// A new ElectricPotential instance. - public static ElectricPotential FromVolts(T volts) => Create(volts); - - /// - /// Calculates power from voltage and current (P = V×I). - /// - /// The electric potential. - /// The electric current. - /// The resulting electrical power. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Power operator *(ElectricPotential voltage, ElectricCurrent current) - { - Ensure.NotNull(voltage); - Ensure.NotNull(current); - - T powerValue = voltage.Value * current.Value; - - return Power.Create(powerValue); - } - - /// - /// Calculates current from voltage and resistance (I = V/R). - /// - /// The electric potential. - /// The electric resistance. - /// The resulting electric current. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static ElectricCurrent operator /(ElectricPotential voltage, ElectricResistance resistance) - { - Ensure.NotNull(voltage); - Ensure.NotNull(resistance); - - T currentValue = voltage.Value / resistance.Value; - - return ElectricCurrent.Create(currentValue); - } - - /// - /// Calculates electric field from voltage and distance (E = V/d). - /// - /// The electric potential. - /// The distance. - /// The resulting electric field. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static ElectricField operator /(ElectricPotential voltage, Length distance) - { - Ensure.NotNull(voltage); - Ensure.NotNull(distance); - - T fieldValue = voltage.Value / distance.Value; - - return ElectricField.Create(fieldValue); - } -} diff --git a/Semantics.Quantities/Electrical/ElectricPowerDensity.cs b/Semantics.Quantities/Electrical/ElectricPowerDensity.cs deleted file mode 100644 index a8eeaf0..0000000 --- a/Semantics.Quantities/Electrical/ElectricPowerDensity.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an electric power density quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record ElectricPowerDensity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of electricpowerdensity [M T⁻³]. - public override PhysicalDimension Dimension => PhysicalDimensions.ElectricPowerDensity; - - /// - /// Initializes a new instance of the class. - /// - public ElectricPowerDensity() : base() { } - - /// - /// Creates a new ElectricPowerDensity from a value in watts per cubic meter. - /// - /// The value in watts per cubic meter. - /// A new ElectricPowerDensity instance. - public static ElectricPowerDensity FromWattsPerCubicMeter(T wattsPerCubicMeter) => Create(wattsPerCubicMeter); - - /// - /// Divides power by volume to create power density. - /// - /// The power. - /// The volume. - /// The resulting power density. - public static ElectricPowerDensity Divide(Power power, Volume volume) - { - Ensure.NotNull(power); - Ensure.NotNull(volume); - return Create(power.Value / volume.Value); - } - - /// - /// Multiplies power density by volume to get power. - /// - /// The power density. - /// The volume. - /// The resulting power. - public static Power Multiply(ElectricPowerDensity powerDensity, Volume volume) - { - Ensure.NotNull(powerDensity); - Ensure.NotNull(volume); - return Power.Create(powerDensity.Value * volume.Value); - } -} diff --git a/Semantics.Quantities/Electrical/ElectricResistance.cs b/Semantics.Quantities/Electrical/ElectricResistance.cs deleted file mode 100644 index 9901aad..0000000 --- a/Semantics.Quantities/Electrical/ElectricResistance.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an electric resistance quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record ElectricResistance : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of electricresistance [M L² T⁻³ I⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.ElectricResistance; - - /// - /// Initializes a new instance of the class. - /// - public ElectricResistance() : base() { } - - /// - /// Creates a new ElectricResistance from a value in ohms. - /// - /// The value in ohms. - /// A new ElectricResistance instance. - public static ElectricResistance FromOhms(T ohms) => Create(ohms); - - /// - /// Calculates electric potential from resistance and current using Ohm's law (V = I×R). - /// - /// The electric resistance. - /// The electric current. - /// The resulting electric potential. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static ElectricPotential operator *(ElectricResistance resistance, ElectricCurrent current) - { - Ensure.NotNull(resistance); - Ensure.NotNull(current); - - T voltageValue = resistance.Value * current.Value; - - return ElectricPotential.Create(voltageValue); - } - - /// - /// Calculates current from resistance and voltage (I = V/R). - /// - /// The electric potential. - /// The electric resistance. - /// The resulting electric current. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static ElectricCurrent operator /(ElectricPotential voltage, ElectricResistance resistance) - { - Ensure.NotNull(voltage); - Ensure.NotNull(resistance); - - T currentValue = voltage.Value / resistance.Value; - - return ElectricCurrent.Create(currentValue); - } -} diff --git a/Semantics.Quantities/Electrical/ImpedanceAC.cs b/Semantics.Quantities/Electrical/ImpedanceAC.cs deleted file mode 100644 index 671deae..0000000 --- a/Semantics.Quantities/Electrical/ImpedanceAC.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an AC impedance quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record ImpedanceAC : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of impedanceac [M L² T⁻³ I⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.ImpedanceAC; - - /// - /// Initializes a new instance of the class. - /// - public ImpedanceAC() : base() { } - - /// - /// Creates a new ImpedanceAC from a value in ohms. - /// - /// The value in ohms. - /// A new ImpedanceAC instance. - public static ImpedanceAC FromOhms(T ohms) => Create(ohms); - - /// - /// Divides electric potential by electric current to create impedance. - /// - /// The electric potential (AC voltage). - /// The electric current (AC current). - /// The resulting AC impedance. - public static ImpedanceAC Divide(ElectricPotential potential, ElectricCurrent current) - { - Ensure.NotNull(potential); - Ensure.NotNull(current); - return Create(potential.Value / current.Value); - } - - /// - /// Multiplies impedance by current to get potential. - /// - /// The AC impedance. - /// The electric current. - /// The resulting electric potential. - public static ElectricPotential Multiply(ImpedanceAC impedance, ElectricCurrent current) - { - Ensure.NotNull(impedance); - Ensure.NotNull(current); - return ElectricPotential.Create(impedance.Value * current.Value); - } -} diff --git a/Semantics.Quantities/Electrical/Permittivity.cs b/Semantics.Quantities/Electrical/Permittivity.cs deleted file mode 100644 index 5f268a6..0000000 --- a/Semantics.Quantities/Electrical/Permittivity.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a permittivity quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Permittivity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of permittivity [M⁻¹ L⁻³ T⁴ I²]. - public override PhysicalDimension Dimension => PhysicalDimensions.Permittivity; - - /// - /// Initializes a new instance of the class. - /// - public Permittivity() : base() { } - - /// - /// Creates a new Permittivity from a value in farads per meter. - /// - /// The value in farads per meter. - /// A new Permittivity instance. - public static Permittivity FromFaradsPerMeter(T faradsPerMeter) => Create(faradsPerMeter); -} diff --git a/Semantics.Quantities/FluidDynamics/BulkModulus.cs b/Semantics.Quantities/FluidDynamics/BulkModulus.cs deleted file mode 100644 index 3de2f1c..0000000 --- a/Semantics.Quantities/FluidDynamics/BulkModulus.cs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents bulk modulus with a specific unit of measurement. -/// Bulk modulus is a measure of a substance's resistance to uniform compression. -/// It is measured in pascals (Pa) in the SI system. -/// -/// The numeric type for the bulk modulus value. -public sealed record BulkModulus : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of bulk modulus [M L⁻¹ T⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.BulkModulus; - - /// Initializes a new instance of the class. - public BulkModulus() : base() { } - - /// Creates a new bulk modulus from a value in pascals. - /// The bulk modulus in pascals. - /// A new BulkModulus instance. - public static BulkModulus FromPascals(T pascals) => Create(pascals); - - /// Creates a new bulk modulus from a value in gigapascals. - /// The bulk modulus in gigapascals. - /// A new BulkModulus instance. - public static BulkModulus FromGigapascals(T gigapascals) - { - T pascals = gigapascals * T.CreateChecked(1e9); - return Create(pascals); - } - - /// Creates a new bulk modulus from a value in megapascals. - /// The bulk modulus in megapascals. - /// A new BulkModulus instance. - public static BulkModulus FromMegapascals(T megapascals) - { - T pascals = megapascals * T.CreateChecked(1e6); - return Create(pascals); - } - - /// Creates a new bulk modulus from a value in kilopascals. - /// The bulk modulus in kilopascals. - /// A new BulkModulus instance. - public static BulkModulus FromKilopascals(T kilopascals) - { - T pascals = kilopascals * T.CreateChecked(1000); - return Create(pascals); - } - - /// Creates a new bulk modulus from pressure change and relative volume change. - /// The change in pressure. - /// The relative change in volume (ΔV/V₀). - /// A new BulkModulus instance. - /// - /// Uses the relationship: K = -ΔP / (ΔV/V₀) - /// where K is bulk modulus, ΔP is pressure change, and ΔV/V₀ is relative volume change. - /// The negative sign accounts for the fact that increased pressure typically decreases volume. - /// - public static BulkModulus FromPressureAndVolumeChange(Pressure pressureChange, T relativeVolumeChange) - { - Ensure.NotNull(pressureChange); - - if (relativeVolumeChange == T.Zero) - { - throw new ArgumentException("Relative volume change cannot be zero.", nameof(relativeVolumeChange)); - } - - T deltaP = pressureChange.In(Units.Pascal); - T bulkModulus = -deltaP / relativeVolumeChange; - return Create(bulkModulus); - } - - /// Calculates the relative volume change from bulk modulus and pressure change. - /// The change in pressure. - /// The relative volume change (ΔV/V₀). - /// - /// Uses the relationship: ΔV/V₀ = -ΔP / K - /// where ΔV/V₀ is relative volume change, ΔP is pressure change, and K is bulk modulus. - /// - public T CalculateRelativeVolumeChange(Pressure pressureChange) - { - Ensure.NotNull(pressureChange); - - T deltaP = pressureChange.In(Units.Pascal); - T k = In(Units.Pascal); - - return k == T.Zero ? throw new InvalidOperationException("Bulk modulus cannot be zero for volume change calculation.") : -deltaP / k; - } - - /// Calculates the compressibility from bulk modulus. - /// The compressibility (1/Pa). - /// - /// Uses the relationship: β = 1/K - /// where β is compressibility and K is bulk modulus. - /// - public T CalculateCompressibility() - { - T k = In(Units.Pascal); - - return k == T.Zero ? throw new InvalidOperationException("Bulk modulus cannot be zero for compressibility calculation.") : T.One / k; - } - - /// Calculates the speed of sound in the material from bulk modulus and density. - /// The material density. - /// The speed of sound. - /// - /// Uses the relationship: c = √(K / ρ) - /// where c is speed of sound, K is bulk modulus, and ρ is density. - /// This is the Newton-Laplace equation for sound speed in fluids. - /// - public Velocity CalculateSpeedOfSound(Density density) - { - Ensure.NotNull(density); - - T k = In(Units.Pascal); - T rho = density.In(Units.Kilogram); - - if (rho == T.Zero) - { - throw new ArgumentException("Density cannot be zero.", nameof(density)); - } - - T speedSquared = k / rho; - T speed = T.CreateChecked(Math.Sqrt(double.CreateChecked(speedSquared))); - return Velocity.Create(speed); - } - - /// Calculates pressure change from bulk modulus and relative volume change. - /// The relative change in volume (ΔV/V₀). - /// The pressure change. - /// - /// Uses the relationship: ΔP = -K × (ΔV/V₀) - /// where ΔP is pressure change, K is bulk modulus, and ΔV/V₀ is relative volume change. - /// - public Pressure CalculatePressureChange(T relativeVolumeChange) - { - T k = In(Units.Pascal); - T pressureChange = -k * relativeVolumeChange; - return Pressure.Create(pressureChange); - } - - /// Determines if this bulk modulus is typical for water. - /// True if the value is close to water's bulk modulus (≈ 2.2 GPa at 20°C). - public bool IsTypicalForWater() - { - T modulus = In(Units.Pascal); - T waterModulus = T.CreateChecked(2.2e9); // 2.2 GPa - T tolerance = T.CreateChecked(0.5e9); // ±0.5 GPa - T difference = T.Abs(modulus - waterModulus); - return difference <= tolerance; - } - - /// Determines if this bulk modulus is typical for steel. - /// True if the value is close to steel's bulk modulus (≈ 160 GPa). - public bool IsTypicalForSteel() - { - T modulus = In(Units.Pascal); - T steelModulus = T.CreateChecked(160e9); // 160 GPa - T tolerance = T.CreateChecked(20e9); // ±20 GPa - T difference = T.Abs(modulus - steelModulus); - return difference <= tolerance; - } - - /// Determines if this material is essentially incompressible. - /// True if the bulk modulus is very high (> 100 GPa), indicating low compressibility. - public bool IsEssentiallyIncompressible() - { - T modulus = In(Units.Pascal); - T threshold = T.CreateChecked(100e9); // 100 GPa - return modulus > threshold; - } -} diff --git a/Semantics.Quantities/FluidDynamics/KinematicViscosity.cs b/Semantics.Quantities/FluidDynamics/KinematicViscosity.cs deleted file mode 100644 index 861a5c7..0000000 --- a/Semantics.Quantities/FluidDynamics/KinematicViscosity.cs +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents kinematic viscosity with a specific unit of measurement. -/// Kinematic viscosity is the ratio of dynamic viscosity to fluid density. -/// It is measured in square meters per second (m²/s) in the SI system. -/// -/// The numeric type for the kinematic viscosity value. -public sealed record KinematicViscosity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of kinematic viscosity [L² T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.KinematicViscosity; - - /// Initializes a new instance of the class. - public KinematicViscosity() : base() { } - - /// Creates a new kinematic viscosity from a value in square meters per second. - /// The kinematic viscosity in m²/s. - /// A new KinematicViscosity instance. - public static KinematicViscosity FromSquareMetersPerSecond(T squareMetersPerSecond) => Create(squareMetersPerSecond); - - /// Creates a new kinematic viscosity from a value in stokes. - /// The kinematic viscosity in stokes. - /// A new KinematicViscosity instance. - public static KinematicViscosity FromStokes(T stokes) - { - T squareMetersPerSecond = stokes * T.CreateChecked(1e-4); - return Create(squareMetersPerSecond); - } - - /// Creates a new kinematic viscosity from a value in centistokes. - /// The kinematic viscosity in centistokes. - /// A new KinematicViscosity instance. - public static KinematicViscosity FromCentistokes(T centistokes) - { - T squareMetersPerSecond = centistokes * T.CreateChecked(1e-6); - return Create(squareMetersPerSecond); - } - - /// Creates a new kinematic viscosity from dynamic viscosity and density. - /// The dynamic viscosity. - /// The fluid density. - /// A new KinematicViscosity instance. - /// - /// Uses the relationship: ν = μ / ρ - /// where ν is kinematic viscosity, μ is dynamic viscosity, and ρ is density. - /// - public static KinematicViscosity FromDynamicViscosityAndDensity(DynamicViscosity dynamicViscosity, Density density) - { - Ensure.NotNull(dynamicViscosity); - Ensure.NotNull(density); - - T mu = dynamicViscosity.In(Units.Pascal); - T rho = density.In(Units.Kilogram); - - if (rho == T.Zero) - { - throw new ArgumentException("Density cannot be zero.", nameof(density)); - } - - T kinematicViscosity = mu / rho; - return Create(kinematicViscosity); - } - - /// Calculates dynamic viscosity from kinematic viscosity and density. - /// The fluid density. - /// The dynamic viscosity. - /// - /// Uses the relationship: μ = ν × ρ - /// where μ is dynamic viscosity, ν is kinematic viscosity, and ρ is density. - /// - public DynamicViscosity CalculateDynamicViscosity(Density density) - { - Ensure.NotNull(density); - - T nu = In(Units.Meter); - T rho = density.In(Units.Kilogram); - T dynamicViscosity = nu * rho; - - return DynamicViscosity.Create(dynamicViscosity); - } - - /// Calculates fluid density from kinematic viscosity and dynamic viscosity. - /// The dynamic viscosity. - /// The fluid density. - /// - /// Uses the relationship: ρ = μ / ν - /// where ρ is density, μ is dynamic viscosity, and ν is kinematic viscosity. - /// - public Density CalculateDensity(DynamicViscosity dynamicViscosity) - { - Ensure.NotNull(dynamicViscosity); - - T nu = In(Units.Meter); - T mu = dynamicViscosity.In(Units.Pascal); - - if (nu == T.Zero) - { - throw new InvalidOperationException("Kinematic viscosity cannot be zero for density calculation."); - } - - T density = mu / nu; - return Density.Create(density); - } - - /// Calculates Reynolds number from velocity, characteristic length, and kinematic viscosity. - /// The flow velocity. - /// The characteristic length. - /// The Reynolds number (dimensionless). - /// - /// Uses the relationship: Re = (v × L) / ν - /// where Re is Reynolds number, v is velocity, L is characteristic length, and ν is kinematic viscosity. - /// - public T CalculateReynoldsNumber(Velocity velocity, Length characteristicLength) - { - Ensure.NotNull(velocity); - Ensure.NotNull(characteristicLength); - - T v = velocity.In(Units.MetersPerSecond); - T length = characteristicLength.In(Units.Meter); - T nu = In(Units.Meter); - - return nu == T.Zero - ? throw new InvalidOperationException("Kinematic viscosity cannot be zero for Reynolds number calculation.") - : v * length / nu; - } - - /// Determines if the flow is laminar based on Reynolds number criteria. - /// The flow velocity. - /// The characteristic length. - /// The Reynolds number threshold for laminar flow (default: 2300 for pipe flow). - /// True if the flow is laminar (Re < threshold). - public bool IsLaminarFlow(Velocity velocity, Length characteristicLength, T? laminarThreshold = null) - { - T threshold = laminarThreshold ?? T.CreateChecked(2300); // Default for pipe flow - T reynoldsNumber = CalculateReynoldsNumber(velocity, characteristicLength); - return reynoldsNumber < threshold; - } - - /// Determines if the flow is turbulent based on Reynolds number criteria. - /// The flow velocity. - /// The characteristic length. - /// The Reynolds number threshold for turbulent flow (default: 4000 for pipe flow). - /// True if the flow is turbulent (Re > threshold). - public bool IsTurbulentFlow(Velocity velocity, Length characteristicLength, T? turbulentThreshold = null) - { - T threshold = turbulentThreshold ?? T.CreateChecked(4000); // Default for pipe flow - T reynoldsNumber = CalculateReynoldsNumber(velocity, characteristicLength); - return reynoldsNumber > threshold; - } - - /// Determines if this kinematic viscosity is typical for water at room temperature. - /// True if the value is close to water's kinematic viscosity (≈ 1.0 × 10⁻⁶ m²/s at 20°C). - public bool IsTypicalForWater() - { - T viscosity = In(Units.Meter); - T waterViscosity = T.CreateChecked(1.0e-6); // m²/s at 20°C - T tolerance = T.CreateChecked(0.5e-6); - T difference = T.Abs(viscosity - waterViscosity); - return difference <= tolerance; - } - - /// Determines if this kinematic viscosity is typical for air at standard conditions. - /// True if the value is close to air's kinematic viscosity (≈ 1.5 × 10⁻⁵ m²/s at 20°C). - public bool IsTypicalForAir() - { - T viscosity = In(Units.Meter); - T airViscosity = T.CreateChecked(1.5e-5); // m²/s at 20°C - T tolerance = T.CreateChecked(0.5e-5); - T difference = T.Abs(viscosity - airViscosity); - return difference <= tolerance; - } -} diff --git a/Semantics.Quantities/FluidDynamics/MassFlowRate.cs b/Semantics.Quantities/FluidDynamics/MassFlowRate.cs deleted file mode 100644 index c628503..0000000 --- a/Semantics.Quantities/FluidDynamics/MassFlowRate.cs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents mass flow rate with a specific unit of measurement. -/// Mass flow rate is the mass of substance that passes per unit time. -/// It is measured in kilograms per second (kg/s) in the SI system. -/// -/// The numeric type for the mass flow rate value. -public sealed record MassFlowRate : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of mass flow rate [M T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.MassFlowRate; - - /// Initializes a new instance of the class. - public MassFlowRate() : base() { } - - /// Creates a new mass flow rate from a value in kilograms per second. - /// The mass flow rate in kg/s. - /// A new MassFlowRate instance. - public static MassFlowRate FromKilogramsPerSecond(T kilogramsPerSecond) => Create(kilogramsPerSecond); - - /// Creates a new mass flow rate from a value in grams per second. - /// The mass flow rate in g/s. - /// A new MassFlowRate instance. - public static MassFlowRate FromGramsPerSecond(T gramsPerSecond) - { - T kilogramsPerSecond = gramsPerSecond / T.CreateChecked(1000); - return Create(kilogramsPerSecond); - } - - /// Creates a new mass flow rate from a value in kilograms per hour. - /// The mass flow rate in kg/h. - /// A new MassFlowRate instance. - public static MassFlowRate FromKilogramsPerHour(T kilogramsPerHour) - { - T kilogramsPerSecond = kilogramsPerHour / T.CreateChecked(3600); - return Create(kilogramsPerSecond); - } - - /// Creates a new mass flow rate from a value in pounds per second. - /// The mass flow rate in lb/s. - /// A new MassFlowRate instance. - public static MassFlowRate FromPoundsPerSecond(T poundsPerSecond) - { - T kilogramsPerSecond = poundsPerSecond * PhysicalConstants.Generic.PoundMassToKilogram(); - return Create(kilogramsPerSecond); - } - - /// Creates a new mass flow rate from volumetric flow rate and density. - /// The volumetric flow rate. - /// The fluid density. - /// A new MassFlowRate instance. - /// - /// Uses the relationship: ṁ = ρ × Q - /// where ṁ is mass flow rate, ρ is density, and Q is volumetric flow rate. - /// - public static MassFlowRate FromVolumetricFlowRateAndDensity(VolumetricFlowRate volumetricFlowRate, Density density) - { - Ensure.NotNull(volumetricFlowRate); - Ensure.NotNull(density); - - T q = volumetricFlowRate.In(Units.CubicMeter); - T rho = density.In(Units.Kilogram); - T massFlowRate = rho * q; - return Create(massFlowRate); - } - - /// Calculates volumetric flow rate from mass flow rate and density. - /// The fluid density. - /// The volumetric flow rate. - /// - /// Uses the relationship: Q = ṁ / ρ - /// where Q is volumetric flow rate, ṁ is mass flow rate, and ρ is density. - /// - public VolumetricFlowRate CalculateVolumetricFlowRate(Density density) - { - Ensure.NotNull(density); - - T massFlow = In(Units.Kilogram); - T rho = density.In(Units.Kilogram); - - if (rho == T.Zero) - { - throw new ArgumentException("Density cannot be zero.", nameof(density)); - } - - T volumetricFlow = massFlow / rho; - return VolumetricFlowRate.Create(volumetricFlow); - } - - /// Calculates fluid density from mass flow rate and volumetric flow rate. - /// The volumetric flow rate. - /// The fluid density. - /// - /// Uses the relationship: ρ = ṁ / Q - /// where ρ is density, ṁ is mass flow rate, and Q is volumetric flow rate. - /// - public Density CalculateDensity(VolumetricFlowRate volumetricFlowRate) - { - Ensure.NotNull(volumetricFlowRate); - - T massFlow = In(Units.Kilogram); - T volumeFlow = volumetricFlowRate.In(Units.CubicMeter); - - if (volumeFlow == T.Zero) - { - throw new ArgumentException("Volumetric flow rate cannot be zero.", nameof(volumetricFlowRate)); - } - - T density = massFlow / volumeFlow; - return Density.Create(density); - } - - /// Calculates the mass transferred over a given time period. - /// The time period. - /// The total mass transferred. - /// - /// Uses the relationship: m = ṁ × t - /// where m is mass, ṁ is mass flow rate, and t is time. - /// - public Mass CalculateMassTransferred(Time time) - { - Ensure.NotNull(time); - - T massFlow = In(Units.Kilogram); - T t = time.In(Units.Second); - T mass = massFlow * t; - - return Mass.Create(mass); - } - - /// Calculates the time required to transfer a given mass. - /// The mass to transfer. - /// The time required. - /// - /// Uses the relationship: t = m / ṁ - /// where t is time, m is mass, and ṁ is mass flow rate. - /// - public Time CalculateTransferTime(Mass mass) - { - Ensure.NotNull(mass); - - T m = mass.In(Units.Kilogram); - T massFlow = In(Units.Kilogram); - - if (massFlow == T.Zero) - { - throw new InvalidOperationException("Mass flow rate cannot be zero for time calculation."); - } - - T time = m / massFlow; - return Time.Create(time); - } - - /// Calculates the momentum flow rate from mass flow rate and velocity. - /// The flow velocity. - /// The momentum flow rate (kg⋅m/s²). - /// - /// Uses the relationship: Momentum Flow = ṁ × v - /// where ṁ is mass flow rate and v is velocity. - /// This represents the rate of momentum transfer. - /// - public T CalculateMomentumFlowRate(Velocity velocity) - { - Ensure.NotNull(velocity); - - T massFlow = In(Units.Kilogram); - T v = velocity.In(Units.MetersPerSecond); - return massFlow * v; - } - - /// Determines if this mass flow rate is typical for industrial pipelines. - /// True if the mass flow rate is greater than 1 kg/s. - public bool IsTypicalIndustrialFlow() - { - T massFlow = In(Units.Kilogram); - T threshold = T.CreateChecked(1.0); // 1 kg/s - return massFlow > threshold; - } - - /// Determines if this mass flow rate is typical for laboratory scale. - /// True if the mass flow rate is less than 0.1 kg/s. - public bool IsTypicalLaboratoryScale() - { - T massFlow = In(Units.Kilogram); - T threshold = T.CreateChecked(0.1); // 0.1 kg/s - return massFlow < threshold; - } - - /// Determines if this mass flow rate is typical for household applications. - /// True if the mass flow rate is between 0.001 and 0.1 kg/s. - public bool IsTypicalHouseholdScale() - { - T massFlow = In(Units.Kilogram); - T lowerBound = T.CreateChecked(0.001); // 0.001 kg/s - T upperBound = T.CreateChecked(0.1); // 0.1 kg/s - return massFlow >= lowerBound && massFlow <= upperBound; - } -} diff --git a/Semantics.Quantities/FluidDynamics/ReynoldsNumber.cs b/Semantics.Quantities/FluidDynamics/ReynoldsNumber.cs deleted file mode 100644 index 3b7ec20..0000000 --- a/Semantics.Quantities/FluidDynamics/ReynoldsNumber.cs +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents Reynolds number with a specific unit of measurement. -/// Reynolds number is a dimensionless quantity that characterizes fluid flow regimes. -/// It indicates whether flow is laminar or turbulent. -/// -/// The numeric type for the Reynolds number value. -public sealed record ReynoldsNumber : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of Reynolds number [1] (dimensionless). - public override PhysicalDimension Dimension => PhysicalDimensions.ReynoldsNumber; - - /// Initializes a new instance of the class. - public ReynoldsNumber() : base() { } - - /// Creates a new Reynolds number from a dimensionless value. - /// The dimensionless Reynolds number value. - /// A new ReynoldsNumber instance. - public static ReynoldsNumber FromValue(T value) => Create(value); - - /// Creates a new Reynolds number from velocity, characteristic length, and kinematic viscosity. - /// The flow velocity. - /// The characteristic length. - /// The kinematic viscosity. - /// A new ReynoldsNumber instance. - /// - /// Uses the relationship: Re = (v × L) / ν - /// where Re is Reynolds number, v is velocity, L is characteristic length, and ν is kinematic viscosity. - /// - public static ReynoldsNumber FromVelocityLengthAndKinematicViscosity(Velocity velocity, Length characteristicLength, KinematicViscosity kinematicViscosity) - { - Ensure.NotNull(velocity); - Ensure.NotNull(characteristicLength); - Ensure.NotNull(kinematicViscosity); - - T v = velocity.In(Units.MetersPerSecond); - T length = characteristicLength.In(Units.Meter); - T nu = kinematicViscosity.In(Units.Meter); - - if (nu == T.Zero) - { - throw new ArgumentException("Kinematic viscosity cannot be zero.", nameof(kinematicViscosity)); - } - - T reynoldsNumber = v * length / nu; - return Create(reynoldsNumber); - } - - /// Creates a new Reynolds number from velocity, characteristic length, dynamic viscosity, and density. - /// The flow velocity. - /// The characteristic length. - /// The dynamic viscosity. - /// The fluid density. - /// A new ReynoldsNumber instance. - /// - /// Uses the relationship: Re = (ρ × v × L) / μ - /// where Re is Reynolds number, ρ is density, v is velocity, L is characteristic length, and μ is dynamic viscosity. - /// - public static ReynoldsNumber FromVelocityLengthDynamicViscosityAndDensity(Velocity velocity, Length characteristicLength, DynamicViscosity dynamicViscosity, Density density) - { - Ensure.NotNull(velocity); - Ensure.NotNull(characteristicLength); - Ensure.NotNull(dynamicViscosity); - Ensure.NotNull(density); - - T v = velocity.In(Units.MetersPerSecond); - T length = characteristicLength.In(Units.Meter); - T mu = dynamicViscosity.In(Units.Pascal); - T rho = density.In(Units.Kilogram); - - if (mu == T.Zero) - { - throw new ArgumentException("Dynamic viscosity cannot be zero.", nameof(dynamicViscosity)); - } - - T reynoldsNumber = rho * v * length / mu; - return Create(reynoldsNumber); - } - - /// Calculates the critical velocity for transition from laminar to turbulent flow. - /// The characteristic length. - /// The kinematic viscosity. - /// The critical Reynolds number (default: 2300 for pipe flow). - /// The critical velocity. - /// - /// Uses the relationship: v_critical = (Re_critical × ν) / L - /// where v_critical is critical velocity, Re_critical is critical Reynolds number, ν is kinematic viscosity, and L is characteristic length. - /// - public static Velocity CalculateCriticalVelocity(Length characteristicLength, KinematicViscosity kinematicViscosity, T? criticalReynoldsNumber = null) - { - Ensure.NotNull(characteristicLength); - Ensure.NotNull(kinematicViscosity); - - T reCritical = criticalReynoldsNumber ?? T.CreateChecked(2300); // Default for pipe flow - T length = characteristicLength.In(Units.Meter); - T nu = kinematicViscosity.In(Units.Meter); - - if (length == T.Zero) - { - throw new ArgumentException("Characteristic length cannot be zero.", nameof(characteristicLength)); - } - - T criticalVelocity = reCritical * nu / length; - return Velocity.Create(criticalVelocity); - } - - /// Determines if the flow is laminar. - /// The Reynolds number threshold for laminar flow (default: 2300). - /// True if the flow is laminar (Re < threshold). - public bool IsLaminarFlow(T? laminarThreshold = null) - { - T threshold = laminarThreshold ?? T.CreateChecked(2300); - T re = In(Units.Radian); - return re < threshold; - } - - /// Determines if the flow is turbulent. - /// The Reynolds number threshold for turbulent flow (default: 4000). - /// True if the flow is turbulent (Re > threshold). - public bool IsTurbulentFlow(T? turbulentThreshold = null) - { - T threshold = turbulentThreshold ?? T.CreateChecked(4000); - T re = In(Units.Radian); - return re > threshold; - } - - /// Determines if the flow is in the transition region. - /// The laminar threshold (default: 2300). - /// The turbulent threshold (default: 4000). - /// True if the flow is in the transition region. - public bool IsTransitionFlow(T? laminarThreshold = null, T? turbulentThreshold = null) - { - T lowerThreshold = laminarThreshold ?? T.CreateChecked(2300); - T upperThreshold = turbulentThreshold ?? T.CreateChecked(4000); - T re = In(Units.Radian); - return re >= lowerThreshold && re <= upperThreshold; - } - - /// Gets the flow regime classification. - /// The laminar threshold (default: 2300). - /// The turbulent threshold (default: 4000). - /// A string describing the flow regime. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S3358:Ternary operators should not be nested", Justification = "")] - public string GetFlowRegime(T? laminarThreshold = null, T? turbulentThreshold = null) => - IsLaminarFlow(laminarThreshold) - ? "Laminar" - : IsTurbulentFlow(turbulentThreshold) - ? "Turbulent" - : "Transitional"; - - /// Calculates the friction factor for pipe flow using appropriate correlations. - /// The relative roughness (ε/D) for the pipe (default: 0 for smooth pipes). - /// The Darcy friction factor. - /// - /// Uses different correlations based on flow regime: - /// - Laminar: f = 64/Re - /// - Turbulent smooth: Blasius equation f = 0.316/Re^0.25 (for Re < 100,000) - /// - Turbulent rough: Colebrook-White equation approximation - /// - public T CalculateFrictionFactor(T? relativeRoughness = null) - { - T re = In(Units.Radian); - T roughness = relativeRoughness ?? T.Zero; - - if (IsLaminarFlow()) - { - // Laminar flow: f = 64/Re - return T.CreateChecked(64) / re; - } - else if (IsTurbulentFlow()) - { - if (roughness == T.Zero) - { - // Smooth pipe turbulent flow: Blasius equation - if (re < T.CreateChecked(100000)) - { - return T.CreateChecked(0.316) / T.CreateChecked(Math.Pow(double.CreateChecked(re), 0.25)); - } - else - { - // Prandtl equation for smooth pipes - T logRe = T.CreateChecked(Math.Log10(double.CreateChecked(re))); - return T.One / T.CreateChecked(Math.Pow((2.0 * double.CreateChecked(logRe)) - 0.8, 2)); - } - } - else - { - // Rough pipe: simplified Colebrook-White approximation - T term1 = roughness / T.CreateChecked(3.7); - T term2 = T.CreateChecked(2.51) / re; - T logTerm = T.CreateChecked(Math.Log10(double.CreateChecked(term1 + term2))); - return T.CreateChecked(0.25) / (logTerm * logTerm); - } - } - else - { - // Transition region: linear interpolation between laminar and turbulent - T laminarF = T.CreateChecked(64) / re; - T turbulentF = T.CreateChecked(0.316) / T.CreateChecked(Math.Pow(double.CreateChecked(re), 0.25)); - T factor = (re - T.CreateChecked(2300)) / T.CreateChecked(1700); // interpolation factor - return laminarF + (factor * (turbulentF - laminarF)); - } - } - - /// Determines if this Reynolds number is typical for flow around a sphere. - /// True if the value is in the typical range for sphere flow (0.1 to 200,000). - public bool IsTypicalSphereFlow() - { - T re = In(Units.Radian); - T lowerBound = T.CreateChecked(0.1); - T upperBound = T.CreateChecked(200000); - return re >= lowerBound && re <= upperBound; - } - - /// Determines if this Reynolds number is typical for pipe flow. - /// True if the value is in the typical range for pipe flow (1 to 1,000,000). - public bool IsTypicalPipeFlow() - { - T re = In(Units.Radian); - T lowerBound = T.CreateChecked(1); - T upperBound = T.CreateChecked(1000000); - return re >= lowerBound && re <= upperBound; - } - - /// - /// Calculates Reynolds number from density, velocity, length, and dynamic viscosity (Re = ρvL/μ). - /// - /// The fluid density. - /// The characteristic velocity. - /// The characteristic length. - /// The dynamic viscosity. - /// The resulting Reynolds number. - public static ReynoldsNumber FromFluidProperties(Density density, Velocity velocity, Length length, DynamicViscosity dynamicViscosity) - { - Ensure.NotNull(density); - Ensure.NotNull(velocity); - Ensure.NotNull(length); - Ensure.NotNull(dynamicViscosity); - - T reynoldsValue = density.Value * velocity.Value * length.Value / dynamicViscosity.Value; - - return Create(reynoldsValue); - } - - /// - /// Calculates Reynolds number from velocity, length, and kinematic viscosity (Re = vL/ν). - /// - /// The characteristic velocity. - /// The characteristic length. - /// The kinematic viscosity. - /// The resulting Reynolds number. - public static ReynoldsNumber FromKinematicProperties(Velocity velocity, Length length, KinematicViscosity kinematicViscosity) - { - Ensure.NotNull(velocity); - Ensure.NotNull(length); - Ensure.NotNull(kinematicViscosity); - - T reynoldsValue = velocity.Value * length.Value / kinematicViscosity.Value; - - return Create(reynoldsValue); - } - - /// - /// Calculates Reynolds number for air flow at standard conditions. - /// - /// The air velocity. - /// The characteristic length. - /// The resulting Reynolds number for standard air. - public static ReynoldsNumber ForStandardAir(Velocity velocity, Length length) - { - Ensure.NotNull(velocity); - Ensure.NotNull(length); - - // Standard air properties at 20°C, 1 atm - T airDensity = PhysicalConstants.Generic.StandardAirDensity(); - T airViscosity = T.CreateChecked(1.825e-5); // Pa·s for air at 20°C - - T reynoldsValue = airDensity * velocity.Value * length.Value / airViscosity; - - return Create(reynoldsValue); - } - - /// - /// Determines the flow regime based on Reynolds number for pipe flow. - /// - /// A description of the flow regime. - public string GetPipeFlowRegime() - { - double re = double.CreateChecked(Value); - return re switch - { - < 2300 => "Laminar", - < 4000 => "Transitional", - _ => "Turbulent" - }; - } -} diff --git a/Semantics.Quantities/FluidDynamics/VolumetricFlowRate.cs b/Semantics.Quantities/FluidDynamics/VolumetricFlowRate.cs deleted file mode 100644 index fe1a94d..0000000 --- a/Semantics.Quantities/FluidDynamics/VolumetricFlowRate.cs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents volumetric flow rate with a specific unit of measurement. -/// Volumetric flow rate is the volume of fluid that passes per unit time. -/// It is measured in cubic meters per second (m³/s) in the SI system. -/// -/// The numeric type for the volumetric flow rate value. -public sealed record VolumetricFlowRate : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of volumetric flow rate [L³ T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.VolumetricFlowRate; - - /// Initializes a new instance of the class. - public VolumetricFlowRate() : base() { } - - /// Creates a new volumetric flow rate from a value in cubic meters per second. - /// The volumetric flow rate in m³/s. - /// A new VolumetricFlowRate instance. - public static VolumetricFlowRate FromCubicMetersPerSecond(T cubicMetersPerSecond) => Create(cubicMetersPerSecond); - - /// Creates a new volumetric flow rate from a value in liters per second. - /// The volumetric flow rate in L/s. - /// A new VolumetricFlowRate instance. - public static VolumetricFlowRate FromLitersPerSecond(T litersPerSecond) - { - T cubicMetersPerSecond = litersPerSecond * T.CreateChecked(0.001); - return Create(cubicMetersPerSecond); - } - - /// Creates a new volumetric flow rate from a value in liters per minute. - /// The volumetric flow rate in L/min. - /// A new VolumetricFlowRate instance. - public static VolumetricFlowRate FromLitersPerMinute(T litersPerMinute) - { - T cubicMetersPerSecond = litersPerMinute * T.CreateChecked(0.001 / 60); - return Create(cubicMetersPerSecond); - } - - /// Creates a new volumetric flow rate from a value in gallons per minute. - /// The volumetric flow rate in GPM (US gallons). - /// A new VolumetricFlowRate instance. - public static VolumetricFlowRate FromGallonsPerMinute(T gallonsPerMinute) - { - T cubicMetersPerSecond = gallonsPerMinute * T.CreateChecked(3.78541e-3 / 60); - return Create(cubicMetersPerSecond); - } - - /// Creates a new volumetric flow rate from velocity and cross-sectional area. - /// The average flow velocity. - /// The cross-sectional area. - /// A new VolumetricFlowRate instance. - /// - /// Uses the relationship: Q = v × A - /// where Q is volumetric flow rate, v is velocity, and A is area. - /// - public static VolumetricFlowRate FromVelocityAndArea(Velocity velocity, Area area) - { - Ensure.NotNull(velocity); - Ensure.NotNull(area); - - T v = velocity.In(Units.MetersPerSecond); - T a = area.In(Units.SquareMeter); - T flowRate = v * a; - return Create(flowRate); - } - - /// Calculates the average velocity from flow rate and cross-sectional area. - /// The cross-sectional area. - /// The average flow velocity. - /// - /// Uses the relationship: v = Q / A - /// where v is velocity, Q is volumetric flow rate, and A is area. - /// - public Velocity CalculateVelocity(Area area) - { - Ensure.NotNull(area); - - T q = In(Units.CubicMeter); - T a = area.In(Units.SquareMeter); - - if (a == T.Zero) - { - throw new ArgumentException("Area cannot be zero.", nameof(area)); - } - - T velocity = q / a; - return Velocity.Create(velocity); - } - - /// Calculates the cross-sectional area from flow rate and velocity. - /// The average flow velocity. - /// The cross-sectional area. - /// - /// Uses the relationship: A = Q / v - /// where A is area, Q is volumetric flow rate, and v is velocity. - /// - public Area CalculateArea(Velocity velocity) - { - Ensure.NotNull(velocity); - - T q = In(Units.CubicMeter); - T v = velocity.In(Units.MetersPerSecond); - - if (v == T.Zero) - { - throw new ArgumentException("Velocity cannot be zero.", nameof(velocity)); - } - - T area = q / v; - return Area.Create(area); - } - - /// Calculates mass flow rate from volumetric flow rate and density. - /// The fluid density. - /// The mass flow rate. - /// - /// Uses the relationship: ṁ = ρ × Q - /// where ṁ is mass flow rate, ρ is density, and Q is volumetric flow rate. - /// - public MassFlowRate CalculateMassFlowRate(Density density) - { - Ensure.NotNull(density); - - T q = In(Units.CubicMeter); - T rho = density.In(Units.Kilogram); - T massFlowRate = rho * q; - - return MassFlowRate.Create(massFlowRate); - } - - /// Calculates the time to fill a given volume. - /// The volume to fill. - /// The time required to fill the volume. - /// - /// Uses the relationship: t = V / Q - /// where t is time, V is volume, and Q is volumetric flow rate. - /// - public Time CalculateFillTime(Volume volume) - { - Ensure.NotNull(volume); - - T v = volume.In(Units.CubicMeter); - T q = In(Units.CubicMeter); - - if (q == T.Zero) - { - throw new InvalidOperationException("Flow rate cannot be zero for time calculation."); - } - - T time = v / q; - return Time.Create(time); - } - - /// Calculates the volume delivered over a given time period. - /// The time period. - /// The volume delivered. - /// - /// Uses the relationship: V = Q × t - /// where V is volume, Q is volumetric flow rate, and t is time. - /// - public Volume CalculateVolumeDelivered(Time time) - { - Ensure.NotNull(time); - - T q = In(Units.CubicMeter); - T t = time.In(Units.Second); - T volume = q * t; - - return Volume.Create(volume); - } - - /// Determines if this flow rate is typical for household plumbing. - /// True if the flow rate is between 0.1 and 0.5 L/s (typical for taps and showers). - public bool IsTypicalHouseholdFlow() - { - T flowRate = In(Units.CubicMeter); - T lowerBound = T.CreateChecked(0.0001); // 0.1 L/s in m³/s - T upperBound = T.CreateChecked(0.0005); // 0.5 L/s in m³/s - return flowRate >= lowerBound && flowRate <= upperBound; - } - - /// Determines if this flow rate is typical for industrial processes. - /// True if the flow rate is greater than 1 L/s. - public bool IsTypicalIndustrialFlow() - { - T flowRate = In(Units.CubicMeter); - T threshold = T.CreateChecked(0.001); // 1 L/s in m³/s - return flowRate > threshold; - } -} diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.ConversionsGenerator/ConversionConstants.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.ConversionsGenerator/ConversionConstants.g.cs new file mode 100644 index 0000000..b3c0882 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.ConversionsGenerator/ConversionConstants.g.cs @@ -0,0 +1,278 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities.Units; + +/// +/// Conversion constants used by generated unit definitions. +/// Values sourced from conversions.json metadata. +/// +internal static class ConversionConstants{ + /// Foot to meter conversion: 0.3048 m/ft (exact by definition) + internal const double FeetToMeters = 0.3048; + + /// Inch to meter conversion: 0.0254 m/in (exact by definition) + internal const double InchesToMeters = 0.0254; + + /// Yard to meter conversion: 0.9144 m/yd (exact by definition) + internal const double YardToMeters = 0.9144; + + /// Mile to meter conversion: 1609.344 m/mi (exact by definition) + internal const double MileToMeters = 1609.344; + + /// Angstrom to meter conversion: 1e-10 m/Å (exact by definition) + internal const double AngstromToMeters = 1e-10; + + /// Nautical mile to meter conversion: 1852 m/nmi (exact by definition) + internal const double NauticalMileToMeters = 1852; + + /// Pound mass to kilogram: 0.453592 kg/lb (exact) + internal const double PoundMassToKilogram = 0.453592; + + /// Pound to kilogram conversion: 0.45359237 kg/lb (exact by definition) + internal const double PoundToKilograms = 0.45359237; + + /// Ounce to kilogram conversion: 0.028349523125 kg/oz (exact) + internal const double OunceToKilograms = 0.028349523125; + + /// Metric ton to kilogram conversion: 1000 kg/t (exact by definition) + internal const double TonToKilograms = 1000; + + /// Stone to kilogram conversion: 6.35029318 kg/st (14 lb, exact) + internal const double StoneToKilograms = 6.35029318; + + /// Short ton to kilogram conversion: 907.18474 kg/ton (2000 lb, exact) + internal const double ShortTonToKilograms = 907.18474; + + /// Atomic mass unit to kilogram: 1.66053906660e-27 kg/u (2018 CODATA) + internal const double AtomicMassUnitToKilograms = 1.66053906660e-27; + + /// Liter to cubic meter conversion: 0.001 m³/L (exact by definition) + internal const double LiterToCubicMeters = 0.001; + + /// US gallon to cubic meter conversion: 0.003785411784 m³/gal (exact) + internal const double GallonToCubicMeters = 0.003785411784; + + /// Cubic centimeter to cubic meter: 1e-6 m³/cm³ (exact by definition) + internal const double CubicCentimeterToCubicMeters = 1e-6; + + /// Cubic foot to cubic meter: 0.028316846592 m³/ft³ (exact) + internal const double CubicFootToCubicMeters = 0.028316846592; + + /// Cubic inch to cubic meter: 1.6387064e-5 m³/in³ (exact) + internal const double CubicInchToCubicMeters = 1.6387064e-5; + + /// Imperial gallon to cubic meter: 0.00454609 m³/imp gal (exact by definition) + internal const double ImperialGallonToCubicMeters = 0.00454609; + + /// US liquid quart to cubic meter: 0.000946352946 m³/qt (exact) + internal const double USQuartToCubicMeters = 0.000946352946; + + /// US liquid pint to cubic meter: 0.000473176473 m³/pt (exact) + internal const double USPintToCubicMeters = 0.000473176473; + + /// US fluid ounce to cubic meter: 2.95735295625e-5 m³/fl oz (exact) + internal const double USFluidOunceToCubicMeters = 2.95735295625e-5; + + /// Minute to second conversion: 60 s/min (exact) + internal const double MinuteToSeconds = 60; + + /// Hour to second conversion: 3600 s/h (exact) + internal const double HourToSeconds = 3600; + + /// Day to second conversion: 86400 s/day (exact) + internal const double DayToSeconds = 86400; + + /// Year to second conversion: 31557600 s/year (365.25 days, exact) + internal const double YearToSeconds = 31557600; + + /// Week to second conversion: 604800 s/wk (exact) + internal const double WeekToSeconds = 604800; + + /// Celsius to Kelvin temperature offset: 273.15 K (exact by definition) + internal const double CelsiusToKelvinOffset = 273.15; + + /// Fahrenheit-to-Kelvin degree scale factor: 5/9 (exact) + internal const double FahrenheitScale = 0.5555555555555556; + + /// Fahrenheit to Kelvin affine offset: 459.67 × 5/9 ≈ 255.372 K (exact) + internal const double FahrenheitToKelvinOffset = 255.37222222222223; + + /// Degree to radian conversion: π/180 rad/° (exact) + internal const double DegreeToRadians = 0.017453292519943295769236907684886127134428718885417254560971914401710091146034494436822415696345097379101040706699150667990539631694451077627806983; + + /// Gradian to radian conversion: π/200 rad/grad (exact) + internal const double GradianToRadians = 0.015707963267948966; + + /// Revolution to radian conversion: 2π rad/rev (exact) + internal const double RevolutionToRadians = 6.283185307179586; + + /// Calorie to joule conversion: 4.184 J/cal (exact, thermochemical calorie) + internal const double CalorieToJoules = 4.184; + + /// Kilowatt-hour to joule conversion: 3600000 J/kWh (exact) + internal const double KilowattHourToJoules = 3600000; + + /// Mechanical horsepower to watt conversion: 745.6998715822702 W/hp (exact) + internal const double HorsepowerToWatts = 745.6998715822702; + + /// Electron volt to joule conversion: 1.602176634e-19 J/eV (exact, based on elementary charge) + internal const double ElectronVoltToJoules = 1.602176634e-19; + + /// Kilocalorie to joule conversion: 4184 J/kcal (exact, thermochemical) + internal const double KilocalorieToJoules = 4184; + + /// Watt-hour to joule conversion: 3600 J/Wh (exact) + internal const double WattHourToJoules = 3600; + + /// Erg to joule conversion: 1e-7 J/erg (exact by definition) + internal const double ErgToJoules = 1e-7; + + /// British thermal unit (IT) to joule conversion: 1055.05585262 J/BTU (exact) + internal const double BtuToJoules = 1055.05585262; + + /// Bar to pascal conversion: 100000 Pa/bar (exact by definition) + internal const double BarToPascals = 100000; + + /// Atmosphere to pascal conversion: 101325 Pa/atm (exact by definition) + internal const double AtmosphereToPascals = 101325; + + /// PSI to pascal conversion: 6894.757293168361 Pa/psi (exact) + internal const double PsiToPascals = 6894.757293168361; + + /// Torr to pascal conversion: 101325/760 ≈ 133.322 Pa/Torr (exact) + internal const double TorrToPascals = 133.32236842105263; + + /// Square foot to square meter conversion: 0.09290304 m²/ft² (exact) + internal const double SquareFootToSquareMeters = 0.09290304; + + /// Square inch to square meter conversion: 0.00064516 m²/in² (exact) + internal const double SquareInchToSquareMeters = 0.00064516; + + /// Barn to square meter conversion: 1e-28 m² (exact by definition) + internal const double BarnToSquareMeters = 1e-28; + + /// Square kilometer to square meter: 1e6 m²/km² (exact by definition) + internal const double SquareKilometerToSquareMeters = 1e6; + + /// Square centimeter to square meter: 1e-4 m²/cm² (exact by definition) + internal const double SquareCentimeterToSquareMeters = 1e-4; + + /// Square mile to square meter: 2589988.110336 m²/mi² (exact) + internal const double SquareMileToSquareMeters = 2589988.110336; + + /// Hectare to square meter: 10000 m²/ha (exact by definition) + internal const double HectareToSquareMeters = 10000; + + /// Acre to square meter: 4046.8564224 m²/ac (exact) + internal const double AcreToSquareMeters = 4046.8564224; + + /// Kilometers per hour to meters per second conversion: 0.2777777777777778 m/s per km/h (exact) + internal const double KilometerPerHourToMeterPerSecond = 0.2777777777777778; + + /// Miles per hour to meters per second conversion: 0.44704 m/s per mph (exact) + internal const double MilePerHourToMeterPerSecond = 0.44704; + + /// Feet per second to meters per second: 0.3048 m/s per ft/s (exact) + internal const double FootPerSecondToMeterPerSecond = 0.3048; + + /// Knot to meters per second: 1852/3600 ≈ 0.514444 m/s per kn (exact) + internal const double KnotToMeterPerSecond = 0.5144444444444445; + + /// RPM to rad/s conversion: π/30 rad/s per rpm (exact) + internal const double RevolutionPerMinuteToRadianPerSecond = 0.10471975511965977; + + /// Pound-foot to Newton-meter conversion: 1.3558179483314004 N⋅m per lb⋅ft (exact) + internal const double PoundFootToNewtonMeters = 1.3558179483314004; + + /// Molar to cubic meter concentration conversion: 1000.0 mol/m³ per mol/L (exact) + internal const double MolarToCubicMeter = 1000.0; + + /// Millimolar to mole per cubic meter: 1 mol/m³ per mM (exact) + internal const double MillimolarToMolePerCubicMeter = 1.0; + + /// Micromolar to mole per cubic meter: 0.001 mol/m³ per μM (exact) + internal const double MicromolarToMolePerCubicMeter = 0.001; + + /// Stokes to square meter per second: 1e-4 m²/s per St (exact by definition) + internal const double StokesToSquareMeterPerSecond = 1e-4; + + /// Poise to pascal second: 0.1 Pa·s per P (exact by definition) + internal const double PoiseToPascalSecond = 0.1; + + /// Liter per second to cubic meter per second: 0.001 m³/s per L/s (exact by definition) + internal const double LiterPerSecondToCubicMeterPerSecond = 0.001; + + /// Centipoise to pascal second: 0.001 Pa·s per cP (exact by definition) + internal const double CentipoiseToPascalSecond = 0.001; + + /// Dyne per centimeter to newton per meter: 0.001 N/m per dyn/cm (exact) + internal const double DynePerCentimeterToNewtonPerMeter = 0.001; + + /// Gram per cubic centimeter to kilogram per cubic meter: 1000 kg/m³ per g/cm³ (exact) + internal const double GramPerCubicCentimeterToKilogramPerCubicMeter = 1000; + + /// Gram per liter to kilogram per cubic meter: 1 kg/m³ per g/L (exact) + internal const double GramPerLiterToKilogramPerCubicMeter = 1.0; + + /// Gauss to Tesla: 1e-4 T per G (exact by definition) + internal const double GaussToTesla = 1e-4; + + /// Ampere-hour to coulomb conversion: 3600 C/Ah (exact) + internal const double AmpereHourToCoulombs = 3600; + + /// Gram per mole to kilogram per mole: 0.001 kg/mol per g/mol (exact by definition) + internal const double GramPerMoleToKilogramPerMole = 0.001; + + /// Kilojoule per mole to joule per mole: 1000 J/mol per kJ/mol (exact by definition) + internal const double KilojoulePerMoleToJoulePerMole = 1000; + + /// Calorie per mole to joule per mole: 4.184 J/mol per cal/mol (exact, thermochemical) + internal const double CaloriePerMoleToJoulePerMole = 4.184; + + /// Enzyme unit (1 μmol/min) to katal: 1/6e7 ≈ 1.6667e-8 kat/U (exact) + internal const double EnzymeUnitToKatals = 1.6666666666666667e-8; + + /// Standard gravity to meters per second squared: 9.80665 m/s² per g (exact by definition) + internal const double StandardGravityToMeterPerSecondSquared = 9.80665; + + /// Dyne to newton conversion: 1e-5 N/dyn (exact by definition) + internal const double DyneToNewtons = 1e-5; + + /// Pound-force to newton conversion: 4.4482216152605 N/lbf (exact) + internal const double PoundForceToNewtons = 4.4482216152605; + + /// Curie to becquerel conversion: 3.7e10 Bq/Ci (exact by definition) + internal const double CurieToBecquerels = 3.7e10; + + /// Rad to gray conversion: 0.01 Gy/rad (exact by definition) + internal const double RadToGrays = 0.01; + + /// Rem to sievert conversion: 0.01 Sv/rem (exact by definition) + internal const double RemToSieverts = 0.01; + + /// Roentgen to coulomb per kilogram: 2.58e-4 C/kg per R (exact by definition) + internal const double RoentgenToCoulombsPerKilogram = 2.58e-4; + + /// Foot-candle to lux conversion: 1/0.09290304 ≈ 10.7639 lx/fc (exact) + internal const double FootCandleToLux = 10.763910416709722; + + /// Foot-lambert to candela per square meter: 1/(π·0.09290304) ≈ 3.4263 cd/m² per fL (exact) + internal const double FootLambertToCandelaPerSquareMeter = 3.4262590996353905; + + /// Percent to ratio: 0.01 (exact by definition) + internal const double PercentToRatio = 0.01; + + /// Parts per million to ratio: 1e-6 (exact by definition) + internal const double PartPerMillionToRatio = 1e-6; + + /// Parts per billion to ratio: 1e-9 (exact by definition) + internal const double PartPerBillionToRatio = 1e-9; + + /// Percent by weight to mass-fraction ratio: 0.01 (exact by definition) + internal const double PercentByWeightToRatio = 0.01; + +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.DimensionsGenerator/PhysicalDimensions.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.DimensionsGenerator/PhysicalDimensions.g.cs new file mode 100644 index 0000000..9a44180 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.DimensionsGenerator/PhysicalDimensions.g.cs @@ -0,0 +1,743 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Collections.Generic; + +/// +/// Dimension information record. +/// +public record DimensionInfo(string Name, string Symbol, Dictionary DimensionalFormula, List Quantities){ } + +/// +/// Static registry of physical dimensions. +/// +public static class PhysicalDimensions{ + /// Physical dimension: AbsorbedDose + public static readonly DimensionInfo AbsorbedDose = new("AbsorbedDose", "L² T⁻²", new Dictionary { ["length"] = 2, ["time"] = -2 }, new List { "AbsorbedDose" }); + + /// Physical dimension: Acceleration + public static readonly DimensionInfo Acceleration = new("Acceleration", "L T⁻²", new Dictionary { ["length"] = 1, ["time"] = -2 }, new List { "AccelerationMagnitude", "GravitationalAcceleration", "Acceleration1D", "Acceleration2D", "Acceleration3D", "GravitationalField3D", "Acceleration4D" }); + + /// Physical dimension: AcousticImpedance + public static readonly DimensionInfo AcousticImpedance = new("AcousticImpedance", "M L⁻² T⁻¹", new Dictionary { ["mass"] = 1, ["length"] = -2, ["time"] = -1 }, new List { "AcousticImpedance" }); + + /// Physical dimension: AmountOfSubstance + public static readonly DimensionInfo AmountOfSubstance = new("AmountOfSubstance", "N", new Dictionary { ["amountOfSubstance"] = 1 }, new List { "AmountOfSubstance" }); + + /// Physical dimension: AngularAcceleration + public static readonly DimensionInfo AngularAcceleration = new("AngularAcceleration", "T⁻²", new Dictionary { ["time"] = -2 }, new List { "AngularAccelerationMagnitude", "AngularAcceleration1D", "AngularAcceleration3D" }); + + /// Physical dimension: AngularDisplacement + public static readonly DimensionInfo AngularDisplacement = new("AngularDisplacement", "1", new Dictionary(), new List { "Angle", "FieldOfView", "ApertureAngle", "SignedAngle", "Rotation", "Phase", "Bearing", "Heading", "AngularDisplacement3D" }); + + /// Physical dimension: AngularJerk + public static readonly DimensionInfo AngularJerk = new("AngularJerk", "T⁻³", new Dictionary { ["time"] = -3 }, new List { "AngularJerkMagnitude", "AngularJerk1D", "AngularJerk3D" }); + + /// Physical dimension: AngularMomentum + public static readonly DimensionInfo AngularMomentum = new("AngularMomentum", "M L² T⁻¹", new Dictionary { ["mass"] = 1, ["length"] = 2, ["time"] = -1 }, new List { "AngularMomentumMagnitude", "AngularMomentum1D", "AngularMomentum3D" }); + + /// Physical dimension: AngularVelocity + public static readonly DimensionInfo AngularVelocity = new("AngularVelocity", "T⁻¹", new Dictionary { ["time"] = -1 }, new List { "AngularSpeed", "AngularVelocity1D", "AngularVelocity3D" }); + + /// Physical dimension: Area + public static readonly DimensionInfo Area = new("Area", "L²", new Dictionary { ["length"] = 2 }, new List { "Area", "SurfaceArea", "CrossSectionalArea" }); + + /// Physical dimension: CatalyticActivity + public static readonly DimensionInfo CatalyticActivity = new("CatalyticActivity", "N T⁻¹", new Dictionary { ["amountOfSubstance"] = 1, ["time"] = -1 }, new List { "CatalyticActivity", "EnzymeActivity" }); + + /// Physical dimension: Concentration + public static readonly DimensionInfo Concentration = new("Concentration", "N L⁻³", new Dictionary { ["amountOfSubstance"] = 1, ["length"] = -3 }, new List { "Concentration" }); + + /// Physical dimension: Density + public static readonly DimensionInfo Density = new("Density", "M L⁻³", new Dictionary { ["mass"] = 1, ["length"] = -3 }, new List { "Density" }); + + /// Physical dimension: Dimensionless + public static readonly DimensionInfo Dimensionless = new("Dimensionless", "1", new Dictionary(), new List { "Ratio", "RefractiveIndex", "ReynoldsNumber", "SpecificGravity", "MachNumber", "SoundAbsorption", "NoiseReductionCoefficient", "SoundTransmissionClass", "Gain", "SignedRatio", "ReflectionCoefficient" }); + + /// Physical dimension: DynamicViscosity + public static readonly DimensionInfo DynamicViscosity = new("DynamicViscosity", "M L⁻¹ T⁻¹", new Dictionary { ["mass"] = 1, ["length"] = -1, ["time"] = -1 }, new List { "DynamicViscosity" }); + + /// Physical dimension: ElectricCapacitance + public static readonly DimensionInfo ElectricCapacitance = new("ElectricCapacitance", "M⁻¹ L⁻² T⁴ I²", new Dictionary { ["mass"] = -1, ["length"] = -2, ["time"] = 4, ["electricCurrent"] = 2 }, new List { "Capacitance" }); + + /// Physical dimension: ElectricCharge + public static readonly DimensionInfo ElectricCharge = new("ElectricCharge", "I T", new Dictionary { ["electricCurrent"] = 1, ["time"] = 1 }, new List { "ChargeMagnitude", "Charge" }); + + /// Physical dimension: ElectricConductance + public static readonly DimensionInfo ElectricConductance = new("ElectricConductance", "M⁻¹ L⁻² T³ I²", new Dictionary { ["mass"] = -1, ["length"] = -2, ["time"] = 3, ["electricCurrent"] = 2 }, new List { "Conductance", "Admittance" }); + + /// Physical dimension: ElectricConductivity + public static readonly DimensionInfo ElectricConductivity = new("ElectricConductivity", "M⁻¹ L⁻³ T³ I²", new Dictionary { ["mass"] = -1, ["length"] = -3, ["time"] = 3, ["electricCurrent"] = 2 }, new List { "ElectricConductivity" }); + + /// Physical dimension: ElectricCurrent + public static readonly DimensionInfo ElectricCurrent = new("ElectricCurrent", "I", new Dictionary { ["electricCurrent"] = 1 }, new List { "CurrentMagnitude", "Current1D", "Current3D" }); + + /// Physical dimension: ElectricField + public static readonly DimensionInfo ElectricField = new("ElectricField", "M L T⁻³ I⁻¹", new Dictionary { ["mass"] = 1, ["length"] = 1, ["time"] = -3, ["electricCurrent"] = -1 }, new List { "ElectricFieldMagnitude", "ElectricField1D", "ElectricField2D", "ElectricField3D" }); + + /// Physical dimension: ElectricFlux + public static readonly DimensionInfo ElectricFlux = new("ElectricFlux", "M L³ T⁻³ I⁻¹", new Dictionary { ["mass"] = 1, ["length"] = 3, ["time"] = -3, ["electricCurrent"] = -1 }, new List { "ElectricFlux" }); + + /// Physical dimension: ElectricPotential + public static readonly DimensionInfo ElectricPotential = new("ElectricPotential", "M L² T⁻³ I⁻¹", new Dictionary { ["mass"] = 1, ["length"] = 2, ["time"] = -3, ["electricCurrent"] = -1 }, new List { "VoltageMagnitude", "EMF", "VoltageDrop", "Voltage" }); + + /// Physical dimension: ElectricPowerDensity + public static readonly DimensionInfo ElectricPowerDensity = new("ElectricPowerDensity", "M L⁻¹ T⁻³", new Dictionary { ["mass"] = 1, ["length"] = -1, ["time"] = -3 }, new List { "ElectricPowerDensity" }); + + /// Physical dimension: ElectricResistance + public static readonly DimensionInfo ElectricResistance = new("ElectricResistance", "M L² T⁻³ I⁻²", new Dictionary { ["mass"] = 1, ["length"] = 2, ["time"] = -3, ["electricCurrent"] = -2 }, new List { "Resistance", "Impedance" }); + + /// Physical dimension: Energy + public static readonly DimensionInfo Energy = new("Energy", "M L² T⁻²", new Dictionary { ["mass"] = 1, ["length"] = 2, ["time"] = -2 }, new List { "Energy", "Work", "Heat", "KineticEnergy", "PotentialEnergy", "ThermalEnergy" }); + + /// Physical dimension: Entropy + public static readonly DimensionInfo Entropy = new("Entropy", "M L² T⁻² Θ⁻¹", new Dictionary { ["mass"] = 1, ["length"] = 2, ["time"] = -2, ["temperature"] = -1 }, new List { "Entropy", "HeatCapacity" }); + + /// Physical dimension: EquivalentDose + public static readonly DimensionInfo EquivalentDose = new("EquivalentDose", "L² T⁻²", new Dictionary { ["length"] = 2, ["time"] = -2 }, new List { "EquivalentDose" }); + + /// Physical dimension: Exposure + public static readonly DimensionInfo Exposure = new("Exposure", "M⁻¹ T I", new Dictionary { ["mass"] = -1, ["time"] = 1, ["electricCurrent"] = 1 }, new List { "Exposure" }); + + /// Physical dimension: Force + public static readonly DimensionInfo Force = new("Force", "M L T⁻²", new Dictionary { ["mass"] = 1, ["length"] = 1, ["time"] = -2 }, new List { "ForceMagnitude", "Weight", "Thrust", "Drag", "Lift", "Tension", "NormalForce", "Friction", "Force1D", "Force2D", "Force3D", "WeightVector", "ThrustVector", "Force4D" }); + + /// Physical dimension: Frequency + public static readonly DimensionInfo Frequency = new("Frequency", "T⁻¹", new Dictionary { ["time"] = -1 }, new List { "Frequency", "SamplingRate", "ClockSpeed", "Bandwidth", "Pitch" }); + + /// Physical dimension: HeatTransferCoefficient + public static readonly DimensionInfo HeatTransferCoefficient = new("HeatTransferCoefficient", "M T⁻³ Θ⁻¹", new Dictionary { ["mass"] = 1, ["time"] = -3, ["temperature"] = -1 }, new List { "HeatTransferCoefficient" }); + + /// Physical dimension: Illuminance + public static readonly DimensionInfo Illuminance = new("Illuminance", "J L⁻²", new Dictionary { ["luminousIntensity"] = 1, ["length"] = -2 }, new List { "Illuminance" }); + + /// Physical dimension: Inductance + public static readonly DimensionInfo Inductance = new("Inductance", "M L² T⁻² I⁻²", new Dictionary { ["mass"] = 1, ["length"] = 2, ["time"] = -2, ["electricCurrent"] = -2 }, new List { "Inductance" }); + + /// Physical dimension: Irradiance + public static readonly DimensionInfo Irradiance = new("Irradiance", "M T⁻³", new Dictionary { ["mass"] = 1, ["time"] = -3 }, new List { "Irradiance", "SoundIntensity", "HeatFlux", "EnergyFluxDensity" }); + + /// Physical dimension: Jerk + public static readonly DimensionInfo Jerk = new("Jerk", "L T⁻³", new Dictionary { ["length"] = 1, ["time"] = -3 }, new List { "JerkMagnitude", "Jerk1D", "Jerk2D", "Jerk3D", "Jerk4D" }); + + /// Physical dimension: KinematicViscosity + public static readonly DimensionInfo KinematicViscosity = new("KinematicViscosity", "L² T⁻¹", new Dictionary { ["length"] = 2, ["time"] = -1 }, new List { "KinematicViscosity", "ThermalDiffusivity" }); + + /// Physical dimension: Length + public static readonly DimensionInfo Length = new("Length", "L", new Dictionary { ["length"] = 1 }, new List { "Length", "Width", "Height", "Depth", "Radius", "Diameter", "Distance", "Altitude", "Wavelength", "Thickness", "Perimeter", "Displacement1D", "Offset", "Displacement2D", "Displacement3D", "Position3D", "Translation3D", "Displacement4D" }); + + /// Physical dimension: Loudness + public static readonly DimensionInfo Loudness = new("Loudness", "1", new Dictionary(), new List { "Loudness" }); + + /// Physical dimension: Luminance + public static readonly DimensionInfo Luminance = new("Luminance", "J L⁻²", new Dictionary { ["luminousIntensity"] = 1, ["length"] = -2 }, new List { "Luminance" }); + + /// Physical dimension: LuminousFlux + public static readonly DimensionInfo LuminousFlux = new("LuminousFlux", "J", new Dictionary { ["luminousIntensity"] = 1 }, new List { "LuminousFlux" }); + + /// Physical dimension: LuminousIntensity + public static readonly DimensionInfo LuminousIntensity = new("LuminousIntensity", "J", new Dictionary { ["luminousIntensity"] = 1 }, new List { "LuminousIntensity" }); + + /// Physical dimension: MagneticFlux + public static readonly DimensionInfo MagneticFlux = new("MagneticFlux", "M L² T⁻² I⁻¹", new Dictionary { ["mass"] = 1, ["length"] = 2, ["time"] = -2, ["electricCurrent"] = -1 }, new List { "MagneticFlux" }); + + /// Physical dimension: MagneticFluxDensity + public static readonly DimensionInfo MagneticFluxDensity = new("MagneticFluxDensity", "M T⁻² I⁻¹", new Dictionary { ["mass"] = 1, ["time"] = -2, ["electricCurrent"] = -1 }, new List { "MagneticFluxDensityMagnitude", "MagneticFluxDensity3D" }); + + /// Physical dimension: Mass + public static readonly DimensionInfo Mass = new("Mass", "M", new Dictionary { ["mass"] = 1 }, new List { "Mass", "AtomicMass" }); + + /// Physical dimension: MassFlowRate + public static readonly DimensionInfo MassFlowRate = new("MassFlowRate", "M T⁻¹", new Dictionary { ["mass"] = 1, ["time"] = -1 }, new List { "MassFlowRate" }); + + /// Physical dimension: MolarEnergy + public static readonly DimensionInfo MolarEnergy = new("MolarEnergy", "M L² T⁻² N⁻¹", new Dictionary { ["mass"] = 1, ["length"] = 2, ["time"] = -2, ["amountOfSubstance"] = -1 }, new List { "MolarEnergy", "ActivationEnergy", "MolarEnthalpy" }); + + /// Physical dimension: MolarMass + public static readonly DimensionInfo MolarMass = new("MolarMass", "M N⁻¹", new Dictionary { ["mass"] = 1, ["amountOfSubstance"] = -1 }, new List { "MolarMass" }); + + /// Physical dimension: MomentOfInertia + public static readonly DimensionInfo MomentOfInertia = new("MomentOfInertia", "M L²", new Dictionary { ["mass"] = 1, ["length"] = 2 }, new List { "MomentOfInertia" }); + + /// Physical dimension: Momentum + public static readonly DimensionInfo Momentum = new("Momentum", "M L T⁻¹", new Dictionary { ["mass"] = 1, ["length"] = 1, ["time"] = -1 }, new List { "MomentumMagnitude", "Momentum1D", "Momentum2D", "Momentum3D", "Momentum4D" }); + + /// Physical dimension: NuclearCrossSection + public static readonly DimensionInfo NuclearCrossSection = new("NuclearCrossSection", "L²", new Dictionary { ["length"] = 2 }, new List { "NuclearCrossSection" }); + + /// Physical dimension: OpticalPower + public static readonly DimensionInfo OpticalPower = new("OpticalPower", "L⁻¹", new Dictionary { ["length"] = -1 }, new List { "OpticalPower" }); + + /// Physical dimension: Permittivity + public static readonly DimensionInfo Permittivity = new("Permittivity", "M⁻¹ L⁻³ T⁴ I²", new Dictionary { ["mass"] = -1, ["length"] = -3, ["time"] = 4, ["electricCurrent"] = 2 }, new List { "Permittivity" }); + + /// Physical dimension: Power + public static readonly DimensionInfo Power = new("Power", "M L² T⁻³", new Dictionary { ["mass"] = 1, ["length"] = 2, ["time"] = -3 }, new List { "Power", "HeatFlowRate", "SoundPower" }); + + /// Physical dimension: Pressure + public static readonly DimensionInfo Pressure = new("Pressure", "M L⁻¹ T⁻²", new Dictionary { ["mass"] = 1, ["length"] = -1, ["time"] = -2 }, new List { "Pressure", "Stress", "AtmosphericPressure", "GaugePressure", "BulkModulus", "YoungsModulus", "ShearModulus", "SoundPressure" }); + + /// Physical dimension: RadioactiveActivity + public static readonly DimensionInfo RadioactiveActivity = new("RadioactiveActivity", "T⁻¹", new Dictionary { ["time"] = -1 }, new List { "RadioactiveActivity" }); + + /// Physical dimension: RateConstant + public static readonly DimensionInfo RateConstant = new("RateConstant", "T⁻¹", new Dictionary { ["time"] = -1 }, new List { "RateConstant" }); + + /// Physical dimension: ReactionRate + public static readonly DimensionInfo ReactionRate = new("ReactionRate", "N L⁻³ T⁻¹", new Dictionary { ["amountOfSubstance"] = 1, ["length"] = -3, ["time"] = -1 }, new List { "ReactionRate" }); + + /// Physical dimension: Sensitivity + public static readonly DimensionInfo Sensitivity = new("Sensitivity", "M⁻¹ L⁻¹ T² I", new Dictionary { ["mass"] = -1, ["length"] = -1, ["time"] = 2, ["electricCurrent"] = 1 }, new List { "Sensitivity" }); + + /// Physical dimension: Sharpness + public static readonly DimensionInfo Sharpness = new("Sharpness", "1", new Dictionary(), new List { "Sharpness" }); + + /// Physical dimension: Snap + public static readonly DimensionInfo Snap = new("Snap", "L T⁻⁴", new Dictionary { ["length"] = 1, ["time"] = -4 }, new List { "SnapMagnitude", "Snap1D", "Snap2D", "Snap3D", "Snap4D" }); + + /// Physical dimension: SpecificHeat + public static readonly DimensionInfo SpecificHeat = new("SpecificHeat", "L² T⁻² Θ⁻¹", new Dictionary { ["length"] = 2, ["time"] = -2, ["temperature"] = -1 }, new List { "SpecificHeat", "SpecificEntropy" }); + + /// Physical dimension: SurfaceTension + public static readonly DimensionInfo SurfaceTension = new("SurfaceTension", "M T⁻²", new Dictionary { ["mass"] = 1, ["time"] = -2 }, new List { "SurfaceTension" }); + + /// Physical dimension: Temperature + public static readonly DimensionInfo Temperature = new("Temperature", "Θ", new Dictionary { ["temperature"] = 1 }, new List { "Temperature", "TemperatureDelta", "TemperatureRise", "TemperatureDrop" }); + + /// Physical dimension: ThermalConductivity + public static readonly DimensionInfo ThermalConductivity = new("ThermalConductivity", "M L T⁻³ Θ⁻¹", new Dictionary { ["mass"] = 1, ["length"] = 1, ["time"] = -3, ["temperature"] = -1 }, new List { "ThermalConductivity" }); + + /// Physical dimension: ThermalExpansion + public static readonly DimensionInfo ThermalExpansion = new("ThermalExpansion", "Θ⁻¹", new Dictionary { ["temperature"] = -1 }, new List { "ThermalExpansionCoefficient" }); + + /// Physical dimension: ThermalResistance + public static readonly DimensionInfo ThermalResistance = new("ThermalResistance", "Θ M⁻¹ L⁻² T³", new Dictionary { ["temperature"] = 1, ["mass"] = -1, ["length"] = -2, ["time"] = 3 }, new List { "ThermalResistance" }); + + /// Physical dimension: Time + public static readonly DimensionInfo Time = new("Time", "T", new Dictionary { ["time"] = 1 }, new List { "Duration", "Period", "HalfLife", "TimeConstant", "Latency", "ReverberationTime", "DecayTime" }); + + /// Physical dimension: Torque + public static readonly DimensionInfo Torque = new("Torque", "M L² T⁻²", new Dictionary { ["mass"] = 1, ["length"] = 2, ["time"] = -2 }, new List { "TorqueMagnitude", "Torque1D", "Torque3D" }); + + /// Physical dimension: Velocity + public static readonly DimensionInfo Velocity = new("Velocity", "L T⁻¹", new Dictionary { ["length"] = 1, ["time"] = -1 }, new List { "Speed", "FlowSpeed", "WindSpeed", "GroundSpeed", "Airspeed", "SoundSpeed", "PhaseVelocity", "GroupVelocity", "Velocity1D", "Velocity2D", "Velocity3D", "WindVelocity3D", "Velocity4D" }); + + /// Physical dimension: Volume + public static readonly DimensionInfo Volume = new("Volume", "L³", new Dictionary { ["length"] = 3 }, new List { "Volume", "Capacity" }); + + /// Physical dimension: VolumetricFlowRate + public static readonly DimensionInfo VolumetricFlowRate = new("VolumetricFlowRate", "L³ T⁻¹", new Dictionary { ["length"] = 3, ["time"] = -1 }, new List { "VolumetricFlowRate" }); + + /// Gets a frozen collection of all standard physical dimensions. + public static IReadOnlySet All = new HashSet([ AbsorbedDose, Acceleration, AcousticImpedance, AmountOfSubstance, AngularAcceleration, AngularDisplacement, AngularJerk, AngularMomentum, AngularVelocity, Area, CatalyticActivity, Concentration, Density, Dimensionless, DynamicViscosity, ElectricCapacitance, ElectricCharge, ElectricConductance, ElectricConductivity, ElectricCurrent, ElectricField, ElectricFlux, ElectricPotential, ElectricPowerDensity, ElectricResistance, Energy, Entropy, EquivalentDose, Exposure, Force, Frequency, HeatTransferCoefficient, Illuminance, Inductance, Irradiance, Jerk, KinematicViscosity, Length, Loudness, Luminance, LuminousFlux, LuminousIntensity, MagneticFlux, MagneticFluxDensity, Mass, MassFlowRate, MolarEnergy, MolarMass, MomentOfInertia, Momentum, NuclearCrossSection, OpticalPower, Permittivity, Power, Pressure, RadioactiveActivity, RateConstant, ReactionRate, Sensitivity, Sharpness, Snap, SpecificHeat, SurfaceTension, Temperature, ThermalConductivity, ThermalExpansion, ThermalResistance, Time, Torque, Velocity, Volume, VolumetricFlowRate ]); + +}; + +/// +/// Marker interface implemented by every unit of the AbsorbedDose dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IAbsorbedDoseUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Acceleration dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IAccelerationUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the AcousticImpedance dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IAcousticImpedanceUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the AmountOfSubstance dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IAmountOfSubstanceUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the AngularAcceleration dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IAngularAccelerationUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the AngularDisplacement dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IAngularDisplacementUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the AngularJerk dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IAngularJerkUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the AngularMomentum dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IAngularMomentumUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the AngularVelocity dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IAngularVelocityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Area dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IAreaUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the CatalyticActivity dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ICatalyticActivityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Concentration dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IConcentrationUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Density dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IDensityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Dimensionless dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IDimensionlessUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the DynamicViscosity dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IDynamicViscosityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ElectricCapacitance dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IElectricCapacitanceUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ElectricCharge dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IElectricChargeUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ElectricConductance dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IElectricConductanceUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ElectricConductivity dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IElectricConductivityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ElectricCurrent dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IElectricCurrentUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ElectricField dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IElectricFieldUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ElectricFlux dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IElectricFluxUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ElectricPotential dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IElectricPotentialUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ElectricPowerDensity dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IElectricPowerDensityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ElectricResistance dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IElectricResistanceUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Energy dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IEnergyUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Entropy dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IEntropyUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the EquivalentDose dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IEquivalentDoseUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Exposure dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IExposureUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Force dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IForceUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Frequency dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IFrequencyUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the HeatTransferCoefficient dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IHeatTransferCoefficientUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Illuminance dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IIlluminanceUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Inductance dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IInductanceUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Irradiance dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IIrradianceUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Jerk dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IJerkUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the KinematicViscosity dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IKinematicViscosityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Length dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ILengthUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Loudness dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ILoudnessUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Luminance dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ILuminanceUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the LuminousFlux dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ILuminousFluxUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the LuminousIntensity dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ILuminousIntensityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the MagneticFlux dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IMagneticFluxUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the MagneticFluxDensity dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IMagneticFluxDensityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Mass dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IMassUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the MassFlowRate dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IMassFlowRateUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the MolarEnergy dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IMolarEnergyUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the MolarMass dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IMolarMassUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the MomentOfInertia dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IMomentOfInertiaUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Momentum dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IMomentumUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the NuclearCrossSection dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface INuclearCrossSectionUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the OpticalPower dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IOpticalPowerUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Permittivity dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IPermittivityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Power dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IPowerUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Pressure dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IPressureUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the RadioactiveActivity dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IRadioactiveActivityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the RateConstant dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IRateConstantUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ReactionRate dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IReactionRateUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Sensitivity dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ISensitivityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Sharpness dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ISharpnessUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Snap dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ISnapUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the SpecificHeat dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ISpecificHeatUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the SurfaceTension dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ISurfaceTensionUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Temperature dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ITemperatureUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ThermalConductivity dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IThermalConductivityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ThermalExpansion dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IThermalExpansionUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the ThermalResistance dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IThermalResistanceUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Time dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ITimeUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Torque dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface ITorqueUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Velocity dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IVelocityUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the Volume dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IVolumeUnit : IUnit +{ } + +/// +/// Marker interface implemented by every unit of the VolumetricFlowRate dimension. +/// Generated quantities use this to make In(...) dimensionally type-safe at compile time. +/// +public interface IVolumetricFlowRateUnit : IUnit +{ } + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/Cents.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/Cents.g.cs new file mode 100644 index 0000000..307bd37 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/Cents.g.cs @@ -0,0 +1,110 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#nullable enable + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Globalization; +using System.Numerics; + +/// +/// Represents a musical pitch interval in cents (1/100 of an equal-tempered semitone). +/// +/// +/// An octave is 1200 cents: cents = 1200·log2(frequencyRatio). +/// Logarithmic scales don't obey linear arithmetic, so this type is generated as a +/// standalone companion (from logarithmic.json) rather than a physical dimension. +/// +/// The floating-point storage type. +/// The scale value. +public readonly partial record struct Cents(T Value) : IComparable> + where T : struct, INumber +{ + /// + /// Creates a value from the raw scale number. + /// + /// The raw scale value. + /// A new . + public static Cents Create(T value) => new(value); + + /// + /// Creates an interval from a frequency ratio using cents = 1200·log2(ratio). + /// + /// The linear . + /// A new . A linear value of zero maps to negative infinity. + public static Cents FromFrequencyRatio(Ratio linear) + { + ArgumentNullException.ThrowIfNull(linear); + double linearValue = double.CreateChecked(linear.Value); + return new(T.CreateChecked(1200.0 * Math.Log2(linearValue))); + } + + /// + /// Converts this interval to a frequency ratio using ratio = 2^(cents/1200). + /// + /// The linear . + public Ratio ToFrequencyRatio() + { + double scaleValue = double.CreateChecked(Value); + return Ratio.Create(T.CreateChecked(Math.Pow(2.0, scaleValue / 1200.0))); + } + + /// Adds two values in log space (cascading two linear stages multiplies them). + /// The first value. + /// The second value. + /// The summed value. + public static Cents operator +(Cents left, Cents right) => new(left.Value + right.Value); + + /// Subtracts one value from another in log space. + /// The value to subtract from. + /// The value to subtract. + /// The difference. + public static Cents operator -(Cents left, Cents right) => new(left.Value - right.Value); + + /// Adds two values (friendly alternate for operator +). + /// The first value. + /// The second value. + /// The summed value. + public static Cents Add(Cents left, Cents right) => left + right; + + /// Subtracts one value from another (friendly alternate for operator -). + /// The value to subtract from. + /// The value to subtract. + /// The difference. + public static Cents Subtract(Cents left, Cents right) => left - right; + + /// + public int CompareTo(Cents other) => Value.CompareTo(other.Value); + + /// Determines whether one value is less than another. + /// The left value. + /// The right value. + /// if is less than . + public static bool operator <(Cents left, Cents right) => left.CompareTo(right) < 0; + + /// Determines whether one value is greater than another. + /// The left value. + /// The right value. + /// if is greater than . + public static bool operator >(Cents left, Cents right) => left.CompareTo(right) > 0; + + /// Determines whether one value is less than or equal to another. + /// The left value. + /// The right value. + /// if is less than or equal to . + public static bool operator <=(Cents left, Cents right) => left.CompareTo(right) <= 0; + + /// Determines whether one value is greater than or equal to another. + /// The left value. + /// The right value. + /// if is greater than or equal to . + public static bool operator >=(Cents left, Cents right) => left.CompareTo(right) >= 0; + + /// Returns a culture-invariant string representation of this value. + /// The formatted value. + public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"{Value} ct"); +} diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/Decibels.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/Decibels.g.cs new file mode 100644 index 0000000..1ecbe38 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/Decibels.g.cs @@ -0,0 +1,132 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#nullable enable + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Globalization; +using System.Numerics; + +/// +/// Represents a logarithmic level in decibels (dB). +/// +/// +/// Decibels express ratios on a logarithmic scale. Amplitude/field quantities use dB = 20·log10(ratio); power quantities use dB = 10·log10(ratio). A level of 0 dB is unity. +/// Logarithmic scales don't obey linear arithmetic, so this type is generated as a +/// standalone companion (from logarithmic.json) rather than a physical dimension. +/// +/// The floating-point storage type. +/// The scale value. +public readonly partial record struct Decibels(T Value) : IComparable> + where T : struct, INumber +{ + /// + /// Creates a value from the raw scale number. + /// + /// The raw scale value. + /// A new . + public static Decibels Create(T value) => new(value); + + /// + /// Creates a level from an amplitude gain using dB = 20·log10(gain). + /// + /// The linear . + /// A new . A linear value of zero maps to negative infinity. + public static Decibels FromGain(Gain linear) + { + ArgumentNullException.ThrowIfNull(linear); + double linearValue = double.CreateChecked(linear.Value); + return new(T.CreateChecked(20.0 * Math.Log10(linearValue))); + } + + /// + /// Converts this level to a linear amplitude gain using gain = 10^(dB/20). + /// + /// The linear . + public Gain ToAmplitude() + { + double scaleValue = double.CreateChecked(Value); + return Gain.Create(T.CreateChecked(Math.Pow(10.0, scaleValue / 20.0))); + } + + /// + /// Creates a level from a linear power ratio using dB = 10·log10(ratio). + /// + /// The linear . + /// A new . A linear value of zero maps to negative infinity. + public static Decibels FromPowerRatio(Ratio linear) + { + ArgumentNullException.ThrowIfNull(linear); + double linearValue = double.CreateChecked(linear.Value); + return new(T.CreateChecked(10.0 * Math.Log10(linearValue))); + } + + /// + /// Converts this level to a linear power ratio using ratio = 10^(dB/10). + /// + /// The linear . + public Ratio ToPower() + { + double scaleValue = double.CreateChecked(Value); + return Ratio.Create(T.CreateChecked(Math.Pow(10.0, scaleValue / 10.0))); + } + + /// Adds two values in log space (cascading two linear stages multiplies them). + /// The first value. + /// The second value. + /// The summed value. + public static Decibels operator +(Decibels left, Decibels right) => new(left.Value + right.Value); + + /// Subtracts one value from another in log space. + /// The value to subtract from. + /// The value to subtract. + /// The difference. + public static Decibels operator -(Decibels left, Decibels right) => new(left.Value - right.Value); + + /// Adds two values (friendly alternate for operator +). + /// The first value. + /// The second value. + /// The summed value. + public static Decibels Add(Decibels left, Decibels right) => left + right; + + /// Subtracts one value from another (friendly alternate for operator -). + /// The value to subtract from. + /// The value to subtract. + /// The difference. + public static Decibels Subtract(Decibels left, Decibels right) => left - right; + + /// + public int CompareTo(Decibels other) => Value.CompareTo(other.Value); + + /// Determines whether one value is less than another. + /// The left value. + /// The right value. + /// if is less than . + public static bool operator <(Decibels left, Decibels right) => left.CompareTo(right) < 0; + + /// Determines whether one value is greater than another. + /// The left value. + /// The right value. + /// if is greater than . + public static bool operator >(Decibels left, Decibels right) => left.CompareTo(right) > 0; + + /// Determines whether one value is less than or equal to another. + /// The left value. + /// The right value. + /// if is less than or equal to . + public static bool operator <=(Decibels left, Decibels right) => left.CompareTo(right) <= 0; + + /// Determines whether one value is greater than or equal to another. + /// The left value. + /// The right value. + /// if is greater than or equal to . + public static bool operator >=(Decibels left, Decibels right) => left.CompareTo(right) >= 0; + + /// Returns a culture-invariant string representation of this value. + /// The formatted value. + public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"{Value} dB"); +} diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/DirectionalityIndex.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/DirectionalityIndex.g.cs new file mode 100644 index 0000000..3dcc1aa --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/DirectionalityIndex.g.cs @@ -0,0 +1,86 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#nullable enable + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Globalization; +using System.Numerics; + +/// +/// Represents a directivity index (DI) in decibels — how much more intense a source or receiver is on-axis than its spherical average. +/// +/// +/// DI = 10·log10(I_axis / I_average). +/// Logarithmic scales don't obey linear arithmetic, so this type is generated as a +/// standalone companion (from logarithmic.json) rather than a physical dimension. +/// +/// The floating-point storage type. +/// The scale value. +public readonly partial record struct DirectionalityIndex(T Value) : IComparable> + where T : struct, INumber +{ + /// + /// Creates a value from the raw scale number. + /// + /// The raw scale value. + /// A new . + public static DirectionalityIndex FromDecibels(T value) => new(value); + + /// + /// Creates an index from the linear on-axis-to-average intensity ratio using DI = 10·log10(ratio). + /// + /// The linear . + /// A new . A linear value of zero maps to negative infinity. + public static DirectionalityIndex FromIntensityRatio(Ratio linear) + { + ArgumentNullException.ThrowIfNull(linear); + double linearValue = double.CreateChecked(linear.Value); + return new(T.CreateChecked(10.0 * Math.Log10(linearValue))); + } + + /// + /// Converts this index to the linear intensity ratio using ratio = 10^(DI/10). + /// + /// The linear . + public Ratio ToIntensityRatio() + { + double scaleValue = double.CreateChecked(Value); + return Ratio.Create(T.CreateChecked(Math.Pow(10.0, scaleValue / 10.0))); + } + + /// + public int CompareTo(DirectionalityIndex other) => Value.CompareTo(other.Value); + + /// Determines whether one value is less than another. + /// The left value. + /// The right value. + /// if is less than . + public static bool operator <(DirectionalityIndex left, DirectionalityIndex right) => left.CompareTo(right) < 0; + + /// Determines whether one value is greater than another. + /// The left value. + /// The right value. + /// if is greater than . + public static bool operator >(DirectionalityIndex left, DirectionalityIndex right) => left.CompareTo(right) > 0; + + /// Determines whether one value is less than or equal to another. + /// The left value. + /// The right value. + /// if is less than or equal to . + public static bool operator <=(DirectionalityIndex left, DirectionalityIndex right) => left.CompareTo(right) <= 0; + + /// Determines whether one value is greater than or equal to another. + /// The left value. + /// The right value. + /// if is greater than or equal to . + public static bool operator >=(DirectionalityIndex left, DirectionalityIndex right) => left.CompareTo(right) >= 0; + + /// Returns a culture-invariant string representation of this value. + /// The formatted value. + public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"{Value} dB"); +} diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/PH.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/PH.g.cs new file mode 100644 index 0000000..539ca96 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/PH.g.cs @@ -0,0 +1,88 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#nullable enable + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Globalization; +using System.Numerics; + +/// +/// Represents acidity on the pH scale: pH = −log10([H⁺]) with the hydrogen-ion activity in mol/L. +/// +/// +/// pH is a logarithmic scale over the linear Concentration quantity (stored in the SI base mol/m³; 1 mol/L = 1000 mol/m³). +/// Logarithmic scales don't obey linear arithmetic, so this type is generated as a +/// standalone companion (from logarithmic.json) rather than a physical dimension. +/// +/// The floating-point storage type. +/// The scale value. +public readonly partial record struct PH(T Value) : IComparable> + where T : struct, INumber +{ + /// + /// Creates a value from the raw scale number. + /// + /// The raw scale value. + /// A new . + public static PH Create(T value) => new(value); + + /// + /// Creates a pH from a hydrogen-ion concentration using pH = −log10([H⁺] in mol/L). + /// + /// The linear . + /// A new . A linear value of zero maps to negative infinity. + public static PH FromHydrogenConcentration(Concentration linear) + { + ArgumentNullException.ThrowIfNull(linear); + double linearValue = double.CreateChecked(linear.Value); + double reference = 1000.0; + return new(T.CreateChecked(-1.0 * Math.Log10(linearValue / reference))); + } + + /// + /// Converts this pH to the equivalent hydrogen-ion concentration using [H⁺] = 10^(−pH) mol/L. + /// + /// The linear . + public Concentration ToHydrogenConcentration() + { + double scaleValue = double.CreateChecked(Value); + double reference = 1000.0; + return Concentration.Create(T.CreateChecked(reference * Math.Pow(10.0, scaleValue / -1.0))); + } + + /// + public int CompareTo(PH other) => Value.CompareTo(other.Value); + + /// Determines whether one value is less than another. + /// The left value. + /// The right value. + /// if is less than . + public static bool operator <(PH left, PH right) => left.CompareTo(right) < 0; + + /// Determines whether one value is greater than another. + /// The left value. + /// The right value. + /// if is greater than . + public static bool operator >(PH left, PH right) => left.CompareTo(right) > 0; + + /// Determines whether one value is less than or equal to another. + /// The left value. + /// The right value. + /// if is less than or equal to . + public static bool operator <=(PH left, PH right) => left.CompareTo(right) <= 0; + + /// Determines whether one value is greater than or equal to another. + /// The left value. + /// The right value. + /// if is greater than or equal to . + public static bool operator >=(PH left, PH right) => left.CompareTo(right) >= 0; + + /// Returns a culture-invariant string representation of this value. + /// The formatted value. + public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"pH {Value}"); +} diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/Semitones.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/Semitones.g.cs new file mode 100644 index 0000000..83e9680 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/Semitones.g.cs @@ -0,0 +1,110 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#nullable enable + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Globalization; +using System.Numerics; + +/// +/// Represents a musical pitch interval in equal-tempered semitones. +/// +/// +/// An octave is 12 semitones: semitones = 12·log2(frequencyRatio). +/// Logarithmic scales don't obey linear arithmetic, so this type is generated as a +/// standalone companion (from logarithmic.json) rather than a physical dimension. +/// +/// The floating-point storage type. +/// The scale value. +public readonly partial record struct Semitones(T Value) : IComparable> + where T : struct, INumber +{ + /// + /// Creates a value from the raw scale number. + /// + /// The raw scale value. + /// A new . + public static Semitones Create(T value) => new(value); + + /// + /// Creates an interval from a frequency ratio using semitones = 12·log2(ratio). + /// + /// The linear . + /// A new . A linear value of zero maps to negative infinity. + public static Semitones FromFrequencyRatio(Ratio linear) + { + ArgumentNullException.ThrowIfNull(linear); + double linearValue = double.CreateChecked(linear.Value); + return new(T.CreateChecked(12.0 * Math.Log2(linearValue))); + } + + /// + /// Converts this interval to a frequency ratio using ratio = 2^(semitones/12). + /// + /// The linear . + public Ratio ToFrequencyRatio() + { + double scaleValue = double.CreateChecked(Value); + return Ratio.Create(T.CreateChecked(Math.Pow(2.0, scaleValue / 12.0))); + } + + /// Adds two values in log space (cascading two linear stages multiplies them). + /// The first value. + /// The second value. + /// The summed value. + public static Semitones operator +(Semitones left, Semitones right) => new(left.Value + right.Value); + + /// Subtracts one value from another in log space. + /// The value to subtract from. + /// The value to subtract. + /// The difference. + public static Semitones operator -(Semitones left, Semitones right) => new(left.Value - right.Value); + + /// Adds two values (friendly alternate for operator +). + /// The first value. + /// The second value. + /// The summed value. + public static Semitones Add(Semitones left, Semitones right) => left + right; + + /// Subtracts one value from another (friendly alternate for operator -). + /// The value to subtract from. + /// The value to subtract. + /// The difference. + public static Semitones Subtract(Semitones left, Semitones right) => left - right; + + /// + public int CompareTo(Semitones other) => Value.CompareTo(other.Value); + + /// Determines whether one value is less than another. + /// The left value. + /// The right value. + /// if is less than . + public static bool operator <(Semitones left, Semitones right) => left.CompareTo(right) < 0; + + /// Determines whether one value is greater than another. + /// The left value. + /// The right value. + /// if is greater than . + public static bool operator >(Semitones left, Semitones right) => left.CompareTo(right) > 0; + + /// Determines whether one value is less than or equal to another. + /// The left value. + /// The right value. + /// if is less than or equal to . + public static bool operator <=(Semitones left, Semitones right) => left.CompareTo(right) <= 0; + + /// Determines whether one value is greater than or equal to another. + /// The left value. + /// The right value. + /// if is greater than or equal to . + public static bool operator >=(Semitones left, Semitones right) => left.CompareTo(right) >= 0; + + /// Returns a culture-invariant string representation of this value. + /// The formatted value. + public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"{Value} st"); +} diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/SoundIntensityLevel.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/SoundIntensityLevel.g.cs new file mode 100644 index 0000000..72a09c3 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/SoundIntensityLevel.g.cs @@ -0,0 +1,112 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#nullable enable + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Globalization; +using System.Numerics; + +/// +/// Represents a sound intensity level (SIL) in decibels relative to the 10⁻¹² W/m² threshold of hearing. +/// +/// +/// SIL is a logarithmic power quantity: SIL = 10·log10(I / I₀) with I₀ = 10⁻¹² W/m². +/// Logarithmic scales don't obey linear arithmetic, so this type is generated as a +/// standalone companion (from logarithmic.json) rather than a physical dimension. +/// +/// The floating-point storage type. +/// The scale value. +public readonly partial record struct SoundIntensityLevel(T Value) : IComparable> + where T : struct, INumber +{ + /// + /// Creates a value from the raw scale number. + /// + /// The raw scale value. + /// A new . + public static SoundIntensityLevel FromDecibels(T value) => new(value); + + /// + /// Creates a level from a linear sound intensity using SIL = 10·log10(I / I₀). + /// + /// The linear . + /// A new . A linear value of zero maps to negative infinity. + public static SoundIntensityLevel FromSoundIntensity(SoundIntensity linear) + { + ArgumentNullException.ThrowIfNull(linear); + double linearValue = double.CreateChecked(linear.Value); + double reference = PhysicalConstants.Generic.ReferenceSoundIntensity(); + return new(T.CreateChecked(10.0 * Math.Log10(linearValue / reference))); + } + + /// + /// Converts this level to the equivalent linear sound intensity using I = I₀·10^(SIL/10). + /// + /// The linear . + public SoundIntensity ToSoundIntensity() + { + double scaleValue = double.CreateChecked(Value); + double reference = PhysicalConstants.Generic.ReferenceSoundIntensity(); + return SoundIntensity.Create(T.CreateChecked(reference * Math.Pow(10.0, scaleValue / 10.0))); + } + + /// Adds two values in log space (cascading two linear stages multiplies them). + /// The first value. + /// The second value. + /// The summed value. + public static SoundIntensityLevel operator +(SoundIntensityLevel left, SoundIntensityLevel right) => new(left.Value + right.Value); + + /// Subtracts one value from another in log space. + /// The value to subtract from. + /// The value to subtract. + /// The difference. + public static SoundIntensityLevel operator -(SoundIntensityLevel left, SoundIntensityLevel right) => new(left.Value - right.Value); + + /// Adds two values (friendly alternate for operator +). + /// The first value. + /// The second value. + /// The summed value. + public static SoundIntensityLevel Add(SoundIntensityLevel left, SoundIntensityLevel right) => left + right; + + /// Subtracts one value from another (friendly alternate for operator -). + /// The value to subtract from. + /// The value to subtract. + /// The difference. + public static SoundIntensityLevel Subtract(SoundIntensityLevel left, SoundIntensityLevel right) => left - right; + + /// + public int CompareTo(SoundIntensityLevel other) => Value.CompareTo(other.Value); + + /// Determines whether one value is less than another. + /// The left value. + /// The right value. + /// if is less than . + public static bool operator <(SoundIntensityLevel left, SoundIntensityLevel right) => left.CompareTo(right) < 0; + + /// Determines whether one value is greater than another. + /// The left value. + /// The right value. + /// if is greater than . + public static bool operator >(SoundIntensityLevel left, SoundIntensityLevel right) => left.CompareTo(right) > 0; + + /// Determines whether one value is less than or equal to another. + /// The left value. + /// The right value. + /// if is less than or equal to . + public static bool operator <=(SoundIntensityLevel left, SoundIntensityLevel right) => left.CompareTo(right) <= 0; + + /// Determines whether one value is greater than or equal to another. + /// The left value. + /// The right value. + /// if is greater than or equal to . + public static bool operator >=(SoundIntensityLevel left, SoundIntensityLevel right) => left.CompareTo(right) >= 0; + + /// Returns a culture-invariant string representation of this value. + /// The formatted value. + public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"{Value} dB SIL"); +} diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/SoundPowerLevel.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/SoundPowerLevel.g.cs new file mode 100644 index 0000000..a0aa125 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/SoundPowerLevel.g.cs @@ -0,0 +1,112 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#nullable enable + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Globalization; +using System.Numerics; + +/// +/// Represents a sound power level (SWL) in decibels relative to the 10⁻¹² W reference sound power. +/// +/// +/// SWL is a logarithmic power quantity: SWL = 10·log10(P / P₀) with P₀ = 10⁻¹² W. +/// Logarithmic scales don't obey linear arithmetic, so this type is generated as a +/// standalone companion (from logarithmic.json) rather than a physical dimension. +/// +/// The floating-point storage type. +/// The scale value. +public readonly partial record struct SoundPowerLevel(T Value) : IComparable> + where T : struct, INumber +{ + /// + /// Creates a value from the raw scale number. + /// + /// The raw scale value. + /// A new . + public static SoundPowerLevel FromDecibels(T value) => new(value); + + /// + /// Creates a level from a linear sound power using SWL = 10·log10(P / P₀). + /// + /// The linear . + /// A new . A linear value of zero maps to negative infinity. + public static SoundPowerLevel FromSoundPower(SoundPower linear) + { + ArgumentNullException.ThrowIfNull(linear); + double linearValue = double.CreateChecked(linear.Value); + double reference = PhysicalConstants.Generic.ReferenceSoundPower(); + return new(T.CreateChecked(10.0 * Math.Log10(linearValue / reference))); + } + + /// + /// Converts this level to the equivalent linear sound power using P = P₀·10^(SWL/10). + /// + /// The linear . + public SoundPower ToSoundPower() + { + double scaleValue = double.CreateChecked(Value); + double reference = PhysicalConstants.Generic.ReferenceSoundPower(); + return SoundPower.Create(T.CreateChecked(reference * Math.Pow(10.0, scaleValue / 10.0))); + } + + /// Adds two values in log space (cascading two linear stages multiplies them). + /// The first value. + /// The second value. + /// The summed value. + public static SoundPowerLevel operator +(SoundPowerLevel left, SoundPowerLevel right) => new(left.Value + right.Value); + + /// Subtracts one value from another in log space. + /// The value to subtract from. + /// The value to subtract. + /// The difference. + public static SoundPowerLevel operator -(SoundPowerLevel left, SoundPowerLevel right) => new(left.Value - right.Value); + + /// Adds two values (friendly alternate for operator +). + /// The first value. + /// The second value. + /// The summed value. + public static SoundPowerLevel Add(SoundPowerLevel left, SoundPowerLevel right) => left + right; + + /// Subtracts one value from another (friendly alternate for operator -). + /// The value to subtract from. + /// The value to subtract. + /// The difference. + public static SoundPowerLevel Subtract(SoundPowerLevel left, SoundPowerLevel right) => left - right; + + /// + public int CompareTo(SoundPowerLevel other) => Value.CompareTo(other.Value); + + /// Determines whether one value is less than another. + /// The left value. + /// The right value. + /// if is less than . + public static bool operator <(SoundPowerLevel left, SoundPowerLevel right) => left.CompareTo(right) < 0; + + /// Determines whether one value is greater than another. + /// The left value. + /// The right value. + /// if is greater than . + public static bool operator >(SoundPowerLevel left, SoundPowerLevel right) => left.CompareTo(right) > 0; + + /// Determines whether one value is less than or equal to another. + /// The left value. + /// The right value. + /// if is less than or equal to . + public static bool operator <=(SoundPowerLevel left, SoundPowerLevel right) => left.CompareTo(right) <= 0; + + /// Determines whether one value is greater than or equal to another. + /// The left value. + /// The right value. + /// if is greater than or equal to . + public static bool operator >=(SoundPowerLevel left, SoundPowerLevel right) => left.CompareTo(right) >= 0; + + /// Returns a culture-invariant string representation of this value. + /// The formatted value. + public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"{Value} dB SWL"); +} diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/SoundPressureLevel.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/SoundPressureLevel.g.cs new file mode 100644 index 0000000..8908d7e --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.LogarithmicScalesGenerator/SoundPressureLevel.g.cs @@ -0,0 +1,112 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#nullable enable + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Globalization; +using System.Numerics; + +/// +/// Represents a sound pressure level (SPL) in decibels relative to the 20 µPa threshold of hearing. +/// +/// +/// SPL is a logarithmic field quantity: SPL = 20·log10(p / p₀) with p₀ = 20 µPa. +/// Logarithmic scales don't obey linear arithmetic, so this type is generated as a +/// standalone companion (from logarithmic.json) rather than a physical dimension. +/// +/// The floating-point storage type. +/// The scale value. +public readonly partial record struct SoundPressureLevel(T Value) : IComparable> + where T : struct, INumber +{ + /// + /// Creates a value from the raw scale number. + /// + /// The raw scale value. + /// A new . + public static SoundPressureLevel FromDecibels(T value) => new(value); + + /// + /// Creates a level from a linear sound pressure using SPL = 20·log10(p / p₀). + /// + /// The linear . + /// A new . A linear value of zero maps to negative infinity. + public static SoundPressureLevel FromSoundPressure(SoundPressure linear) + { + ArgumentNullException.ThrowIfNull(linear); + double linearValue = double.CreateChecked(linear.Value); + double reference = PhysicalConstants.Generic.ReferenceSoundPressure(); + return new(T.CreateChecked(20.0 * Math.Log10(linearValue / reference))); + } + + /// + /// Converts this level to the equivalent linear sound pressure using p = p₀·10^(SPL/20). + /// + /// The linear . + public SoundPressure ToSoundPressure() + { + double scaleValue = double.CreateChecked(Value); + double reference = PhysicalConstants.Generic.ReferenceSoundPressure(); + return SoundPressure.Create(T.CreateChecked(reference * Math.Pow(10.0, scaleValue / 20.0))); + } + + /// Adds two values in log space (cascading two linear stages multiplies them). + /// The first value. + /// The second value. + /// The summed value. + public static SoundPressureLevel operator +(SoundPressureLevel left, SoundPressureLevel right) => new(left.Value + right.Value); + + /// Subtracts one value from another in log space. + /// The value to subtract from. + /// The value to subtract. + /// The difference. + public static SoundPressureLevel operator -(SoundPressureLevel left, SoundPressureLevel right) => new(left.Value - right.Value); + + /// Adds two values (friendly alternate for operator +). + /// The first value. + /// The second value. + /// The summed value. + public static SoundPressureLevel Add(SoundPressureLevel left, SoundPressureLevel right) => left + right; + + /// Subtracts one value from another (friendly alternate for operator -). + /// The value to subtract from. + /// The value to subtract. + /// The difference. + public static SoundPressureLevel Subtract(SoundPressureLevel left, SoundPressureLevel right) => left - right; + + /// + public int CompareTo(SoundPressureLevel other) => Value.CompareTo(other.Value); + + /// Determines whether one value is less than another. + /// The left value. + /// The right value. + /// if is less than . + public static bool operator <(SoundPressureLevel left, SoundPressureLevel right) => left.CompareTo(right) < 0; + + /// Determines whether one value is greater than another. + /// The left value. + /// The right value. + /// if is greater than . + public static bool operator >(SoundPressureLevel left, SoundPressureLevel right) => left.CompareTo(right) > 0; + + /// Determines whether one value is less than or equal to another. + /// The left value. + /// The right value. + /// if is less than or equal to . + public static bool operator <=(SoundPressureLevel left, SoundPressureLevel right) => left.CompareTo(right) <= 0; + + /// Determines whether one value is greater than or equal to another. + /// The left value. + /// The right value. + /// if is greater than or equal to . + public static bool operator >=(SoundPressureLevel left, SoundPressureLevel right) => left.CompareTo(right) >= 0; + + /// Returns a culture-invariant string representation of this value. + /// The formatted value. + public override string ToString() => string.Create(CultureInfo.InvariantCulture, $"{Value} dB SPL"); +} diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.MagnitudesGenerator/MetricMagnitudes.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.MagnitudesGenerator/MetricMagnitudes.g.cs new file mode 100644 index 0000000..fe2d9f6 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.MagnitudesGenerator/MetricMagnitudes.g.cs @@ -0,0 +1,76 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +/// +/// Metric magnitude constants for unit scaling. +/// +public static class MetricMagnitudes{ + /// Yotta magnitude (Y): 10^24 + public const double Yotta = 1e24; + + /// Zetta magnitude (Z): 10^21 + public const double Zetta = 1e21; + + /// Exa magnitude (E): 10^18 + public const double Exa = 1e18; + + /// Peta magnitude (P): 10^15 + public const double Peta = 1e15; + + /// Tera magnitude (T): 10^12 + public const double Tera = 1e12; + + /// Giga magnitude (G): 10^9 + public const double Giga = 1e9; + + /// Mega magnitude (M): 10^6 + public const double Mega = 1e6; + + /// Kilo magnitude (k): 10^3 + public const double Kilo = 1e3; + + /// Hecto magnitude (h): 10^2 + public const double Hecto = 1e2; + + /// Deka magnitude (da): 10^1 + public const double Deka = 1e1; + + /// Unity magnitude (): 10^0 + public const double Unity = 1.0; + + /// Deci magnitude (d): 10^-1 + public const double Deci = 1e-1; + + /// Centi magnitude (c): 10^-2 + public const double Centi = 1e-2; + + /// Milli magnitude (m): 10^-3 + public const double Milli = 1e-3; + + /// Micro magnitude (μ): 10^-6 + public const double Micro = 1e-6; + + /// Nano magnitude (n): 10^-9 + public const double Nano = 1e-9; + + /// Pico magnitude (p): 10^-12 + public const double Pico = 1e-12; + + /// Femto magnitude (f): 10^-15 + public const double Femto = 1e-15; + + /// Atto magnitude (a): 10^-18 + public const double Atto = 1e-18; + + /// Zepto magnitude (z): 10^-21 + public const double Zepto = 1e-21; + + /// Yocto magnitude (y): 10^-24 + public const double Yocto = 1e-24; + +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.PhysicalConstantsGenerator/PhysicalConstants.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.PhysicalConstantsGenerator/PhysicalConstants.g.cs new file mode 100644 index 0000000..01327fe --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.PhysicalConstantsGenerator/PhysicalConstants.g.cs @@ -0,0 +1,238 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Globalization; +using System.Numerics; +using ktsu.PreciseNumber; + +/// +/// Provides fundamental physical constants used throughout the Semantics library. +/// All values are based on the 2019 redefinition of SI base units and CODATA 2018 values. +/// +public static class PhysicalConstants{ + + /// + /// The physics of sound and vibration, including wave propagation, acoustic properties, sound intensity, frequency analysis, and audio-related measurements + /// + public static class Acoustics{ + /// Reference sound intensity: 1 × 10⁻¹² W/m² (threshold of hearing) + public static readonly PreciseNumber ReferenceSoundIntensity = PreciseNumber.Parse("1e-12", CultureInfo.InvariantCulture); + + /// Reference sound power: 1 × 10⁻¹² W + public static readonly PreciseNumber ReferenceSoundPower = PreciseNumber.Parse("1e-12", CultureInfo.InvariantCulture); + + /// Reference sound pressure: 20 × 10⁻⁶ Pa (threshold of hearing) + public static readonly PreciseNumber ReferenceSoundPressure = PreciseNumber.Parse("20e-6", CultureInfo.InvariantCulture); + + /// Sabine reverberation constant: 0.161 s/m + public static readonly PreciseNumber SabineConstant = PreciseNumber.Parse("0.161", CultureInfo.InvariantCulture); + + }; + + /// + /// Rotational motion and angular quantities, including angular velocity, angular acceleration, torque, and moment of inertia + /// + public static class AngularMechanics{ + /// Degrees per radian: 180/π ≈ 57.29577951308232 + public static readonly PreciseNumber DegreesPerRadian = PreciseNumber.Parse("57.29577951308232087679815481410517033240547246656432154916024386120284714832155263244096899585111094418897585567892854596978524038074810298080734906", CultureInfo.InvariantCulture); + + /// Radians per degree: π/180 ≈ 0.017453292519943295 + public static readonly PreciseNumber RadiansPerDegree = PreciseNumber.Parse("0.017453292519943295769236907684886127134428718885417254560971914401710091146034494436822415696345097379101040706699150667990539631694451077627806983", CultureInfo.InvariantCulture); + + /// 2π - Full rotation in radians: 6.283185307179586 + public static readonly PreciseNumber TwoPi = PreciseNumber.Parse("6.283185307179586476925286766559005768394338798750211641949889184615632812572417997256069650684234135964735462226659258240820374631042607435096896808248", CultureInfo.InvariantCulture); + + }; + + /// + /// Chemical quantities and processes, including molecular properties, reaction kinetics, chemical concentrations, and the physical aspects of chemical systems + /// + public static class Chemistry{ + /// Gas constant: 8.31446261815324 J/(mol·K) (exact, derived from Avogadro and Boltzmann constants) + public static readonly PreciseNumber GasConstant = PreciseNumber.Parse("8.31446261815324", CultureInfo.InvariantCulture); + + /// Natural logarithm of 2: 0.6931471805599453 + public static readonly PreciseNumber Ln2 = PreciseNumber.Parse("0.6931471805599453094172321214581765680755001343602552541206800094933936219696947156058633269964186875420014810205706857336855202357581305570326707", CultureInfo.InvariantCulture); + + /// Molar volume of ideal gas at STP: 22.413969545014137 L/mol (calculated from R*T/P at 273.15K, 101325Pa) + public static readonly PreciseNumber MolarVolumeSTP = PreciseNumber.Parse("22.413969545014137", CultureInfo.InvariantCulture); + + /// Neutral pH value at 25°C: 7.0 + public static readonly PreciseNumber NeutralPH = PreciseNumber.Parse("7.0", CultureInfo.InvariantCulture); + + /// Water ion product (Kw) at 25°C: 1.0 × 10⁻¹⁴ (pKw = 14.0) + public static readonly PreciseNumber WaterIonProduct = PreciseNumber.Parse("14.0", CultureInfo.InvariantCulture); + + }; + + /// + /// The physics of motion for macroscopic objects, covering forces, momentum, energy, rotational dynamics, and the motion of particles and rigid bodies + /// + public static class ClassicalMechanics{ + /// Standard atmospheric pressure: 101,325 Pa (exact by definition) + public static readonly PreciseNumber StandardAtmosphericPressure = PreciseNumber.Parse("101325", CultureInfo.InvariantCulture); + + /// Standard gravitational acceleration: 9.80665 m/s² (exact by definition) + public static readonly PreciseNumber StandardGravity = PreciseNumber.Parse("9.80665", CultureInfo.InvariantCulture); + + }; + + /// + /// The study of fluids (liquids and gases) in motion and at rest, including fluid properties, flow dynamics, viscosity, pressure, and fluid-structure interactions + /// + public static class FluidMechanics{ + /// Standard air density at 15°C and 1 atm: 1.225 kg/m³ (ISO 2533) + public static readonly PreciseNumber StandardAirDensity = PreciseNumber.Parse("1.225", CultureInfo.InvariantCulture); + + /// Water surface tension at 20°C: 0.0728 N/m (NIST) + public static readonly PreciseNumber WaterSurfaceTension = PreciseNumber.Parse("0.0728", CultureInfo.InvariantCulture); + + }; + + /// + /// Basic physical quantities and constants that form the foundation of all other physics domains, including fundamental units like length, mass, time, and universal constants + /// + public static class Fundamental{ + /// Avogadro's number: 6.02214076 × 10²³ entities/mol (exact, SI defining constant) + public static readonly PreciseNumber AvogadroNumber = PreciseNumber.Parse("6.02214076e23", CultureInfo.InvariantCulture); + + /// Boltzmann constant: 1.380649 × 10⁻²³ J/K (exact, SI defining constant) + public static readonly PreciseNumber BoltzmannConstant = PreciseNumber.Parse("1.380649e-23", CultureInfo.InvariantCulture); + + /// Elementary charge: 1.602176634 × 10⁻¹⁹ C (exact, SI defining constant) + public static readonly PreciseNumber ElementaryCharge = PreciseNumber.Parse("1.602176634e-19", CultureInfo.InvariantCulture); + + /// Fine structure constant: 7.2973525693 × 10⁻³ (dimensionless, 2018 CODATA) + public static readonly PreciseNumber FineStructureConstant = PreciseNumber.Parse("7.2973525693e-3", CultureInfo.InvariantCulture); + + /// Gravitational constant: 6.67430 × 10⁻¹¹ m³/(kg⋅s²) (2018 CODATA) + public static readonly PreciseNumber GravitationalConstant = PreciseNumber.Parse("6.67430e-11", CultureInfo.InvariantCulture); + + /// Magnetic permeability of free space: 4π × 10⁻⁷ H/m (exact by definition) + public static readonly PreciseNumber PermeabilityOfFreeSpace = PreciseNumber.Parse("1.25663706212e-6", CultureInfo.InvariantCulture); + + /// Electric permittivity of free space: 8.8541878128 × 10⁻¹² F/m (exact, derived) + public static readonly PreciseNumber PermittivityOfFreeSpace = PreciseNumber.Parse("8.8541878128e-12", CultureInfo.InvariantCulture); + + /// Planck constant: 6.62607015 × 10⁻³⁴ J·s (exact, SI defining constant) + public static readonly PreciseNumber PlanckConstant = PreciseNumber.Parse("6.62607015e-34", CultureInfo.InvariantCulture); + + /// Speed of light in vacuum: 299,792,458 m/s (exact, SI defining constant) + public static readonly PreciseNumber SpeedOfLight = PreciseNumber.Parse("299792458", CultureInfo.InvariantCulture); + + }; + + /// + /// The study of atomic nuclei, radioactivity, nuclear reactions, decay processes, and radiation interactions with matter + /// + public static class NuclearPhysics{ + /// Atomic mass unit: 1.66053906660 × 10⁻²⁷ kg (2018 CODATA) + public static readonly PreciseNumber AtomicMassUnit = PreciseNumber.Parse("1.66053906660e-27", CultureInfo.InvariantCulture); + + /// Nuclear magneton: 5.0507837461 × 10⁻²⁷ J/T (2018 CODATA) + public static readonly PreciseNumber NuclearMagneton = PreciseNumber.Parse("5.0507837461e-27", CultureInfo.InvariantCulture); + + }; + + /// + /// The physics of light and optical phenomena, including electromagnetic radiation, photometry, illumination, optical properties, and light-matter interactions + /// + public static class Optics{ + /// Luminous efficacy of monochromatic radiation at 540 THz: 683 lm/W (exact, SI defining constant) + public static readonly PreciseNumber LuminousEfficacy = PreciseNumber.Parse("683.0", CultureInfo.InvariantCulture); + + }; + + /// + /// The physics of heat, temperature, and energy transfer, including thermal properties, heat capacity, entropy, and thermodynamic processes + /// + public static class Thermodynamics{ + /// Absolute zero in Celsius: 273.15 K (exact by definition) + public static readonly PreciseNumber AbsoluteZeroInCelsius = PreciseNumber.Parse("273.15", CultureInfo.InvariantCulture); + + /// Standard temperature (STP): 273.15 K (0°C) + public static readonly PreciseNumber StandardTemperature = PreciseNumber.Parse("273.15", CultureInfo.InvariantCulture); + + /// Water boiling point at 1 atm: 373.15 K (100°C) + public static readonly PreciseNumber WaterBoilingPoint = PreciseNumber.Parse("373.15", CultureInfo.InvariantCulture); + + /// Water triple point: 273.16 K (exact by definition) + public static readonly PreciseNumber WaterTriplePoint = PreciseNumber.Parse("273.16", CultureInfo.InvariantCulture); + + }; + + /// + /// Helper methods to get constants as generic numeric types. + /// + public static class Generic{ + /// Gets absolute zero in celsius: 273.15 k (exact by definition) as type T. + public static T AbsoluteZeroInCelsius() where T : struct, INumber => Thermodynamics.AbsoluteZeroInCelsius.To(); +/// Gets atomic mass unit: 1.66053906660 × 10⁻²⁷ kg (2018 codata) as type T. + public static T AtomicMassUnit() where T : struct, INumber => NuclearPhysics.AtomicMassUnit.To(); +/// Gets avogadro's number: 6.02214076 × 10²³ entities/mol (exact, si defining constant) as type T. + public static T AvogadroNumber() where T : struct, INumber => Fundamental.AvogadroNumber.To(); +/// Gets boltzmann constant: 1.380649 × 10⁻²³ j/k (exact, si defining constant) as type T. + public static T BoltzmannConstant() where T : struct, INumber => Fundamental.BoltzmannConstant.To(); +/// Gets degrees per radian: 180/π ≈ 57.29577951308232 as type T. + public static T DegreesPerRadian() where T : struct, INumber => AngularMechanics.DegreesPerRadian.To(); +/// Gets elementary charge: 1.602176634 × 10⁻¹⁹ c (exact, si defining constant) as type T. + public static T ElementaryCharge() where T : struct, INumber => Fundamental.ElementaryCharge.To(); +/// Gets fine structure constant: 7.2973525693 × 10⁻³ (dimensionless, 2018 codata) as type T. + public static T FineStructureConstant() where T : struct, INumber => Fundamental.FineStructureConstant.To(); +/// Gets gas constant: 8.31446261815324 j/(mol·k) (exact, derived from avogadro and boltzmann constants) as type T. + public static T GasConstant() where T : struct, INumber => Chemistry.GasConstant.To(); +/// Gets gravitational constant: 6.67430 × 10⁻¹¹ m³/(kg⋅s²) (2018 codata) as type T. + public static T GravitationalConstant() where T : struct, INumber => Fundamental.GravitationalConstant.To(); +/// Gets natural logarithm of 2: 0.6931471805599453 as type T. + public static T Ln2() where T : struct, INumber => Chemistry.Ln2.To(); +/// Gets luminous efficacy of monochromatic radiation at 540 thz: 683 lm/w (exact, si defining constant) as type T. + public static T LuminousEfficacy() where T : struct, INumber => Optics.LuminousEfficacy.To(); +/// Gets molar volume of ideal gas at stp: 22.413969545014137 l/mol (calculated from r*t/p at 273.15k, 101325pa) as type T. + public static T MolarVolumeSTP() where T : struct, INumber => Chemistry.MolarVolumeSTP.To(); +/// Gets neutral ph value at 25°c: 7.0 as type T. + public static T NeutralPH() where T : struct, INumber => Chemistry.NeutralPH.To(); +/// Gets nuclear magneton: 5.0507837461 × 10⁻²⁷ j/t (2018 codata) as type T. + public static T NuclearMagneton() where T : struct, INumber => NuclearPhysics.NuclearMagneton.To(); +/// Gets magnetic permeability of free space: 4π × 10⁻⁷ h/m (exact by definition) as type T. + public static T PermeabilityOfFreeSpace() where T : struct, INumber => Fundamental.PermeabilityOfFreeSpace.To(); +/// Gets electric permittivity of free space: 8.8541878128 × 10⁻¹² f/m (exact, derived) as type T. + public static T PermittivityOfFreeSpace() where T : struct, INumber => Fundamental.PermittivityOfFreeSpace.To(); +/// Gets planck constant: 6.62607015 × 10⁻³⁴ j·s (exact, si defining constant) as type T. + public static T PlanckConstant() where T : struct, INumber => Fundamental.PlanckConstant.To(); +/// Gets radians per degree: π/180 ≈ 0.017453292519943295 as type T. + public static T RadiansPerDegree() where T : struct, INumber => AngularMechanics.RadiansPerDegree.To(); +/// Gets reference sound intensity: 1 × 10⁻¹² w/m² (threshold of hearing) as type T. + public static T ReferenceSoundIntensity() where T : struct, INumber => Acoustics.ReferenceSoundIntensity.To(); +/// Gets reference sound power: 1 × 10⁻¹² w as type T. + public static T ReferenceSoundPower() where T : struct, INumber => Acoustics.ReferenceSoundPower.To(); +/// Gets reference sound pressure: 20 × 10⁻⁶ pa (threshold of hearing) as type T. + public static T ReferenceSoundPressure() where T : struct, INumber => Acoustics.ReferenceSoundPressure.To(); +/// Gets sabine reverberation constant: 0.161 s/m as type T. + public static T SabineConstant() where T : struct, INumber => Acoustics.SabineConstant.To(); +/// Gets speed of light in vacuum: 299,792,458 m/s (exact, si defining constant) as type T. + public static T SpeedOfLight() where T : struct, INumber => Fundamental.SpeedOfLight.To(); +/// Gets standard air density at 15°c and 1 atm: 1.225 kg/m³ (iso 2533) as type T. + public static T StandardAirDensity() where T : struct, INumber => FluidMechanics.StandardAirDensity.To(); +/// Gets standard atmospheric pressure: 101,325 pa (exact by definition) as type T. + public static T StandardAtmosphericPressure() where T : struct, INumber => ClassicalMechanics.StandardAtmosphericPressure.To(); +/// Gets standard gravitational acceleration: 9.80665 m/s² (exact by definition) as type T. + public static T StandardGravity() where T : struct, INumber => ClassicalMechanics.StandardGravity.To(); +/// Gets standard temperature (stp): 273.15 k (0°c) as type T. + public static T StandardTemperature() where T : struct, INumber => Thermodynamics.StandardTemperature.To(); +/// Gets 2π - full rotation in radians: 6.283185307179586 as type T. + public static T TwoPi() where T : struct, INumber => AngularMechanics.TwoPi.To(); +/// Gets water boiling point at 1 atm: 373.15 k (100°c) as type T. + public static T WaterBoilingPoint() where T : struct, INumber => Thermodynamics.WaterBoilingPoint.To(); +/// Gets water ion product (kw) at 25°c: 1.0 × 10⁻¹⁴ (pkw = 14.0) as type T. + public static T WaterIonProduct() where T : struct, INumber => Chemistry.WaterIonProduct.To(); +/// Gets water surface tension at 20°c: 0.0728 n/m (nist) as type T. + public static T WaterSurfaceTension() where T : struct, INumber => FluidMechanics.WaterSurfaceTension.To(); +/// Gets water triple point: 273.16 k (exact by definition) as type T. + public static T WaterTriplePoint() where T : struct, INumber => Thermodynamics.WaterTriplePoint.To(); +}; +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.PrecisionGenerator/StorageTypes.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.PrecisionGenerator/StorageTypes.g.cs new file mode 100644 index 0000000..7080106 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.PrecisionGenerator/StorageTypes.g.cs @@ -0,0 +1,28 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Collections.Generic; + +/// +/// Available storage types for numeric values in the Semantics library. +/// +public static class StorageTypes{ + /// The double storage type. + public static readonly Type DOUBLE = typeof(double); + + /// The float storage type. + public static readonly Type FLOAT = typeof(float); + + /// Gets all available storage types. + public static readonly IReadOnlyList All = new List { DOUBLE, FLOAT }; + + /// Gets the names of all available storage types. + public static readonly IReadOnlyList Names = new List { "double", "float" }; + +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AbsorbedDose.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AbsorbedDose.g.cs new file mode 100644 index 0000000..63e0814 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AbsorbedDose.g.cs @@ -0,0 +1,50 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the AbsorbedDose dimension. +/// +/// The numeric storage type. +public partial record AbsorbedDose : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AbsorbedDose Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AbsorbedDose; + + /// + /// Creates a new from a value in Gray. + /// + /// The value in Gray. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static AbsorbedDose FromGray(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Rad. + /// + /// The value in Rad. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static AbsorbedDose FromRad(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RadToGrays)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AbsorbedDose unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAbsorbedDoseUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two AbsorbedDose values, returning the absolute difference as a non-negative AbsorbedDose. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AbsorbedDose operator -(AbsorbedDose left, AbsorbedDose right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Acceleration1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Acceleration1D.g.cs new file mode 100644 index 0000000..03e8756 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Acceleration1D.g.cs @@ -0,0 +1,56 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the Acceleration dimension. +/// +/// The numeric storage type. +public partial record Acceleration1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Acceleration1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Acceleration; + + /// + /// Creates a new from a value in MeterPerSecondSquared. + /// + /// The value in MeterPerSecondSquared. + /// A new instance. + public static Acceleration1D FromMeterPerSecondSquared(T value) => Create(value); +/// + /// Creates a new from a value in StandardGravity. + /// + /// The value in StandardGravity. + /// A new instance. + public static Acceleration1D FromStandardGravity(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.StandardGravityToMeterPerSecondSquared))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Acceleration unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAccelerationUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public AccelerationMagnitude Magnitude() => AccelerationMagnitude.Create(T.Abs(Value)); +/// + /// Multiplies Acceleration1D by Duration to produce Velocity1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Velocity1D operator *(Acceleration1D left, Duration right) => Multiply>(left, right); +/// + /// Divides Acceleration1D by Duration to produce Jerk1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Jerk1D operator /(Acceleration1D left, Duration right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Acceleration2D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Acceleration2D.g.cs new file mode 100644 index 0000000..6e98707 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Acceleration2D.g.cs @@ -0,0 +1,104 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 2D vector representation of Acceleration. +/// +/// The numeric component type. +public partial record Acceleration2D : IVector2, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets a vector with all components set to zero. + public static Acceleration2D Zero => new() { X = T.Zero, Y = T.Zero }; + + /// Gets a vector with all components set to one. + public static Acceleration2D One => new() { X = T.One, Y = T.One }; + + /// Gets the unit vector for the X-axis. + public static Acceleration2D UnitX => new() { X = T.One, Y = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Acceleration2D UnitY => new() { X = T.Zero, Y = T.One }; + + /// Gets the magnitude as a . + public AccelerationMagnitude Magnitude() => AccelerationMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y); + + /// Calculates the dot product of two vectors. + public T Dot(Acceleration2D other) => (X * other.X) + (Y * other.Y); + + /// Calculates the distance between two vectors. + public T Distance(Acceleration2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T sum = (dX * dX) + (dY * dY); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Acceleration2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + return (dX * dX) + (dY * dY); + } + + /// Returns a normalized version of the vector. + public Acceleration2D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len }; + } + + /// Adds two vectors. + public static Acceleration2D operator +(Acceleration2D left, Acceleration2D right) => new() { X = left.X + right.X, Y = left.Y + right.Y }; + + /// Subtracts two vectors. + public static Acceleration2D operator -(Acceleration2D left, Acceleration2D right) => new() { X = left.X - right.X, Y = left.Y - right.Y }; + + /// Multiplies a vector by a scalar. + public static Acceleration2D operator *(Acceleration2D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar }; + + /// Multiplies a scalar by a vector. + public static Acceleration2D operator *(T scalar, Acceleration2D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y }; + + /// Divides a vector by a scalar. + public static Acceleration2D operator /(Acceleration2D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar }; + + /// Negates a vector. + public static Acceleration2D operator -(Acceleration2D vector) => new() { X = -vector.X, Y = -vector.Y }; + /// Acceleration2D * Duration = Velocity2D. + public static Velocity2D operator *(Acceleration2D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value }; + + /// Acceleration2D / Duration = Jerk2D. + public static Jerk2D operator /(Acceleration2D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Acceleration3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Acceleration3D.g.cs new file mode 100644 index 0000000..ad571da --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Acceleration3D.g.cs @@ -0,0 +1,118 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of Acceleration. +/// +/// The numeric component type. +public partial record Acceleration3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static Acceleration3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static Acceleration3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static Acceleration3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Acceleration3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Acceleration3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public AccelerationMagnitude Magnitude() => AccelerationMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(Acceleration3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public Acceleration3D Cross(Acceleration3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(Acceleration3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Acceleration3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public Acceleration3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static Acceleration3D operator +(Acceleration3D left, Acceleration3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static Acceleration3D operator -(Acceleration3D left, Acceleration3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static Acceleration3D operator *(Acceleration3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static Acceleration3D operator *(T scalar, Acceleration3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static Acceleration3D operator /(Acceleration3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static Acceleration3D operator -(Acceleration3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Acceleration3D * Duration = Velocity3D. + public static Velocity3D operator *(Acceleration3D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value }; + + /// Acceleration3D / Duration = Jerk3D. + public static Jerk3D operator /(Acceleration3D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Acceleration4D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Acceleration4D.g.cs new file mode 100644 index 0000000..de32487 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Acceleration4D.g.cs @@ -0,0 +1,120 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 4D vector representation of Acceleration. +/// +/// The numeric component type. +public partial record Acceleration4D : IVector4, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets the W component. + public T W { get; init; } + + /// Gets a vector with all components set to zero. + public static Acceleration4D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets a vector with all components set to one. + public static Acceleration4D One => new() { X = T.One, Y = T.One, Z = T.One, W = T.One }; + + /// Gets the unit vector for the X-axis. + public static Acceleration4D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Acceleration4D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Acceleration4D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One, W = T.Zero }; + + /// Gets the unit vector for the W-axis. + public static Acceleration4D UnitW => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.One }; + + /// Gets the magnitude as a . + public AccelerationMagnitude Magnitude() => AccelerationMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z) + (W * W); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z) + (W * W); + + /// Calculates the dot product of two vectors. + public T Dot(Acceleration4D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z) + (W * other.W); + + /// Calculates the distance between two vectors. + public T Distance(Acceleration4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Acceleration4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + return (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + } + + /// Returns a normalized version of the vector. + public Acceleration4D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len, W = W / len }; + } + + /// Adds two vectors. + public static Acceleration4D operator +(Acceleration4D left, Acceleration4D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z, W = left.W + right.W }; + + /// Subtracts two vectors. + public static Acceleration4D operator -(Acceleration4D left, Acceleration4D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z, W = left.W - right.W }; + + /// Multiplies a vector by a scalar. + public static Acceleration4D operator *(Acceleration4D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar, W = vector.W * scalar }; + + /// Multiplies a scalar by a vector. + public static Acceleration4D operator *(T scalar, Acceleration4D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z, W = scalar * vector.W }; + + /// Divides a vector by a scalar. + public static Acceleration4D operator /(Acceleration4D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar, W = vector.W / scalar }; + + /// Negates a vector. + public static Acceleration4D operator -(Acceleration4D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z, W = -vector.W }; + /// Acceleration4D * Duration = Velocity4D. + public static Velocity4D operator *(Acceleration4D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value, W = left.W * right.Value }; + + /// Acceleration4D / Duration = Jerk4D. + public static Jerk4D operator /(Acceleration4D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value, W = left.W / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AccelerationMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AccelerationMagnitude.g.cs new file mode 100644 index 0000000..cc85545 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AccelerationMagnitude.g.cs @@ -0,0 +1,66 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Acceleration dimension. +/// +/// The numeric storage type. +public partial record AccelerationMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AccelerationMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Acceleration; + + /// + /// Creates a new from a value in MeterPerSecondSquared. + /// + /// The value in MeterPerSecondSquared. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static AccelerationMagnitude FromMeterPerSecondSquared(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in StandardGravity. + /// + /// The value in StandardGravity. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static AccelerationMagnitude FromStandardGravity(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.StandardGravityToMeterPerSecondSquared)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Acceleration unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAccelerationUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two AccelerationMagnitude values, returning the absolute difference as a non-negative AccelerationMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AccelerationMagnitude operator -(AccelerationMagnitude left, AccelerationMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies AccelerationMagnitude by Mass to produce ForceMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ForceMagnitude operator *(AccelerationMagnitude left, Mass right) => Multiply>(left, right); +/// + /// Multiplies AccelerationMagnitude by Duration to produce Speed. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Speed operator *(AccelerationMagnitude left, Duration right) => Multiply>(left, right); +/// + /// Divides AccelerationMagnitude by Duration to produce JerkMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static JerkMagnitude operator /(AccelerationMagnitude left, Duration right) => Divide>(left, right); +/// + /// Divides AccelerationMagnitude by JerkMagnitude to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(AccelerationMagnitude left, JerkMagnitude right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AcousticImpedance.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AcousticImpedance.g.cs new file mode 100644 index 0000000..4f6ac03 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AcousticImpedance.g.cs @@ -0,0 +1,43 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the AcousticImpedance dimension. +/// +/// The numeric storage type. +public partial record AcousticImpedance : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AcousticImpedance Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AcousticImpedance; + + /// + /// Creates a new from a value in PascalSecondPerMeter. + /// + /// The value in PascalSecondPerMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static AcousticImpedance FromPascalSecondPerMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AcousticImpedance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAcousticImpedanceUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two AcousticImpedance values, returning the absolute difference as a non-negative AcousticImpedance. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AcousticImpedance operator -(AcousticImpedance left, AcousticImpedance right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ActivationEnergy.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ActivationEnergy.g.cs new file mode 100644 index 0000000..711acbd --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ActivationEnergy.g.cs @@ -0,0 +1,61 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Minimum energy for a chemical reaction to occur. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record ActivationEnergy : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ActivationEnergy Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.MolarEnergy; + + /// + /// Creates a new ActivationEnergy from a value in JoulePerMole. + /// + /// The value in JoulePerMole. + /// A new ActivationEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static ActivationEnergy FromJoulePerMole(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new ActivationEnergy from a value in KilojoulePerMole. + /// + /// The value in KilojoulePerMole. + /// A new ActivationEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static ActivationEnergy FromKilojoulePerMole(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilojoulePerMoleToJoulePerMole)), nameof(value))); +/// + /// Creates a new ActivationEnergy from a value in CaloriePerMole. + /// + /// The value in CaloriePerMole. + /// A new ActivationEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static ActivationEnergy FromCaloriePerMole(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CaloriePerMoleToJoulePerMole)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-MolarEnergy unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IMolarEnergyUnit unit) => unit.FromBase(Value); +/// Implicit conversion to MolarEnergy. + public static implicit operator MolarEnergy(ActivationEnergy value) => MolarEnergy.Create(value.Value); +/// Explicit conversion from MolarEnergy. + public static explicit operator ActivationEnergy(MolarEnergy value) => Create(value.Value); +/// Creates a ActivationEnergy from a MolarEnergy value. + public static ActivationEnergy From(MolarEnergy value) => Create(value.Value); +/// Subtracts two ActivationEnergy values, returning the absolute difference as a non-negative ActivationEnergy. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ActivationEnergy operator -(ActivationEnergy left, ActivationEnergy right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Admittance.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Admittance.g.cs new file mode 100644 index 0000000..aeb9eac --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Admittance.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Measure of how easily a circuit allows current flow (inverse of impedance). +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Admittance : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Admittance Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricConductance; + + /// + /// Creates a new Admittance from a value in Siemens. + /// + /// The value in Siemens. + /// A new Admittance instance. + /// Thrown when the resulting magnitude would be negative. + public static Admittance FromSiemens(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricConductance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricConductanceUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Conductance. + public static implicit operator Conductance(Admittance value) => Conductance.Create(value.Value); +/// Explicit conversion from Conductance. + public static explicit operator Admittance(Conductance value) => Create(value.Value); +/// Creates a Admittance from a Conductance value. + public static Admittance From(Conductance value) => Create(value.Value); +/// Subtracts two Admittance values, returning the absolute difference as a non-negative Admittance. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Admittance operator -(Admittance left, Admittance right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Airspeed.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Airspeed.g.cs new file mode 100644 index 0000000..8f8e7e8 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Airspeed.g.cs @@ -0,0 +1,75 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Speed relative to the air. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Airspeed : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Airspeed Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// + /// Creates a new Airspeed from a value in MeterPerSecond. + /// + /// The value in MeterPerSecond. + /// A new Airspeed instance. + /// Thrown when the resulting magnitude would be negative. + public static Airspeed FromMeterPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Airspeed from a value in KilometerPerHour. + /// + /// The value in KilometerPerHour. + /// A new Airspeed instance. + /// Thrown when the resulting magnitude would be negative. + public static Airspeed FromKilometerPerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilometerPerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new Airspeed from a value in MilePerHour. + /// + /// The value in MilePerHour. + /// A new Airspeed instance. + /// Thrown when the resulting magnitude would be negative. + public static Airspeed FromMilePerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MilePerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new Airspeed from a value in FootPerSecond. + /// + /// The value in FootPerSecond. + /// A new Airspeed instance. + /// Thrown when the resulting magnitude would be negative. + public static Airspeed FromFootPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FootPerSecondToMeterPerSecond)), nameof(value))); +/// + /// Creates a new Airspeed from a value in Knot. + /// + /// The value in Knot. + /// A new Airspeed instance. + /// Thrown when the resulting magnitude would be negative. + public static Airspeed FromKnot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KnotToMeterPerSecond)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Velocity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IVelocityUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Speed. + public static implicit operator Speed(Airspeed value) => Speed.Create(value.Value); +/// Explicit conversion from Speed. + public static explicit operator Airspeed(Speed value) => Create(value.Value); +/// Creates a Airspeed from a Speed value. + public static Airspeed From(Speed value) => Create(value.Value); +/// Subtracts two Airspeed values, returning the absolute difference as a non-negative Airspeed. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Airspeed operator -(Airspeed left, Airspeed right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Altitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Altitude.g.cs new file mode 100644 index 0000000..84a8f65 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Altitude.g.cs @@ -0,0 +1,124 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Height above a reference level. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Altitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Altitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Length; + + /// + /// Creates a new Altitude from a value in Meter. + /// + /// The value in Meter. + /// A new Altitude instance. + /// Thrown when the resulting magnitude would be negative. + public static Altitude FromMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Altitude from a value in Kilometer. + /// + /// The value in Kilometer. + /// A new Altitude instance. + /// Thrown when the resulting magnitude would be negative. + public static Altitude FromKilometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Altitude from a value in Centimeter. + /// + /// The value in Centimeter. + /// A new Altitude instance. + /// Thrown when the resulting magnitude would be negative. + public static Altitude FromCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Centi)), nameof(value))); +/// + /// Creates a new Altitude from a value in Millimeter. + /// + /// The value in Millimeter. + /// A new Altitude instance. + /// Thrown when the resulting magnitude would be negative. + public static Altitude FromMillimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Altitude from a value in Micrometer. + /// + /// The value in Micrometer. + /// A new Altitude instance. + /// Thrown when the resulting magnitude would be negative. + public static Altitude FromMicrometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new Altitude from a value in Nanometer. + /// + /// The value in Nanometer. + /// A new Altitude instance. + /// Thrown when the resulting magnitude would be negative. + public static Altitude FromNanometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Creates a new Altitude from a value in Angstrom. + /// + /// The value in Angstrom. + /// A new Altitude instance. + /// Thrown when the resulting magnitude would be negative. + public static Altitude FromAngstrom(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AngstromToMeters)), nameof(value))); +/// + /// Creates a new Altitude from a value in Foot. + /// + /// The value in Foot. + /// A new Altitude instance. + /// Thrown when the resulting magnitude would be negative. + public static Altitude FromFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FeetToMeters)), nameof(value))); +/// + /// Creates a new Altitude from a value in Inch. + /// + /// The value in Inch. + /// A new Altitude instance. + /// Thrown when the resulting magnitude would be negative. + public static Altitude FromInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.InchesToMeters)), nameof(value))); +/// + /// Creates a new Altitude from a value in Yard. + /// + /// The value in Yard. + /// A new Altitude instance. + /// Thrown when the resulting magnitude would be negative. + public static Altitude FromYard(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YardToMeters)), nameof(value))); +/// + /// Creates a new Altitude from a value in Mile. + /// + /// The value in Mile. + /// A new Altitude instance. + /// Thrown when the resulting magnitude would be negative. + public static Altitude FromMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MileToMeters)), nameof(value))); +/// + /// Creates a new Altitude from a value in NauticalMile. + /// + /// The value in NauticalMile. + /// A new Altitude instance. + /// Thrown when the resulting magnitude would be negative. + public static Altitude FromNauticalMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.NauticalMileToMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Length unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILengthUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Length. + public static implicit operator Length(Altitude value) => Length.Create(value.Value); +/// Explicit conversion from Length. + public static explicit operator Altitude(Length value) => Create(value.Value); +/// Creates a Altitude from a Length value. + public static Altitude From(Length value) => Create(value.Value); +/// Subtracts two Altitude values, returning the absolute difference as a non-negative Altitude. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Altitude operator -(Altitude left, Altitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AmountOfSubstance.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AmountOfSubstance.g.cs new file mode 100644 index 0000000..ccc9230 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AmountOfSubstance.g.cs @@ -0,0 +1,81 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the AmountOfSubstance dimension. +/// +/// The numeric storage type. +public partial record AmountOfSubstance : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AmountOfSubstance Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AmountOfSubstance; + + /// + /// Creates a new from a value in Mole. + /// + /// The value in Mole. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static AmountOfSubstance FromMole(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Kilomole. + /// + /// The value in Kilomole. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static AmountOfSubstance FromKilomole(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new from a value in Millimole. + /// + /// The value in Millimole. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static AmountOfSubstance FromMillimole(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AmountOfSubstance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAmountOfSubstanceUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two AmountOfSubstance values, returning the absolute difference as a non-negative AmountOfSubstance. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AmountOfSubstance operator -(AmountOfSubstance left, AmountOfSubstance right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides AmountOfSubstance by Volume to produce Concentration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Concentration operator /(AmountOfSubstance left, Volume right) => Divide>(left, right); +/// + /// Divides AmountOfSubstance by Concentration to produce Volume. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Volume operator /(AmountOfSubstance left, Concentration right) => Divide>(left, right); +/// + /// Multiplies AmountOfSubstance by MolarMass to produce Mass. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Mass operator *(AmountOfSubstance left, MolarMass right) => Multiply>(left, right); +/// + /// Divides AmountOfSubstance by Duration to produce CatalyticActivity. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static CatalyticActivity operator /(AmountOfSubstance left, Duration right) => Divide>(left, right); +/// + /// Divides AmountOfSubstance by CatalyticActivity to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(AmountOfSubstance left, CatalyticActivity right) => Divide>(left, right); +/// + /// Multiplies AmountOfSubstance by MolarEnergy to produce Energy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Energy operator *(AmountOfSubstance left, MolarEnergy right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Angle.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Angle.g.cs new file mode 100644 index 0000000..8732ba6 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Angle.g.cs @@ -0,0 +1,83 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the AngularDisplacement dimension. +/// +/// The numeric storage type. +public partial record Angle : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Angle Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularDisplacement; + + /// + /// Creates a new from a value in Radian. + /// + /// The value in Radian. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Angle FromRadian(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Degree. + /// + /// The value in Degree. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Angle FromDegree(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians)), nameof(value))); +/// + /// Creates a new from a value in Gradian. + /// + /// The value in Gradian. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Angle FromGradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians)), nameof(value))); +/// + /// Creates a new from a value in Revolution. + /// + /// The value in Revolution. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Angle FromRevolution(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians)), nameof(value))); +/// + /// Creates a new from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Angle FromMilliradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularDisplacement unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularDisplacementUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Angle values, returning the absolute difference as a non-negative Angle. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Angle operator -(Angle left, Angle right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides Angle by Duration to produce AngularSpeed. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularSpeed operator /(Angle left, Duration right) => Divide>(left, right); +/// + /// Divides Angle by AngularSpeed to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(Angle left, AngularSpeed right) => Divide>(left, right); +/// + /// Multiplies Angle by TorqueMagnitude to produce Energy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Energy operator *(Angle left, TorqueMagnitude right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularAcceleration1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularAcceleration1D.g.cs new file mode 100644 index 0000000..85b66ce --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularAcceleration1D.g.cs @@ -0,0 +1,50 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the AngularAcceleration dimension. +/// +/// The numeric storage type. +public partial record AngularAcceleration1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AngularAcceleration1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularAcceleration; + + /// + /// Creates a new from a value in RadianPerSecondSquared. + /// + /// The value in RadianPerSecondSquared. + /// A new instance. + public static AngularAcceleration1D FromRadianPerSecondSquared(T value) => Create(value); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularAcceleration unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularAccelerationUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public AngularAccelerationMagnitude Magnitude() => AngularAccelerationMagnitude.Create(T.Abs(Value)); +/// + /// Multiplies AngularAcceleration1D by Duration to produce AngularVelocity1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularVelocity1D operator *(AngularAcceleration1D left, Duration right) => Multiply>(left, right); +/// + /// Divides AngularAcceleration1D by Duration to produce AngularJerk1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularJerk1D operator /(AngularAcceleration1D left, Duration right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularAcceleration3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularAcceleration3D.g.cs new file mode 100644 index 0000000..6325b8f --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularAcceleration3D.g.cs @@ -0,0 +1,118 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of AngularAcceleration. +/// +/// The numeric component type. +public partial record AngularAcceleration3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static AngularAcceleration3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static AngularAcceleration3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static AngularAcceleration3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static AngularAcceleration3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static AngularAcceleration3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public AngularAccelerationMagnitude Magnitude() => AngularAccelerationMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(AngularAcceleration3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public AngularAcceleration3D Cross(AngularAcceleration3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(AngularAcceleration3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(AngularAcceleration3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public AngularAcceleration3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static AngularAcceleration3D operator +(AngularAcceleration3D left, AngularAcceleration3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static AngularAcceleration3D operator -(AngularAcceleration3D left, AngularAcceleration3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static AngularAcceleration3D operator *(AngularAcceleration3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static AngularAcceleration3D operator *(T scalar, AngularAcceleration3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static AngularAcceleration3D operator /(AngularAcceleration3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static AngularAcceleration3D operator -(AngularAcceleration3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// AngularAcceleration3D * Duration = AngularVelocity3D. + public static AngularVelocity3D operator *(AngularAcceleration3D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value }; + + /// AngularAcceleration3D / Duration = AngularJerk3D. + public static AngularJerk3D operator /(AngularAcceleration3D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularAccelerationMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularAccelerationMagnitude.g.cs new file mode 100644 index 0000000..fa6cdf4 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularAccelerationMagnitude.g.cs @@ -0,0 +1,59 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the AngularAcceleration dimension. +/// +/// The numeric storage type. +public partial record AngularAccelerationMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AngularAccelerationMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularAcceleration; + + /// + /// Creates a new from a value in RadianPerSecondSquared. + /// + /// The value in RadianPerSecondSquared. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static AngularAccelerationMagnitude FromRadianPerSecondSquared(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularAcceleration unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularAccelerationUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two AngularAccelerationMagnitude values, returning the absolute difference as a non-negative AngularAccelerationMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularAccelerationMagnitude operator -(AngularAccelerationMagnitude left, AngularAccelerationMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies AngularAccelerationMagnitude by Duration to produce AngularSpeed. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularSpeed operator *(AngularAccelerationMagnitude left, Duration right) => Multiply>(left, right); +/// + /// Divides AngularAccelerationMagnitude by Duration to produce AngularJerkMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularJerkMagnitude operator /(AngularAccelerationMagnitude left, Duration right) => Divide>(left, right); +/// + /// Divides AngularAccelerationMagnitude by AngularJerkMagnitude to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(AngularAccelerationMagnitude left, AngularJerkMagnitude right) => Divide>(left, right); +/// + /// Multiplies AngularAccelerationMagnitude by MomentOfInertia to produce TorqueMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static TorqueMagnitude operator *(AngularAccelerationMagnitude left, MomentOfInertia right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularDisplacement3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularDisplacement3D.g.cs new file mode 100644 index 0000000..fe2d7ae --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularDisplacement3D.g.cs @@ -0,0 +1,115 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of AngularDisplacement. +/// +/// The numeric component type. +public partial record AngularDisplacement3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static AngularDisplacement3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static AngularDisplacement3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static AngularDisplacement3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static AngularDisplacement3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static AngularDisplacement3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public Angle Magnitude() => Angle.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(AngularDisplacement3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public AngularDisplacement3D Cross(AngularDisplacement3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(AngularDisplacement3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(AngularDisplacement3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public AngularDisplacement3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static AngularDisplacement3D operator +(AngularDisplacement3D left, AngularDisplacement3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static AngularDisplacement3D operator -(AngularDisplacement3D left, AngularDisplacement3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static AngularDisplacement3D operator *(AngularDisplacement3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static AngularDisplacement3D operator *(T scalar, AngularDisplacement3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static AngularDisplacement3D operator /(AngularDisplacement3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static AngularDisplacement3D operator -(AngularDisplacement3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// AngularDisplacement3D / Duration = AngularVelocity3D. + public static AngularVelocity3D operator /(AngularDisplacement3D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularJerk1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularJerk1D.g.cs new file mode 100644 index 0000000..a0327fb --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularJerk1D.g.cs @@ -0,0 +1,46 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the AngularJerk dimension. +/// +/// The numeric storage type. +public partial record AngularJerk1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AngularJerk1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularJerk; + + /// + /// Creates a new from a value in RadianPerSecondCubed. + /// + /// The value in RadianPerSecondCubed. + /// A new instance. + public static AngularJerk1D FromRadianPerSecondCubed(T value) => Create(value); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularJerk unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularJerkUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public AngularJerkMagnitude Magnitude() => AngularJerkMagnitude.Create(T.Abs(Value)); +/// + /// Multiplies AngularJerk1D by Duration to produce AngularAcceleration1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularAcceleration1D operator *(AngularJerk1D left, Duration right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularJerk3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularJerk3D.g.cs new file mode 100644 index 0000000..6d039ed --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularJerk3D.g.cs @@ -0,0 +1,115 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of AngularJerk. +/// +/// The numeric component type. +public partial record AngularJerk3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static AngularJerk3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static AngularJerk3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static AngularJerk3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static AngularJerk3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static AngularJerk3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public AngularJerkMagnitude Magnitude() => AngularJerkMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(AngularJerk3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public AngularJerk3D Cross(AngularJerk3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(AngularJerk3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(AngularJerk3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public AngularJerk3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static AngularJerk3D operator +(AngularJerk3D left, AngularJerk3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static AngularJerk3D operator -(AngularJerk3D left, AngularJerk3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static AngularJerk3D operator *(AngularJerk3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static AngularJerk3D operator *(T scalar, AngularJerk3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static AngularJerk3D operator /(AngularJerk3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static AngularJerk3D operator -(AngularJerk3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// AngularJerk3D * Duration = AngularAcceleration3D. + public static AngularAcceleration3D operator *(AngularJerk3D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularJerkMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularJerkMagnitude.g.cs new file mode 100644 index 0000000..049fa6e --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularJerkMagnitude.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the AngularJerk dimension. +/// +/// The numeric storage type. +public partial record AngularJerkMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AngularJerkMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularJerk; + + /// + /// Creates a new from a value in RadianPerSecondCubed. + /// + /// The value in RadianPerSecondCubed. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static AngularJerkMagnitude FromRadianPerSecondCubed(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularJerk unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularJerkUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two AngularJerkMagnitude values, returning the absolute difference as a non-negative AngularJerkMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularJerkMagnitude operator -(AngularJerkMagnitude left, AngularJerkMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies AngularJerkMagnitude by Duration to produce AngularAccelerationMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularAccelerationMagnitude operator *(AngularJerkMagnitude left, Duration right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularMomentum1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularMomentum1D.g.cs new file mode 100644 index 0000000..ad8a591 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularMomentum1D.g.cs @@ -0,0 +1,46 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the AngularMomentum dimension. +/// +/// The numeric storage type. +public partial record AngularMomentum1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AngularMomentum1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularMomentum; + + /// + /// Creates a new from a value in KilogramMeterSquaredPerSecond. + /// + /// The value in KilogramMeterSquaredPerSecond. + /// A new instance. + public static AngularMomentum1D FromKilogramMeterSquaredPerSecond(T value) => Create(value); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularMomentum unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularMomentumUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public AngularMomentumMagnitude Magnitude() => AngularMomentumMagnitude.Create(T.Abs(Value)); +/// + /// Divides AngularMomentum1D by Duration to produce Torque1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Torque1D operator /(AngularMomentum1D left, Duration right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularMomentum3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularMomentum3D.g.cs new file mode 100644 index 0000000..785279c --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularMomentum3D.g.cs @@ -0,0 +1,115 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of AngularMomentum. +/// +/// The numeric component type. +public partial record AngularMomentum3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static AngularMomentum3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static AngularMomentum3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static AngularMomentum3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static AngularMomentum3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static AngularMomentum3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public AngularMomentumMagnitude Magnitude() => AngularMomentumMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(AngularMomentum3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public AngularMomentum3D Cross(AngularMomentum3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(AngularMomentum3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(AngularMomentum3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public AngularMomentum3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static AngularMomentum3D operator +(AngularMomentum3D left, AngularMomentum3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static AngularMomentum3D operator -(AngularMomentum3D left, AngularMomentum3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static AngularMomentum3D operator *(AngularMomentum3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static AngularMomentum3D operator *(T scalar, AngularMomentum3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static AngularMomentum3D operator /(AngularMomentum3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static AngularMomentum3D operator -(AngularMomentum3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// AngularMomentum3D / Duration = Torque3D. + public static Torque3D operator /(AngularMomentum3D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularMomentumMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularMomentumMagnitude.g.cs new file mode 100644 index 0000000..139c8dd --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularMomentumMagnitude.g.cs @@ -0,0 +1,55 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the AngularMomentum dimension. +/// +/// The numeric storage type. +public partial record AngularMomentumMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AngularMomentumMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularMomentum; + + /// + /// Creates a new from a value in KilogramMeterSquaredPerSecond. + /// + /// The value in KilogramMeterSquaredPerSecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static AngularMomentumMagnitude FromKilogramMeterSquaredPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularMomentum unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularMomentumUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two AngularMomentumMagnitude values, returning the absolute difference as a non-negative AngularMomentumMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularMomentumMagnitude operator -(AngularMomentumMagnitude left, AngularMomentumMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides AngularMomentumMagnitude by Duration to produce TorqueMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static TorqueMagnitude operator /(AngularMomentumMagnitude left, Duration right) => Divide>(left, right); +/// + /// Divides AngularMomentumMagnitude by AngularSpeed to produce MomentOfInertia. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MomentOfInertia operator /(AngularMomentumMagnitude left, AngularSpeed right) => Divide>(left, right); +/// + /// Divides AngularMomentumMagnitude by MomentOfInertia to produce AngularSpeed. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularSpeed operator /(AngularMomentumMagnitude left, MomentOfInertia right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularSpeed.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularSpeed.g.cs new file mode 100644 index 0000000..5e81009 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularSpeed.g.cs @@ -0,0 +1,66 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the AngularVelocity dimension. +/// +/// The numeric storage type. +public partial record AngularSpeed : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AngularSpeed Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularVelocity; + + /// + /// Creates a new from a value in RadianPerSecond. + /// + /// The value in RadianPerSecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static AngularSpeed FromRadianPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in RevolutionPerMinute. + /// + /// The value in RevolutionPerMinute. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static AngularSpeed FromRevolutionPerMinute(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RevolutionPerMinuteToRadianPerSecond)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularVelocity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularVelocityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two AngularSpeed values, returning the absolute difference as a non-negative AngularSpeed. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularSpeed operator -(AngularSpeed left, AngularSpeed right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies AngularSpeed by Duration to produce Angle. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Angle operator *(AngularSpeed left, Duration right) => Multiply>(left, right); +/// + /// Divides AngularSpeed by Duration to produce AngularAccelerationMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularAccelerationMagnitude operator /(AngularSpeed left, Duration right) => Divide>(left, right); +/// + /// Divides AngularSpeed by AngularAccelerationMagnitude to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(AngularSpeed left, AngularAccelerationMagnitude right) => Divide>(left, right); +/// + /// Multiplies AngularSpeed by MomentOfInertia to produce AngularMomentumMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularMomentumMagnitude operator *(AngularSpeed left, MomentOfInertia right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularVelocity1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularVelocity1D.g.cs new file mode 100644 index 0000000..77731ac --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularVelocity1D.g.cs @@ -0,0 +1,56 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the AngularVelocity dimension. +/// +/// The numeric storage type. +public partial record AngularVelocity1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AngularVelocity1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularVelocity; + + /// + /// Creates a new from a value in RadianPerSecond. + /// + /// The value in RadianPerSecond. + /// A new instance. + public static AngularVelocity1D FromRadianPerSecond(T value) => Create(value); +/// + /// Creates a new from a value in RevolutionPerMinute. + /// + /// The value in RevolutionPerMinute. + /// A new instance. + public static AngularVelocity1D FromRevolutionPerMinute(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.RevolutionPerMinuteToRadianPerSecond))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularVelocity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularVelocityUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public AngularSpeed Magnitude() => AngularSpeed.Create(T.Abs(Value)); +/// + /// Multiplies AngularVelocity1D by Duration to produce SignedAngle. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SignedAngle operator *(AngularVelocity1D left, Duration right) => Multiply>(left, right); +/// + /// Divides AngularVelocity1D by Duration to produce AngularAcceleration1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularAcceleration1D operator /(AngularVelocity1D left, Duration right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularVelocity3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularVelocity3D.g.cs new file mode 100644 index 0000000..2314223 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AngularVelocity3D.g.cs @@ -0,0 +1,118 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of AngularVelocity. +/// +/// The numeric component type. +public partial record AngularVelocity3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static AngularVelocity3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static AngularVelocity3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static AngularVelocity3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static AngularVelocity3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static AngularVelocity3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public AngularSpeed Magnitude() => AngularSpeed.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(AngularVelocity3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public AngularVelocity3D Cross(AngularVelocity3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(AngularVelocity3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(AngularVelocity3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public AngularVelocity3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static AngularVelocity3D operator +(AngularVelocity3D left, AngularVelocity3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static AngularVelocity3D operator -(AngularVelocity3D left, AngularVelocity3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static AngularVelocity3D operator *(AngularVelocity3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static AngularVelocity3D operator *(T scalar, AngularVelocity3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static AngularVelocity3D operator /(AngularVelocity3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static AngularVelocity3D operator -(AngularVelocity3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// AngularVelocity3D * Duration = AngularDisplacement3D. + public static AngularDisplacement3D operator *(AngularVelocity3D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value }; + + /// AngularVelocity3D / Duration = AngularAcceleration3D. + public static AngularAcceleration3D operator /(AngularVelocity3D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ApertureAngle.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ApertureAngle.g.cs new file mode 100644 index 0000000..dcb9a94 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ApertureAngle.g.cs @@ -0,0 +1,75 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Opening angle of a cone or lens. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record ApertureAngle : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ApertureAngle Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularDisplacement; + + /// + /// Creates a new ApertureAngle from a value in Radian. + /// + /// The value in Radian. + /// A new ApertureAngle instance. + /// Thrown when the resulting magnitude would be negative. + public static ApertureAngle FromRadian(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new ApertureAngle from a value in Degree. + /// + /// The value in Degree. + /// A new ApertureAngle instance. + /// Thrown when the resulting magnitude would be negative. + public static ApertureAngle FromDegree(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians)), nameof(value))); +/// + /// Creates a new ApertureAngle from a value in Gradian. + /// + /// The value in Gradian. + /// A new ApertureAngle instance. + /// Thrown when the resulting magnitude would be negative. + public static ApertureAngle FromGradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians)), nameof(value))); +/// + /// Creates a new ApertureAngle from a value in Revolution. + /// + /// The value in Revolution. + /// A new ApertureAngle instance. + /// Thrown when the resulting magnitude would be negative. + public static ApertureAngle FromRevolution(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians)), nameof(value))); +/// + /// Creates a new ApertureAngle from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new ApertureAngle instance. + /// Thrown when the resulting magnitude would be negative. + public static ApertureAngle FromMilliradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularDisplacement unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularDisplacementUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Angle. + public static implicit operator Angle(ApertureAngle value) => Angle.Create(value.Value); +/// Explicit conversion from Angle. + public static explicit operator ApertureAngle(Angle value) => Create(value.Value); +/// Creates a ApertureAngle from a Angle value. + public static ApertureAngle From(Angle value) => Create(value.Value); +/// Subtracts two ApertureAngle values, returning the absolute difference as a non-negative ApertureAngle. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ApertureAngle operator -(ApertureAngle left, ApertureAngle right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Area.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Area.g.cs new file mode 100644 index 0000000..595b4a0 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Area.g.cs @@ -0,0 +1,124 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Area dimension. +/// +/// The numeric storage type. +public partial record Area : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Area Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Area; + + /// + /// Creates a new from a value in SquareMeter. + /// + /// The value in SquareMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Area FromSquareMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in SquareKilometer. + /// + /// The value in SquareKilometer. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Area FromSquareKilometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareKilometerToSquareMeters)), nameof(value))); +/// + /// Creates a new from a value in SquareCentimeter. + /// + /// The value in SquareCentimeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Area FromSquareCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareCentimeterToSquareMeters)), nameof(value))); +/// + /// Creates a new from a value in SquareFoot. + /// + /// The value in SquareFoot. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Area FromSquareFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareFootToSquareMeters)), nameof(value))); +/// + /// Creates a new from a value in SquareInch. + /// + /// The value in SquareInch. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Area FromSquareInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareInchToSquareMeters)), nameof(value))); +/// + /// Creates a new from a value in SquareMile. + /// + /// The value in SquareMile. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Area FromSquareMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareMileToSquareMeters)), nameof(value))); +/// + /// Creates a new from a value in Hectare. + /// + /// The value in Hectare. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Area FromHectare(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.HectareToSquareMeters)), nameof(value))); +/// + /// Creates a new from a value in Acre. + /// + /// The value in Acre. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Area FromAcre(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AcreToSquareMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Area unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAreaUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Area values, returning the absolute difference as a non-negative Area. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Area operator -(Area left, Area right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides Area by Length to produce Length. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Length operator /(Area left, Length right) => Divide>(left, right); +/// + /// Multiplies Area by Length to produce Volume. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Volume operator *(Area left, Length right) => Multiply>(left, right); +/// + /// Multiplies Area by Pressure to produce ForceMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ForceMagnitude operator *(Area left, Pressure right) => Multiply>(left, right); +/// + /// Multiplies Area by ElectricFieldMagnitude to produce ElectricFlux. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ElectricFlux operator *(Area left, ElectricFieldMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Area by Illuminance to produce LuminousFlux. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static LuminousFlux operator *(Area left, Illuminance right) => Multiply>(left, right); +/// + /// Multiplies Area by MagneticFluxDensityMagnitude to produce MagneticFlux. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MagneticFlux operator *(Area left, MagneticFluxDensityMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Area by Irradiance to produce Power. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Power operator *(Area left, Irradiance right) => Multiply>(left, right); +/// + /// Multiplies Area by Luminance to produce LuminousIntensity. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static LuminousIntensity operator *(Area left, Luminance right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AtmosphericPressure.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AtmosphericPressure.g.cs new file mode 100644 index 0000000..0362763 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AtmosphericPressure.g.cs @@ -0,0 +1,82 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Pressure exerted by the atmosphere. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record AtmosphericPressure : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AtmosphericPressure Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// + /// Creates a new AtmosphericPressure from a value in Pascal. + /// + /// The value in Pascal. + /// A new AtmosphericPressure instance. + /// Thrown when the resulting magnitude would be negative. + public static AtmosphericPressure FromPascal(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new AtmosphericPressure from a value in Kilopascal. + /// + /// The value in Kilopascal. + /// A new AtmosphericPressure instance. + /// Thrown when the resulting magnitude would be negative. + public static AtmosphericPressure FromKilopascal(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new AtmosphericPressure from a value in Bar. + /// + /// The value in Bar. + /// A new AtmosphericPressure instance. + /// Thrown when the resulting magnitude would be negative. + public static AtmosphericPressure FromBar(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BarToPascals)), nameof(value))); +/// + /// Creates a new AtmosphericPressure from a value in Atmosphere. + /// + /// The value in Atmosphere. + /// A new AtmosphericPressure instance. + /// Thrown when the resulting magnitude would be negative. + public static AtmosphericPressure FromAtmosphere(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AtmosphereToPascals)), nameof(value))); +/// + /// Creates a new AtmosphericPressure from a value in Psi. + /// + /// The value in Psi. + /// A new AtmosphericPressure instance. + /// Thrown when the resulting magnitude would be negative. + public static AtmosphericPressure FromPsi(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PsiToPascals)), nameof(value))); +/// + /// Creates a new AtmosphericPressure from a value in Torr. + /// + /// The value in Torr. + /// A new AtmosphericPressure instance. + /// Thrown when the resulting magnitude would be negative. + public static AtmosphericPressure FromTorr(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.TorrToPascals)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Pressure unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IPressureUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Pressure. + public static implicit operator Pressure(AtmosphericPressure value) => Pressure.Create(value.Value); +/// Explicit conversion from Pressure. + public static explicit operator AtmosphericPressure(Pressure value) => Create(value.Value); +/// Creates a AtmosphericPressure from a Pressure value. + public static AtmosphericPressure From(Pressure value) => Create(value.Value); +/// Subtracts two AtmosphericPressure values, returning the absolute difference as a non-negative AtmosphericPressure. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AtmosphericPressure operator -(AtmosphericPressure left, AtmosphericPressure right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AtomicMass.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AtomicMass.g.cs new file mode 100644 index 0000000..dfdd05a --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/AtomicMass.g.cs @@ -0,0 +1,96 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Mass of a single atom or molecule. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record AtomicMass : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static AtomicMass Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Mass; + + /// + /// Creates a new AtomicMass from a value in Kilogram. + /// + /// The value in Kilogram. + /// A new AtomicMass instance. + /// Thrown when the resulting magnitude would be negative. + public static AtomicMass FromKilogram(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new AtomicMass from a value in Gram. + /// + /// The value in Gram. + /// A new AtomicMass instance. + /// Thrown when the resulting magnitude would be negative. + public static AtomicMass FromGram(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new AtomicMass from a value in Ton. + /// + /// The value in Ton. + /// A new AtomicMass instance. + /// Thrown when the resulting magnitude would be negative. + public static AtomicMass FromTon(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.TonToKilograms)), nameof(value))); +/// + /// Creates a new AtomicMass from a value in Pound. + /// + /// The value in Pound. + /// A new AtomicMass instance. + /// Thrown when the resulting magnitude would be negative. + public static AtomicMass FromPound(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PoundToKilograms)), nameof(value))); +/// + /// Creates a new AtomicMass from a value in Ounce. + /// + /// The value in Ounce. + /// A new AtomicMass instance. + /// Thrown when the resulting magnitude would be negative. + public static AtomicMass FromOunce(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.OunceToKilograms)), nameof(value))); +/// + /// Creates a new AtomicMass from a value in Stone. + /// + /// The value in Stone. + /// A new AtomicMass instance. + /// Thrown when the resulting magnitude would be negative. + public static AtomicMass FromStone(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.StoneToKilograms)), nameof(value))); +/// + /// Creates a new AtomicMass from a value in ShortTon. + /// + /// The value in ShortTon. + /// A new AtomicMass instance. + /// Thrown when the resulting magnitude would be negative. + public static AtomicMass FromShortTon(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ShortTonToKilograms)), nameof(value))); +/// + /// Creates a new AtomicMass from a value in AtomicMassUnit. + /// + /// The value in AtomicMassUnit. + /// A new AtomicMass instance. + /// Thrown when the resulting magnitude would be negative. + public static AtomicMass FromAtomicMassUnit(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AtomicMassUnitToKilograms)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Mass unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IMassUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Mass. + public static implicit operator Mass(AtomicMass value) => Mass.Create(value.Value); +/// Explicit conversion from Mass. + public static explicit operator AtomicMass(Mass value) => Create(value.Value); +/// Creates a AtomicMass from a Mass value. + public static AtomicMass From(Mass value) => Create(value.Value); +/// Subtracts two AtomicMass values, returning the absolute difference as a non-negative AtomicMass. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AtomicMass operator -(AtomicMass left, AtomicMass right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Bandwidth.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Bandwidth.g.cs new file mode 100644 index 0000000..71935fb --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Bandwidth.g.cs @@ -0,0 +1,61 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Range of frequencies in a signal. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Bandwidth : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Bandwidth Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Frequency; + + /// + /// Creates a new Bandwidth from a value in Hertz. + /// + /// The value in Hertz. + /// A new Bandwidth instance. + /// Thrown when the resulting magnitude would be negative. + public static Bandwidth FromHertz(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Bandwidth from a value in Kilohertz. + /// + /// The value in Kilohertz. + /// A new Bandwidth instance. + /// Thrown when the resulting magnitude would be negative. + public static Bandwidth FromKilohertz(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Bandwidth from a value in Megahertz. + /// + /// The value in Megahertz. + /// A new Bandwidth instance. + /// Thrown when the resulting magnitude would be negative. + public static Bandwidth FromMegahertz(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Mega)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Frequency unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IFrequencyUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Frequency. + public static implicit operator Frequency(Bandwidth value) => Frequency.Create(value.Value); +/// Explicit conversion from Frequency. + public static explicit operator Bandwidth(Frequency value) => Create(value.Value); +/// Creates a Bandwidth from a Frequency value. + public static Bandwidth From(Frequency value) => Create(value.Value); +/// Subtracts two Bandwidth values, returning the absolute difference as a non-negative Bandwidth. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Bandwidth operator -(Bandwidth left, Bandwidth right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Bearing.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Bearing.g.cs new file mode 100644 index 0000000..dbbdb46 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Bearing.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Direction relative to a reference. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Bearing : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Bearing Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularDisplacement; + + /// + /// Creates a new Bearing from a value in Radian. + /// + /// The value in Radian. + /// A new Bearing instance. + public static Bearing FromRadian(T value) => Create(value); +/// + /// Creates a new Bearing from a value in Degree. + /// + /// The value in Degree. + /// A new Bearing instance. + public static Bearing FromDegree(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians))); +/// + /// Creates a new Bearing from a value in Gradian. + /// + /// The value in Gradian. + /// A new Bearing instance. + public static Bearing FromGradian(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians))); +/// + /// Creates a new Bearing from a value in Revolution. + /// + /// The value in Revolution. + /// A new Bearing instance. + public static Bearing FromRevolution(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians))); +/// + /// Creates a new Bearing from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new Bearing instance. + public static Bearing FromMilliradian(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Milli))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularDisplacement unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularDisplacementUnit unit) => unit.FromBase(Value); +/// Implicit conversion to SignedAngle. + public static implicit operator SignedAngle(Bearing value) => SignedAngle.Create(value.Value); +/// Explicit conversion from SignedAngle. + public static explicit operator Bearing(SignedAngle value) => Create(value.Value); +/// Creates a Bearing from a SignedAngle value. + public static Bearing From(SignedAngle value) => Create(value.Value); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/BulkModulus.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/BulkModulus.g.cs new file mode 100644 index 0000000..e0eabb1 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/BulkModulus.g.cs @@ -0,0 +1,82 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Resistance of a substance to uniform compression. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record BulkModulus : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static BulkModulus Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// + /// Creates a new BulkModulus from a value in Pascal. + /// + /// The value in Pascal. + /// A new BulkModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static BulkModulus FromPascal(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new BulkModulus from a value in Kilopascal. + /// + /// The value in Kilopascal. + /// A new BulkModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static BulkModulus FromKilopascal(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new BulkModulus from a value in Bar. + /// + /// The value in Bar. + /// A new BulkModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static BulkModulus FromBar(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BarToPascals)), nameof(value))); +/// + /// Creates a new BulkModulus from a value in Atmosphere. + /// + /// The value in Atmosphere. + /// A new BulkModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static BulkModulus FromAtmosphere(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AtmosphereToPascals)), nameof(value))); +/// + /// Creates a new BulkModulus from a value in Psi. + /// + /// The value in Psi. + /// A new BulkModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static BulkModulus FromPsi(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PsiToPascals)), nameof(value))); +/// + /// Creates a new BulkModulus from a value in Torr. + /// + /// The value in Torr. + /// A new BulkModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static BulkModulus FromTorr(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.TorrToPascals)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Pressure unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IPressureUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Pressure. + public static implicit operator Pressure(BulkModulus value) => Pressure.Create(value.Value); +/// Explicit conversion from Pressure. + public static explicit operator BulkModulus(Pressure value) => Create(value.Value); +/// Creates a BulkModulus from a Pressure value. + public static BulkModulus From(Pressure value) => Create(value.Value); +/// Subtracts two BulkModulus values, returning the absolute difference as a non-negative BulkModulus. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static BulkModulus operator -(BulkModulus left, BulkModulus right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Capacitance.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Capacitance.g.cs new file mode 100644 index 0000000..b37619c --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Capacitance.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ElectricCapacitance dimension. +/// +/// The numeric storage type. +public partial record Capacitance : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Capacitance Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricCapacitance; + + /// + /// Creates a new from a value in Farad. + /// + /// The value in Farad. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacitance FromFarad(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Microfarad. + /// + /// The value in Microfarad. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacitance FromMicrofarad(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new from a value in Nanofarad. + /// + /// The value in Nanofarad. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacitance FromNanofarad(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Creates a new from a value in Picofarad. + /// + /// The value in Picofarad. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacitance FromPicofarad(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Pico)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricCapacitance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricCapacitanceUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Capacitance values, returning the absolute difference as a non-negative Capacitance. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Capacitance operator -(Capacitance left, Capacitance right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Capacitance by VoltageMagnitude to produce ChargeMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ChargeMagnitude operator *(Capacitance left, VoltageMagnitude right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Capacity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Capacity.g.cs new file mode 100644 index 0000000..a61527c --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Capacity.g.cs @@ -0,0 +1,117 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Maximum volume a container can hold. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Capacity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Capacity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Volume; + + /// + /// Creates a new Capacity from a value in CubicMeter. + /// + /// The value in CubicMeter. + /// A new Capacity instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacity FromCubicMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Capacity from a value in Liter. + /// + /// The value in Liter. + /// A new Capacity instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacity FromLiter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.LiterToCubicMeters)), nameof(value))); +/// + /// Creates a new Capacity from a value in Milliliter. + /// + /// The value in Milliliter. + /// A new Capacity instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacity FromMilliliter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Capacity from a value in CubicCentimeter. + /// + /// The value in CubicCentimeter. + /// A new Capacity instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacity FromCubicCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CubicCentimeterToCubicMeters)), nameof(value))); +/// + /// Creates a new Capacity from a value in CubicFoot. + /// + /// The value in CubicFoot. + /// A new Capacity instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacity FromCubicFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CubicFootToCubicMeters)), nameof(value))); +/// + /// Creates a new Capacity from a value in CubicInch. + /// + /// The value in CubicInch. + /// A new Capacity instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacity FromCubicInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CubicInchToCubicMeters)), nameof(value))); +/// + /// Creates a new Capacity from a value in Gallon. + /// + /// The value in Gallon. + /// A new Capacity instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacity FromGallon(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GallonToCubicMeters)), nameof(value))); +/// + /// Creates a new Capacity from a value in ImperialGallon. + /// + /// The value in ImperialGallon. + /// A new Capacity instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacity FromImperialGallon(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ImperialGallonToCubicMeters)), nameof(value))); +/// + /// Creates a new Capacity from a value in USQuart. + /// + /// The value in USQuart. + /// A new Capacity instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacity FromUSQuart(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.USQuartToCubicMeters)), nameof(value))); +/// + /// Creates a new Capacity from a value in USPint. + /// + /// The value in USPint. + /// A new Capacity instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacity FromUSPint(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.USPintToCubicMeters)), nameof(value))); +/// + /// Creates a new Capacity from a value in USFluidOunce. + /// + /// The value in USFluidOunce. + /// A new Capacity instance. + /// Thrown when the resulting magnitude would be negative. + public static Capacity FromUSFluidOunce(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.USFluidOunceToCubicMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Volume unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IVolumeUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Volume. + public static implicit operator Volume(Capacity value) => Volume.Create(value.Value); +/// Explicit conversion from Volume. + public static explicit operator Capacity(Volume value) => Create(value.Value); +/// Creates a Capacity from a Volume value. + public static Capacity From(Volume value) => Create(value.Value); +/// Subtracts two Capacity values, returning the absolute difference as a non-negative Capacity. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Capacity operator -(Capacity left, Capacity right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/CatalyticActivity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/CatalyticActivity.g.cs new file mode 100644 index 0000000..ded02fe --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/CatalyticActivity.g.cs @@ -0,0 +1,54 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the CatalyticActivity dimension. +/// +/// The numeric storage type. +public partial record CatalyticActivity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static CatalyticActivity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.CatalyticActivity; + + /// + /// Creates a new from a value in Katal. + /// + /// The value in Katal. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static CatalyticActivity FromKatal(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in EnzymeUnit. + /// + /// The value in EnzymeUnit. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static CatalyticActivity FromEnzymeUnit(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.EnzymeUnitToKatals)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-CatalyticActivity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ICatalyticActivityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two CatalyticActivity values, returning the absolute difference as a non-negative CatalyticActivity. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static CatalyticActivity operator -(CatalyticActivity left, CatalyticActivity right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies CatalyticActivity by Duration to produce AmountOfSubstance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AmountOfSubstance operator *(CatalyticActivity left, Duration right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Charge.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Charge.g.cs new file mode 100644 index 0000000..2f5e203 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Charge.g.cs @@ -0,0 +1,52 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the ElectricCharge dimension. +/// +/// The numeric storage type. +public partial record Charge : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Charge Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricCharge; + + /// + /// Creates a new from a value in Coulomb. + /// + /// The value in Coulomb. + /// A new instance. + public static Charge FromCoulomb(T value) => Create(value); +/// + /// Creates a new from a value in AmpereHour. + /// + /// The value in AmpereHour. + /// A new instance. + public static Charge FromAmpereHour(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.AmpereHourToCoulombs))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricCharge unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricChargeUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public ChargeMagnitude Magnitude() => ChargeMagnitude.Create(T.Abs(Value)); +/// + /// Divides Charge by Duration to produce Current1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Current1D operator /(Charge left, Duration right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ChargeMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ChargeMagnitude.g.cs new file mode 100644 index 0000000..baf923a --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ChargeMagnitude.g.cs @@ -0,0 +1,66 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ElectricCharge dimension. +/// +/// The numeric storage type. +public partial record ChargeMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ChargeMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricCharge; + + /// + /// Creates a new from a value in Coulomb. + /// + /// The value in Coulomb. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ChargeMagnitude FromCoulomb(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in AmpereHour. + /// + /// The value in AmpereHour. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ChargeMagnitude FromAmpereHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AmpereHourToCoulombs)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricCharge unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricChargeUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two ChargeMagnitude values, returning the absolute difference as a non-negative ChargeMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ChargeMagnitude operator -(ChargeMagnitude left, ChargeMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides ChargeMagnitude by Duration to produce CurrentMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static CurrentMagnitude operator /(ChargeMagnitude left, Duration right) => Divide>(left, right); +/// + /// Divides ChargeMagnitude by CurrentMagnitude to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(ChargeMagnitude left, CurrentMagnitude right) => Divide>(left, right); +/// + /// Divides ChargeMagnitude by VoltageMagnitude to produce Capacitance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Capacitance operator /(ChargeMagnitude left, VoltageMagnitude right) => Divide>(left, right); +/// + /// Divides ChargeMagnitude by Capacitance to produce VoltageMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VoltageMagnitude operator /(ChargeMagnitude left, Capacitance right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ClockSpeed.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ClockSpeed.g.cs new file mode 100644 index 0000000..514e8ae --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ClockSpeed.g.cs @@ -0,0 +1,61 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Operating frequency of a processor. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record ClockSpeed : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ClockSpeed Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Frequency; + + /// + /// Creates a new ClockSpeed from a value in Hertz. + /// + /// The value in Hertz. + /// A new ClockSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static ClockSpeed FromHertz(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new ClockSpeed from a value in Kilohertz. + /// + /// The value in Kilohertz. + /// A new ClockSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static ClockSpeed FromKilohertz(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new ClockSpeed from a value in Megahertz. + /// + /// The value in Megahertz. + /// A new ClockSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static ClockSpeed FromMegahertz(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Mega)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Frequency unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IFrequencyUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Frequency. + public static implicit operator Frequency(ClockSpeed value) => Frequency.Create(value.Value); +/// Explicit conversion from Frequency. + public static explicit operator ClockSpeed(Frequency value) => Create(value.Value); +/// Creates a ClockSpeed from a Frequency value. + public static ClockSpeed From(Frequency value) => Create(value.Value); +/// Subtracts two ClockSpeed values, returning the absolute difference as a non-negative ClockSpeed. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ClockSpeed operator -(ClockSpeed left, ClockSpeed right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Concentration.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Concentration.g.cs new file mode 100644 index 0000000..ab9557c --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Concentration.g.cs @@ -0,0 +1,76 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Concentration dimension. +/// +/// The numeric storage type. +public partial record Concentration : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Concentration Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Concentration; + + /// + /// Creates a new from a value in MolePerCubicMeter. + /// + /// The value in MolePerCubicMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Concentration FromMolePerCubicMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Molar. + /// + /// The value in Molar. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Concentration FromMolar(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MolarToCubicMeter)), nameof(value))); +/// + /// Creates a new from a value in Millimolar. + /// + /// The value in Millimolar. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Concentration FromMillimolar(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MillimolarToMolePerCubicMeter)), nameof(value))); +/// + /// Creates a new from a value in Micromolar. + /// + /// The value in Micromolar. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Concentration FromMicromolar(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MicromolarToMolePerCubicMeter)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Concentration unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IConcentrationUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Concentration values, returning the absolute difference as a non-negative Concentration. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Concentration operator -(Concentration left, Concentration right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Concentration by Volume to produce AmountOfSubstance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AmountOfSubstance operator *(Concentration left, Volume right) => Multiply>(left, right); +/// + /// Divides Concentration by Duration to produce ReactionRate. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ReactionRate operator /(Concentration left, Duration right) => Divide>(left, right); +/// + /// Divides Concentration by ReactionRate to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(Concentration left, ReactionRate right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Conductance.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Conductance.g.cs new file mode 100644 index 0000000..1a32d56 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Conductance.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ElectricConductance dimension. +/// +/// The numeric storage type. +public partial record Conductance : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Conductance Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricConductance; + + /// + /// Creates a new from a value in Siemens. + /// + /// The value in Siemens. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Conductance FromSiemens(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricConductance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricConductanceUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Conductance values, returning the absolute difference as a non-negative Conductance. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Conductance operator -(Conductance left, Conductance right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Conductance by VoltageMagnitude to produce CurrentMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static CurrentMagnitude operator *(Conductance left, VoltageMagnitude right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/CrossSectionalArea.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/CrossSectionalArea.g.cs new file mode 100644 index 0000000..260285e --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/CrossSectionalArea.g.cs @@ -0,0 +1,96 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Area of a cross-section perpendicular to an axis. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record CrossSectionalArea : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static CrossSectionalArea Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Area; + + /// + /// Creates a new CrossSectionalArea from a value in SquareMeter. + /// + /// The value in SquareMeter. + /// A new CrossSectionalArea instance. + /// Thrown when the resulting magnitude would be negative. + public static CrossSectionalArea FromSquareMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new CrossSectionalArea from a value in SquareKilometer. + /// + /// The value in SquareKilometer. + /// A new CrossSectionalArea instance. + /// Thrown when the resulting magnitude would be negative. + public static CrossSectionalArea FromSquareKilometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareKilometerToSquareMeters)), nameof(value))); +/// + /// Creates a new CrossSectionalArea from a value in SquareCentimeter. + /// + /// The value in SquareCentimeter. + /// A new CrossSectionalArea instance. + /// Thrown when the resulting magnitude would be negative. + public static CrossSectionalArea FromSquareCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareCentimeterToSquareMeters)), nameof(value))); +/// + /// Creates a new CrossSectionalArea from a value in SquareFoot. + /// + /// The value in SquareFoot. + /// A new CrossSectionalArea instance. + /// Thrown when the resulting magnitude would be negative. + public static CrossSectionalArea FromSquareFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareFootToSquareMeters)), nameof(value))); +/// + /// Creates a new CrossSectionalArea from a value in SquareInch. + /// + /// The value in SquareInch. + /// A new CrossSectionalArea instance. + /// Thrown when the resulting magnitude would be negative. + public static CrossSectionalArea FromSquareInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareInchToSquareMeters)), nameof(value))); +/// + /// Creates a new CrossSectionalArea from a value in SquareMile. + /// + /// The value in SquareMile. + /// A new CrossSectionalArea instance. + /// Thrown when the resulting magnitude would be negative. + public static CrossSectionalArea FromSquareMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareMileToSquareMeters)), nameof(value))); +/// + /// Creates a new CrossSectionalArea from a value in Hectare. + /// + /// The value in Hectare. + /// A new CrossSectionalArea instance. + /// Thrown when the resulting magnitude would be negative. + public static CrossSectionalArea FromHectare(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.HectareToSquareMeters)), nameof(value))); +/// + /// Creates a new CrossSectionalArea from a value in Acre. + /// + /// The value in Acre. + /// A new CrossSectionalArea instance. + /// Thrown when the resulting magnitude would be negative. + public static CrossSectionalArea FromAcre(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AcreToSquareMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Area unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAreaUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Area. + public static implicit operator Area(CrossSectionalArea value) => Area.Create(value.Value); +/// Explicit conversion from Area. + public static explicit operator CrossSectionalArea(Area value) => Create(value.Value); +/// Creates a CrossSectionalArea from a Area value. + public static CrossSectionalArea From(Area value) => Create(value.Value); +/// Subtracts two CrossSectionalArea values, returning the absolute difference as a non-negative CrossSectionalArea. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static CrossSectionalArea operator -(CrossSectionalArea left, CrossSectionalArea right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Current1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Current1D.g.cs new file mode 100644 index 0000000..5e24e1f --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Current1D.g.cs @@ -0,0 +1,62 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the ElectricCurrent dimension. +/// +/// The numeric storage type. +public partial record Current1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Current1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricCurrent; + + /// + /// Creates a new from a value in Ampere. + /// + /// The value in Ampere. + /// A new instance. + public static Current1D FromAmpere(T value) => Create(value); +/// + /// Creates a new from a value in Milliampere. + /// + /// The value in Milliampere. + /// A new instance. + public static Current1D FromMilliampere(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Milli))); +/// + /// Creates a new from a value in Kiloampere. + /// + /// The value in Kiloampere. + /// A new instance. + public static Current1D FromKiloampere(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Kilo))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricCurrent unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricCurrentUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public CurrentMagnitude Magnitude() => CurrentMagnitude.Create(T.Abs(Value)); +/// + /// Multiplies Current1D by Duration to produce Charge. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Charge operator *(Current1D left, Duration right) => Multiply>(left, right); +/// + /// Multiplies Current1D by Resistance to produce Voltage. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Voltage operator *(Current1D left, Resistance right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Current3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Current3D.g.cs new file mode 100644 index 0000000..fc8f7e4 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Current3D.g.cs @@ -0,0 +1,112 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of ElectricCurrent. +/// +/// The numeric component type. +public partial record Current3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static Current3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static Current3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static Current3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Current3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Current3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public CurrentMagnitude Magnitude() => CurrentMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(Current3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public Current3D Cross(Current3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(Current3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Current3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public Current3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static Current3D operator +(Current3D left, Current3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static Current3D operator -(Current3D left, Current3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static Current3D operator *(Current3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static Current3D operator *(T scalar, Current3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static Current3D operator /(Current3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static Current3D operator -(Current3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/CurrentMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/CurrentMagnitude.g.cs new file mode 100644 index 0000000..1790fd8 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/CurrentMagnitude.g.cs @@ -0,0 +1,81 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ElectricCurrent dimension. +/// +/// The numeric storage type. +public partial record CurrentMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static CurrentMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricCurrent; + + /// + /// Creates a new from a value in Ampere. + /// + /// The value in Ampere. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static CurrentMagnitude FromAmpere(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Milliampere. + /// + /// The value in Milliampere. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static CurrentMagnitude FromMilliampere(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new from a value in Kiloampere. + /// + /// The value in Kiloampere. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static CurrentMagnitude FromKiloampere(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricCurrent unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricCurrentUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two CurrentMagnitude values, returning the absolute difference as a non-negative CurrentMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static CurrentMagnitude operator -(CurrentMagnitude left, CurrentMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies CurrentMagnitude by Duration to produce ChargeMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ChargeMagnitude operator *(CurrentMagnitude left, Duration right) => Multiply>(left, right); +/// + /// Multiplies CurrentMagnitude by Resistance to produce VoltageMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VoltageMagnitude operator *(CurrentMagnitude left, Resistance right) => Multiply>(left, right); +/// + /// Multiplies CurrentMagnitude by VoltageMagnitude to produce Power. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Power operator *(CurrentMagnitude left, VoltageMagnitude right) => Multiply>(left, right); +/// + /// Divides CurrentMagnitude by VoltageMagnitude to produce Conductance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Conductance operator /(CurrentMagnitude left, VoltageMagnitude right) => Divide>(left, right); +/// + /// Divides CurrentMagnitude by Conductance to produce VoltageMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VoltageMagnitude operator /(CurrentMagnitude left, Conductance right) => Divide>(left, right); +/// + /// Multiplies CurrentMagnitude by Inductance to produce MagneticFlux. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MagneticFlux operator *(CurrentMagnitude left, Inductance right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/DecayTime.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/DecayTime.g.cs new file mode 100644 index 0000000..afff1b1 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/DecayTime.g.cs @@ -0,0 +1,103 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Time for exponential decay to a specific fraction. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record DecayTime : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static DecayTime Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Time; + + /// + /// Creates a new DecayTime from a value in Second. + /// + /// The value in Second. + /// A new DecayTime instance. + /// Thrown when the resulting magnitude would be negative. + public static DecayTime FromSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new DecayTime from a value in Millisecond. + /// + /// The value in Millisecond. + /// A new DecayTime instance. + /// Thrown when the resulting magnitude would be negative. + public static DecayTime FromMillisecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new DecayTime from a value in Microsecond. + /// + /// The value in Microsecond. + /// A new DecayTime instance. + /// Thrown when the resulting magnitude would be negative. + public static DecayTime FromMicrosecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new DecayTime from a value in Minute. + /// + /// The value in Minute. + /// A new DecayTime instance. + /// Thrown when the resulting magnitude would be negative. + public static DecayTime FromMinute(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MinuteToSeconds)), nameof(value))); +/// + /// Creates a new DecayTime from a value in Hour. + /// + /// The value in Hour. + /// A new DecayTime instance. + /// Thrown when the resulting magnitude would be negative. + public static DecayTime FromHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.HourToSeconds)), nameof(value))); +/// + /// Creates a new DecayTime from a value in Day. + /// + /// The value in Day. + /// A new DecayTime instance. + /// Thrown when the resulting magnitude would be negative. + public static DecayTime FromDay(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DayToSeconds)), nameof(value))); +/// + /// Creates a new DecayTime from a value in Year. + /// + /// The value in Year. + /// A new DecayTime instance. + /// Thrown when the resulting magnitude would be negative. + public static DecayTime FromYear(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YearToSeconds)), nameof(value))); +/// + /// Creates a new DecayTime from a value in Week. + /// + /// The value in Week. + /// A new DecayTime instance. + /// Thrown when the resulting magnitude would be negative. + public static DecayTime FromWeek(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.WeekToSeconds)), nameof(value))); +/// + /// Creates a new DecayTime from a value in Nanosecond. + /// + /// The value in Nanosecond. + /// A new DecayTime instance. + /// Thrown when the resulting magnitude would be negative. + public static DecayTime FromNanosecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Time unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ITimeUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Duration. + public static implicit operator Duration(DecayTime value) => Duration.Create(value.Value); +/// Explicit conversion from Duration. + public static explicit operator DecayTime(Duration value) => Create(value.Value); +/// Creates a DecayTime from a Duration value. + public static DecayTime From(Duration value) => Create(value.Value); +/// Subtracts two DecayTime values, returning the absolute difference as a non-negative DecayTime. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static DecayTime operator -(DecayTime left, DecayTime right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Density.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Density.g.cs new file mode 100644 index 0000000..df6c7c4 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Density.g.cs @@ -0,0 +1,65 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Density dimension. +/// +/// The numeric storage type. +public partial record Density : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Density Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Density; + + /// + /// Creates a new from a value in KilogramPerCubicMeter. + /// + /// The value in KilogramPerCubicMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Density FromKilogramPerCubicMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in GramPerCubicCentimeter. + /// + /// The value in GramPerCubicCentimeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Density FromGramPerCubicCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GramPerCubicCentimeterToKilogramPerCubicMeter)), nameof(value))); +/// + /// Creates a new from a value in GramPerLiter. + /// + /// The value in GramPerLiter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Density FromGramPerLiter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GramPerLiterToKilogramPerCubicMeter)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Density unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IDensityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Density values, returning the absolute difference as a non-negative Density. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Density operator -(Density left, Density right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Density by Volume to produce Mass. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Mass operator *(Density left, Volume right) => Multiply>(left, right); +/// + /// Multiplies Density by KinematicViscosity to produce DynamicViscosity. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static DynamicViscosity operator *(Density left, KinematicViscosity right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Depth.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Depth.g.cs new file mode 100644 index 0000000..852b52c --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Depth.g.cs @@ -0,0 +1,124 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Extent into a surface or volume. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Depth : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Depth Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Length; + + /// + /// Creates a new Depth from a value in Meter. + /// + /// The value in Meter. + /// A new Depth instance. + /// Thrown when the resulting magnitude would be negative. + public static Depth FromMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Depth from a value in Kilometer. + /// + /// The value in Kilometer. + /// A new Depth instance. + /// Thrown when the resulting magnitude would be negative. + public static Depth FromKilometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Depth from a value in Centimeter. + /// + /// The value in Centimeter. + /// A new Depth instance. + /// Thrown when the resulting magnitude would be negative. + public static Depth FromCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Centi)), nameof(value))); +/// + /// Creates a new Depth from a value in Millimeter. + /// + /// The value in Millimeter. + /// A new Depth instance. + /// Thrown when the resulting magnitude would be negative. + public static Depth FromMillimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Depth from a value in Micrometer. + /// + /// The value in Micrometer. + /// A new Depth instance. + /// Thrown when the resulting magnitude would be negative. + public static Depth FromMicrometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new Depth from a value in Nanometer. + /// + /// The value in Nanometer. + /// A new Depth instance. + /// Thrown when the resulting magnitude would be negative. + public static Depth FromNanometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Creates a new Depth from a value in Angstrom. + /// + /// The value in Angstrom. + /// A new Depth instance. + /// Thrown when the resulting magnitude would be negative. + public static Depth FromAngstrom(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AngstromToMeters)), nameof(value))); +/// + /// Creates a new Depth from a value in Foot. + /// + /// The value in Foot. + /// A new Depth instance. + /// Thrown when the resulting magnitude would be negative. + public static Depth FromFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FeetToMeters)), nameof(value))); +/// + /// Creates a new Depth from a value in Inch. + /// + /// The value in Inch. + /// A new Depth instance. + /// Thrown when the resulting magnitude would be negative. + public static Depth FromInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.InchesToMeters)), nameof(value))); +/// + /// Creates a new Depth from a value in Yard. + /// + /// The value in Yard. + /// A new Depth instance. + /// Thrown when the resulting magnitude would be negative. + public static Depth FromYard(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YardToMeters)), nameof(value))); +/// + /// Creates a new Depth from a value in Mile. + /// + /// The value in Mile. + /// A new Depth instance. + /// Thrown when the resulting magnitude would be negative. + public static Depth FromMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MileToMeters)), nameof(value))); +/// + /// Creates a new Depth from a value in NauticalMile. + /// + /// The value in NauticalMile. + /// A new Depth instance. + /// Thrown when the resulting magnitude would be negative. + public static Depth FromNauticalMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.NauticalMileToMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Length unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILengthUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Length. + public static implicit operator Length(Depth value) => Length.Create(value.Value); +/// Explicit conversion from Length. + public static explicit operator Depth(Length value) => Create(value.Value); +/// Creates a Depth from a Length value. + public static Depth From(Length value) => Create(value.Value); +/// Subtracts two Depth values, returning the absolute difference as a non-negative Depth. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Depth operator -(Depth left, Depth right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Diameter.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Diameter.g.cs new file mode 100644 index 0000000..745afa3 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Diameter.g.cs @@ -0,0 +1,128 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Distance across a circle or sphere through its center. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Diameter : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Diameter Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Length; + + /// + /// Creates a new Diameter from a value in Meter. + /// + /// The value in Meter. + /// A new Diameter instance. + /// Thrown when the resulting magnitude would be negative. + public static Diameter FromMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Diameter from a value in Kilometer. + /// + /// The value in Kilometer. + /// A new Diameter instance. + /// Thrown when the resulting magnitude would be negative. + public static Diameter FromKilometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Diameter from a value in Centimeter. + /// + /// The value in Centimeter. + /// A new Diameter instance. + /// Thrown when the resulting magnitude would be negative. + public static Diameter FromCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Centi)), nameof(value))); +/// + /// Creates a new Diameter from a value in Millimeter. + /// + /// The value in Millimeter. + /// A new Diameter instance. + /// Thrown when the resulting magnitude would be negative. + public static Diameter FromMillimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Diameter from a value in Micrometer. + /// + /// The value in Micrometer. + /// A new Diameter instance. + /// Thrown when the resulting magnitude would be negative. + public static Diameter FromMicrometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new Diameter from a value in Nanometer. + /// + /// The value in Nanometer. + /// A new Diameter instance. + /// Thrown when the resulting magnitude would be negative. + public static Diameter FromNanometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Creates a new Diameter from a value in Angstrom. + /// + /// The value in Angstrom. + /// A new Diameter instance. + /// Thrown when the resulting magnitude would be negative. + public static Diameter FromAngstrom(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AngstromToMeters)), nameof(value))); +/// + /// Creates a new Diameter from a value in Foot. + /// + /// The value in Foot. + /// A new Diameter instance. + /// Thrown when the resulting magnitude would be negative. + public static Diameter FromFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FeetToMeters)), nameof(value))); +/// + /// Creates a new Diameter from a value in Inch. + /// + /// The value in Inch. + /// A new Diameter instance. + /// Thrown when the resulting magnitude would be negative. + public static Diameter FromInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.InchesToMeters)), nameof(value))); +/// + /// Creates a new Diameter from a value in Yard. + /// + /// The value in Yard. + /// A new Diameter instance. + /// Thrown when the resulting magnitude would be negative. + public static Diameter FromYard(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YardToMeters)), nameof(value))); +/// + /// Creates a new Diameter from a value in Mile. + /// + /// The value in Mile. + /// A new Diameter instance. + /// Thrown when the resulting magnitude would be negative. + public static Diameter FromMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MileToMeters)), nameof(value))); +/// + /// Creates a new Diameter from a value in NauticalMile. + /// + /// The value in NauticalMile. + /// A new Diameter instance. + /// Thrown when the resulting magnitude would be negative. + public static Diameter FromNauticalMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.NauticalMileToMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Length unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILengthUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Length. + public static implicit operator Length(Diameter value) => Length.Create(value.Value); +/// Explicit conversion from Length. + public static explicit operator Diameter(Length value) => Create(value.Value); +/// Creates a Diameter from a Length value. + public static Diameter From(Length value) => Create(value.Value); +/// Subtracts two Diameter values, returning the absolute difference as a non-negative Diameter. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Diameter operator -(Diameter left, Diameter right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// Converts this Diameter to a Radius. + public Radius ToRadius() => Radius.Create(Value / T.CreateChecked(2)); +/// Creates a Diameter from a Radius value. + public static Diameter FromRadius(Radius source) => Create(source.Value * T.CreateChecked(2)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Displacement1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Displacement1D.g.cs new file mode 100644 index 0000000..311896f --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Displacement1D.g.cs @@ -0,0 +1,112 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the Length dimension. +/// +/// The numeric storage type. +public partial record Displacement1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Displacement1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Length; + + /// + /// Creates a new from a value in Meter. + /// + /// The value in Meter. + /// A new instance. + public static Displacement1D FromMeter(T value) => Create(value); +/// + /// Creates a new from a value in Kilometer. + /// + /// The value in Kilometer. + /// A new instance. + public static Displacement1D FromKilometer(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Kilo))); +/// + /// Creates a new from a value in Centimeter. + /// + /// The value in Centimeter. + /// A new instance. + public static Displacement1D FromCentimeter(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Centi))); +/// + /// Creates a new from a value in Millimeter. + /// + /// The value in Millimeter. + /// A new instance. + public static Displacement1D FromMillimeter(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Milli))); +/// + /// Creates a new from a value in Micrometer. + /// + /// The value in Micrometer. + /// A new instance. + public static Displacement1D FromMicrometer(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Micro))); +/// + /// Creates a new from a value in Nanometer. + /// + /// The value in Nanometer. + /// A new instance. + public static Displacement1D FromNanometer(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Nano))); +/// + /// Creates a new from a value in Angstrom. + /// + /// The value in Angstrom. + /// A new instance. + public static Displacement1D FromAngstrom(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.AngstromToMeters))); +/// + /// Creates a new from a value in Foot. + /// + /// The value in Foot. + /// A new instance. + public static Displacement1D FromFoot(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.FeetToMeters))); +/// + /// Creates a new from a value in Inch. + /// + /// The value in Inch. + /// A new instance. + public static Displacement1D FromInch(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.InchesToMeters))); +/// + /// Creates a new from a value in Yard. + /// + /// The value in Yard. + /// A new instance. + public static Displacement1D FromYard(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.YardToMeters))); +/// + /// Creates a new from a value in Mile. + /// + /// The value in Mile. + /// A new instance. + public static Displacement1D FromMile(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.MileToMeters))); +/// + /// Creates a new from a value in NauticalMile. + /// + /// The value in NauticalMile. + /// A new instance. + public static Displacement1D FromNauticalMile(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.NauticalMileToMeters))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Length unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILengthUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public Length Magnitude() => Length.Create(T.Abs(Value)); +/// + /// Divides Displacement1D by Duration to produce Velocity1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Velocity1D operator /(Displacement1D left, Duration right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Displacement2D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Displacement2D.g.cs new file mode 100644 index 0000000..9c4de42 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Displacement2D.g.cs @@ -0,0 +1,101 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 2D vector representation of Length. +/// +/// The numeric component type. +public partial record Displacement2D : IVector2, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets a vector with all components set to zero. + public static Displacement2D Zero => new() { X = T.Zero, Y = T.Zero }; + + /// Gets a vector with all components set to one. + public static Displacement2D One => new() { X = T.One, Y = T.One }; + + /// Gets the unit vector for the X-axis. + public static Displacement2D UnitX => new() { X = T.One, Y = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Displacement2D UnitY => new() { X = T.Zero, Y = T.One }; + + /// Gets the magnitude as a . + public Length Magnitude() => Length.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y); + + /// Calculates the dot product of two vectors. + public T Dot(Displacement2D other) => (X * other.X) + (Y * other.Y); + + /// Calculates the distance between two vectors. + public T Distance(Displacement2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T sum = (dX * dX) + (dY * dY); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Displacement2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + return (dX * dX) + (dY * dY); + } + + /// Returns a normalized version of the vector. + public Displacement2D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len }; + } + + /// Adds two vectors. + public static Displacement2D operator +(Displacement2D left, Displacement2D right) => new() { X = left.X + right.X, Y = left.Y + right.Y }; + + /// Subtracts two vectors. + public static Displacement2D operator -(Displacement2D left, Displacement2D right) => new() { X = left.X - right.X, Y = left.Y - right.Y }; + + /// Multiplies a vector by a scalar. + public static Displacement2D operator *(Displacement2D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar }; + + /// Multiplies a scalar by a vector. + public static Displacement2D operator *(T scalar, Displacement2D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y }; + + /// Divides a vector by a scalar. + public static Displacement2D operator /(Displacement2D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar }; + + /// Negates a vector. + public static Displacement2D operator -(Displacement2D vector) => new() { X = -vector.X, Y = -vector.Y }; + /// Displacement2D / Duration = Velocity2D. + public static Velocity2D operator /(Displacement2D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Displacement3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Displacement3D.g.cs new file mode 100644 index 0000000..46eb512 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Displacement3D.g.cs @@ -0,0 +1,115 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of Length. +/// +/// The numeric component type. +public partial record Displacement3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static Displacement3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static Displacement3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static Displacement3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Displacement3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Displacement3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public Length Magnitude() => Length.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(Displacement3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public Displacement3D Cross(Displacement3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(Displacement3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Displacement3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public Displacement3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static Displacement3D operator +(Displacement3D left, Displacement3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static Displacement3D operator -(Displacement3D left, Displacement3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static Displacement3D operator *(Displacement3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static Displacement3D operator *(T scalar, Displacement3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static Displacement3D operator /(Displacement3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static Displacement3D operator -(Displacement3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Displacement3D / Duration = Velocity3D. + public static Velocity3D operator /(Displacement3D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Displacement4D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Displacement4D.g.cs new file mode 100644 index 0000000..006b756 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Displacement4D.g.cs @@ -0,0 +1,117 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 4D vector representation of Length. +/// +/// The numeric component type. +public partial record Displacement4D : IVector4, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets the W component. + public T W { get; init; } + + /// Gets a vector with all components set to zero. + public static Displacement4D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets a vector with all components set to one. + public static Displacement4D One => new() { X = T.One, Y = T.One, Z = T.One, W = T.One }; + + /// Gets the unit vector for the X-axis. + public static Displacement4D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Displacement4D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Displacement4D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One, W = T.Zero }; + + /// Gets the unit vector for the W-axis. + public static Displacement4D UnitW => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.One }; + + /// Gets the magnitude as a . + public Length Magnitude() => Length.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z) + (W * W); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z) + (W * W); + + /// Calculates the dot product of two vectors. + public T Dot(Displacement4D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z) + (W * other.W); + + /// Calculates the distance between two vectors. + public T Distance(Displacement4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Displacement4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + return (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + } + + /// Returns a normalized version of the vector. + public Displacement4D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len, W = W / len }; + } + + /// Adds two vectors. + public static Displacement4D operator +(Displacement4D left, Displacement4D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z, W = left.W + right.W }; + + /// Subtracts two vectors. + public static Displacement4D operator -(Displacement4D left, Displacement4D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z, W = left.W - right.W }; + + /// Multiplies a vector by a scalar. + public static Displacement4D operator *(Displacement4D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar, W = vector.W * scalar }; + + /// Multiplies a scalar by a vector. + public static Displacement4D operator *(T scalar, Displacement4D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z, W = scalar * vector.W }; + + /// Divides a vector by a scalar. + public static Displacement4D operator /(Displacement4D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar, W = vector.W / scalar }; + + /// Negates a vector. + public static Displacement4D operator -(Displacement4D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z, W = -vector.W }; + /// Displacement4D / Duration = Velocity4D. + public static Velocity4D operator /(Displacement4D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value, W = left.W / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Distance.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Distance.g.cs new file mode 100644 index 0000000..8913ced --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Distance.g.cs @@ -0,0 +1,124 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Separation between two points in space. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Distance : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Distance Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Length; + + /// + /// Creates a new Distance from a value in Meter. + /// + /// The value in Meter. + /// A new Distance instance. + /// Thrown when the resulting magnitude would be negative. + public static Distance FromMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Distance from a value in Kilometer. + /// + /// The value in Kilometer. + /// A new Distance instance. + /// Thrown when the resulting magnitude would be negative. + public static Distance FromKilometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Distance from a value in Centimeter. + /// + /// The value in Centimeter. + /// A new Distance instance. + /// Thrown when the resulting magnitude would be negative. + public static Distance FromCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Centi)), nameof(value))); +/// + /// Creates a new Distance from a value in Millimeter. + /// + /// The value in Millimeter. + /// A new Distance instance. + /// Thrown when the resulting magnitude would be negative. + public static Distance FromMillimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Distance from a value in Micrometer. + /// + /// The value in Micrometer. + /// A new Distance instance. + /// Thrown when the resulting magnitude would be negative. + public static Distance FromMicrometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new Distance from a value in Nanometer. + /// + /// The value in Nanometer. + /// A new Distance instance. + /// Thrown when the resulting magnitude would be negative. + public static Distance FromNanometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Creates a new Distance from a value in Angstrom. + /// + /// The value in Angstrom. + /// A new Distance instance. + /// Thrown when the resulting magnitude would be negative. + public static Distance FromAngstrom(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AngstromToMeters)), nameof(value))); +/// + /// Creates a new Distance from a value in Foot. + /// + /// The value in Foot. + /// A new Distance instance. + /// Thrown when the resulting magnitude would be negative. + public static Distance FromFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FeetToMeters)), nameof(value))); +/// + /// Creates a new Distance from a value in Inch. + /// + /// The value in Inch. + /// A new Distance instance. + /// Thrown when the resulting magnitude would be negative. + public static Distance FromInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.InchesToMeters)), nameof(value))); +/// + /// Creates a new Distance from a value in Yard. + /// + /// The value in Yard. + /// A new Distance instance. + /// Thrown when the resulting magnitude would be negative. + public static Distance FromYard(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YardToMeters)), nameof(value))); +/// + /// Creates a new Distance from a value in Mile. + /// + /// The value in Mile. + /// A new Distance instance. + /// Thrown when the resulting magnitude would be negative. + public static Distance FromMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MileToMeters)), nameof(value))); +/// + /// Creates a new Distance from a value in NauticalMile. + /// + /// The value in NauticalMile. + /// A new Distance instance. + /// Thrown when the resulting magnitude would be negative. + public static Distance FromNauticalMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.NauticalMileToMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Length unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILengthUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Length. + public static implicit operator Length(Distance value) => Length.Create(value.Value); +/// Explicit conversion from Length. + public static explicit operator Distance(Length value) => Create(value.Value); +/// Creates a Distance from a Length value. + public static Distance From(Length value) => Create(value.Value); +/// Subtracts two Distance values, returning the absolute difference as a non-negative Distance. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Distance operator -(Distance left, Distance right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Drag.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Drag.g.cs new file mode 100644 index 0000000..8caef52 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Drag.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Resistive force opposing motion through a medium. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Drag : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Drag Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Force; + + /// + /// Creates a new Drag from a value in Newton. + /// + /// The value in Newton. + /// A new Drag instance. + /// Thrown when the resulting magnitude would be negative. + public static Drag FromNewton(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Drag from a value in Kilonewton. + /// + /// The value in Kilonewton. + /// A new Drag instance. + /// Thrown when the resulting magnitude would be negative. + public static Drag FromKilonewton(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Drag from a value in Dyne. + /// + /// The value in Dyne. + /// A new Drag instance. + /// Thrown when the resulting magnitude would be negative. + public static Drag FromDyne(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DyneToNewtons)), nameof(value))); +/// + /// Creates a new Drag from a value in PoundForce. + /// + /// The value in PoundForce. + /// A new Drag instance. + /// Thrown when the resulting magnitude would be negative. + public static Drag FromPoundForce(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PoundForceToNewtons)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Force unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IForceUnit unit) => unit.FromBase(Value); +/// Implicit conversion to ForceMagnitude. + public static implicit operator ForceMagnitude(Drag value) => ForceMagnitude.Create(value.Value); +/// Explicit conversion from ForceMagnitude. + public static explicit operator Drag(ForceMagnitude value) => Create(value.Value); +/// Creates a Drag from a ForceMagnitude value. + public static Drag From(ForceMagnitude value) => Create(value.Value); +/// Subtracts two Drag values, returning the absolute difference as a non-negative Drag. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Drag operator -(Drag left, Drag right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Duration.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Duration.g.cs new file mode 100644 index 0000000..250a870 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Duration.g.cs @@ -0,0 +1,283 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Time dimension. +/// +/// The numeric storage type. +public partial record Duration : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Duration Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Time; + + /// + /// Creates a new from a value in Second. + /// + /// The value in Second. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Duration FromSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Millisecond. + /// + /// The value in Millisecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Duration FromMillisecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new from a value in Microsecond. + /// + /// The value in Microsecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Duration FromMicrosecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new from a value in Minute. + /// + /// The value in Minute. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Duration FromMinute(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MinuteToSeconds)), nameof(value))); +/// + /// Creates a new from a value in Hour. + /// + /// The value in Hour. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Duration FromHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.HourToSeconds)), nameof(value))); +/// + /// Creates a new from a value in Day. + /// + /// The value in Day. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Duration FromDay(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DayToSeconds)), nameof(value))); +/// + /// Creates a new from a value in Year. + /// + /// The value in Year. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Duration FromYear(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YearToSeconds)), nameof(value))); +/// + /// Creates a new from a value in Week. + /// + /// The value in Week. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Duration FromWeek(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.WeekToSeconds)), nameof(value))); +/// + /// Creates a new from a value in Nanosecond. + /// + /// The value in Nanosecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Duration FromNanosecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Time unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ITimeUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Duration values, returning the absolute difference as a non-negative Duration. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator -(Duration left, Duration right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Duration by CurrentMagnitude to produce ChargeMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ChargeMagnitude operator *(Duration left, CurrentMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Duration by Current1D to produce Charge. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Charge operator *(Duration left, Current1D right) => Multiply>(left, right); +/// + /// Multiplies Duration by Speed to produce Length. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Length operator *(Duration left, Speed right) => Multiply>(left, right); +/// + /// Multiplies Duration by Velocity1D to produce Displacement1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Displacement1D operator *(Duration left, Velocity1D right) => Multiply>(left, right); +/// + /// Multiplies Duration by Velocity2D to produce Displacement2D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Displacement2D operator *(Duration left, Velocity2D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y }; +/// + /// Multiplies Duration by Velocity3D to produce Displacement3D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Displacement3D operator *(Duration left, Velocity3D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z }; +/// + /// Multiplies Duration by Velocity4D to produce Displacement4D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Displacement4D operator *(Duration left, Velocity4D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z, W = left.Value * right.W }; +/// + /// Multiplies Duration by AccelerationMagnitude to produce Speed. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Speed operator *(Duration left, AccelerationMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Duration by Acceleration1D to produce Velocity1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Velocity1D operator *(Duration left, Acceleration1D right) => Multiply>(left, right); +/// + /// Multiplies Duration by Acceleration2D to produce Velocity2D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Velocity2D operator *(Duration left, Acceleration2D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y }; +/// + /// Multiplies Duration by Acceleration3D to produce Velocity3D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Velocity3D operator *(Duration left, Acceleration3D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z }; +/// + /// Multiplies Duration by Acceleration4D to produce Velocity4D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Velocity4D operator *(Duration left, Acceleration4D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z, W = left.Value * right.W }; +/// + /// Multiplies Duration by JerkMagnitude to produce AccelerationMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AccelerationMagnitude operator *(Duration left, JerkMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Duration by Jerk1D to produce Acceleration1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Acceleration1D operator *(Duration left, Jerk1D right) => Multiply>(left, right); +/// + /// Multiplies Duration by Jerk2D to produce Acceleration2D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Acceleration2D operator *(Duration left, Jerk2D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y }; +/// + /// Multiplies Duration by Jerk3D to produce Acceleration3D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Acceleration3D operator *(Duration left, Jerk3D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z }; +/// + /// Multiplies Duration by Jerk4D to produce Acceleration4D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Acceleration4D operator *(Duration left, Jerk4D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z, W = left.Value * right.W }; +/// + /// Multiplies Duration by SnapMagnitude to produce JerkMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static JerkMagnitude operator *(Duration left, SnapMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Duration by Snap1D to produce Jerk1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Jerk1D operator *(Duration left, Snap1D right) => Multiply>(left, right); +/// + /// Multiplies Duration by Snap2D to produce Jerk2D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Jerk2D operator *(Duration left, Snap2D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y }; +/// + /// Multiplies Duration by Snap3D to produce Jerk3D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Jerk3D operator *(Duration left, Snap3D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z }; +/// + /// Multiplies Duration by Snap4D to produce Jerk4D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Jerk4D operator *(Duration left, Snap4D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z, W = left.Value * right.W }; +/// + /// Multiplies Duration by Frequency to produce Ratio. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Ratio operator *(Duration left, Frequency right) => Multiply>(left, right); +/// + /// Multiplies Duration by AngularSpeed to produce Angle. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Angle operator *(Duration left, AngularSpeed right) => Multiply>(left, right); +/// + /// Multiplies Duration by AngularVelocity1D to produce SignedAngle. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SignedAngle operator *(Duration left, AngularVelocity1D right) => Multiply>(left, right); +/// + /// Multiplies Duration by AngularVelocity3D to produce AngularDisplacement3D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularDisplacement3D operator *(Duration left, AngularVelocity3D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z }; +/// + /// Multiplies Duration by AngularAccelerationMagnitude to produce AngularSpeed. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularSpeed operator *(Duration left, AngularAccelerationMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Duration by AngularAcceleration1D to produce AngularVelocity1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularVelocity1D operator *(Duration left, AngularAcceleration1D right) => Multiply>(left, right); +/// + /// Multiplies Duration by AngularAcceleration3D to produce AngularVelocity3D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularVelocity3D operator *(Duration left, AngularAcceleration3D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z }; +/// + /// Multiplies Duration by AngularJerkMagnitude to produce AngularAccelerationMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularAccelerationMagnitude operator *(Duration left, AngularJerkMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Duration by AngularJerk1D to produce AngularAcceleration1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularAcceleration1D operator *(Duration left, AngularJerk1D right) => Multiply>(left, right); +/// + /// Multiplies Duration by AngularJerk3D to produce AngularAcceleration3D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularAcceleration3D operator *(Duration left, AngularJerk3D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z }; +/// + /// Multiplies Duration by TorqueMagnitude to produce AngularMomentumMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularMomentumMagnitude operator *(Duration left, TorqueMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Duration by Torque1D to produce AngularMomentum1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularMomentum1D operator *(Duration left, Torque1D right) => Multiply>(left, right); +/// + /// Multiplies Duration by Torque3D to produce AngularMomentum3D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularMomentum3D operator *(Duration left, Torque3D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z }; +/// + /// Multiplies Duration by ForceMagnitude to produce MomentumMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MomentumMagnitude operator *(Duration left, ForceMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Duration by Force1D to produce Momentum1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Momentum1D operator *(Duration left, Force1D right) => Multiply>(left, right); +/// + /// Multiplies Duration by Force2D to produce Momentum2D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Momentum2D operator *(Duration left, Force2D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y }; +/// + /// Multiplies Duration by Force3D to produce Momentum3D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Momentum3D operator *(Duration left, Force3D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z }; +/// + /// Multiplies Duration by Force4D to produce Momentum4D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Momentum4D operator *(Duration left, Force4D right) => new() { X = left.Value * right.X, Y = left.Value * right.Y, Z = left.Value * right.Z, W = left.Value * right.W }; +/// + /// Multiplies Duration by Power to produce Energy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Energy operator *(Duration left, Power right) => Multiply>(left, right); +/// + /// Multiplies Duration by VolumetricFlowRate to produce Volume. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Volume operator *(Duration left, VolumetricFlowRate right) => Multiply>(left, right); +/// + /// Multiplies Duration by MassFlowRate to produce Mass. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Mass operator *(Duration left, MassFlowRate right) => Multiply>(left, right); +/// + /// Multiplies Duration by CatalyticActivity to produce AmountOfSubstance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AmountOfSubstance operator *(Duration left, CatalyticActivity right) => Multiply>(left, right); +/// + /// Multiplies Duration by ReactionRate to produce Concentration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Concentration operator *(Duration left, ReactionRate right) => Multiply>(left, right); +/// + /// Multiplies Duration by VoltageMagnitude to produce MagneticFlux. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MagneticFlux operator *(Duration left, VoltageMagnitude right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/DynamicViscosity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/DynamicViscosity.g.cs new file mode 100644 index 0000000..3d123c6 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/DynamicViscosity.g.cs @@ -0,0 +1,61 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the DynamicViscosity dimension. +/// +/// The numeric storage type. +public partial record DynamicViscosity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static DynamicViscosity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.DynamicViscosity; + + /// + /// Creates a new from a value in PascalSecond. + /// + /// The value in PascalSecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static DynamicViscosity FromPascalSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Poise. + /// + /// The value in Poise. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static DynamicViscosity FromPoise(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PoiseToPascalSecond)), nameof(value))); +/// + /// Creates a new from a value in Centipoise. + /// + /// The value in Centipoise. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static DynamicViscosity FromCentipoise(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CentipoiseToPascalSecond)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-DynamicViscosity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IDynamicViscosityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two DynamicViscosity values, returning the absolute difference as a non-negative DynamicViscosity. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static DynamicViscosity operator -(DynamicViscosity left, DynamicViscosity right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides DynamicViscosity by Density to produce KinematicViscosity. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static KinematicViscosity operator /(DynamicViscosity left, Density right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/EMF.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/EMF.g.cs new file mode 100644 index 0000000..5278032 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/EMF.g.cs @@ -0,0 +1,54 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Electromotive force. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record EMF : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static EMF Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricPotential; + + /// + /// Creates a new EMF from a value in Volt. + /// + /// The value in Volt. + /// A new EMF instance. + /// Thrown when the resulting magnitude would be negative. + public static EMF FromVolt(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new EMF from a value in Kilovolt. + /// + /// The value in Kilovolt. + /// A new EMF instance. + /// Thrown when the resulting magnitude would be negative. + public static EMF FromKilovolt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricPotential unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricPotentialUnit unit) => unit.FromBase(Value); +/// Implicit conversion to VoltageMagnitude. + public static implicit operator VoltageMagnitude(EMF value) => VoltageMagnitude.Create(value.Value); +/// Explicit conversion from VoltageMagnitude. + public static explicit operator EMF(VoltageMagnitude value) => Create(value.Value); +/// Creates a EMF from a VoltageMagnitude value. + public static EMF From(VoltageMagnitude value) => Create(value.Value); +/// Subtracts two EMF values, returning the absolute difference as a non-negative EMF. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static EMF operator -(EMF left, EMF right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricConductivity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricConductivity.g.cs new file mode 100644 index 0000000..834e488 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricConductivity.g.cs @@ -0,0 +1,43 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ElectricConductivity dimension. +/// +/// The numeric storage type. +public partial record ElectricConductivity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ElectricConductivity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricConductivity; + + /// + /// Creates a new from a value in SiemensPerMeter. + /// + /// The value in SiemensPerMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ElectricConductivity FromSiemensPerMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricConductivity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricConductivityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two ElectricConductivity values, returning the absolute difference as a non-negative ElectricConductivity. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ElectricConductivity operator -(ElectricConductivity left, ElectricConductivity right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricField1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricField1D.g.cs new file mode 100644 index 0000000..ce4b49a --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricField1D.g.cs @@ -0,0 +1,46 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the ElectricField dimension. +/// +/// The numeric storage type. +public partial record ElectricField1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ElectricField1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricField; + + /// + /// Creates a new from a value in VoltPerMeter. + /// + /// The value in VoltPerMeter. + /// A new instance. + public static ElectricField1D FromVoltPerMeter(T value) => Create(value); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricField unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricFieldUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public ElectricFieldMagnitude Magnitude() => ElectricFieldMagnitude.Create(T.Abs(Value)); +/// + /// Multiplies ElectricField1D by Length to produce Voltage. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Voltage operator *(ElectricField1D left, Length right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricField2D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricField2D.g.cs new file mode 100644 index 0000000..19ff49b --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricField2D.g.cs @@ -0,0 +1,98 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 2D vector representation of ElectricField. +/// +/// The numeric component type. +public partial record ElectricField2D : IVector2, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets a vector with all components set to zero. + public static ElectricField2D Zero => new() { X = T.Zero, Y = T.Zero }; + + /// Gets a vector with all components set to one. + public static ElectricField2D One => new() { X = T.One, Y = T.One }; + + /// Gets the unit vector for the X-axis. + public static ElectricField2D UnitX => new() { X = T.One, Y = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static ElectricField2D UnitY => new() { X = T.Zero, Y = T.One }; + + /// Gets the magnitude as a . + public ElectricFieldMagnitude Magnitude() => ElectricFieldMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y); + + /// Calculates the dot product of two vectors. + public T Dot(ElectricField2D other) => (X * other.X) + (Y * other.Y); + + /// Calculates the distance between two vectors. + public T Distance(ElectricField2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T sum = (dX * dX) + (dY * dY); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(ElectricField2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + return (dX * dX) + (dY * dY); + } + + /// Returns a normalized version of the vector. + public ElectricField2D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len }; + } + + /// Adds two vectors. + public static ElectricField2D operator +(ElectricField2D left, ElectricField2D right) => new() { X = left.X + right.X, Y = left.Y + right.Y }; + + /// Subtracts two vectors. + public static ElectricField2D operator -(ElectricField2D left, ElectricField2D right) => new() { X = left.X - right.X, Y = left.Y - right.Y }; + + /// Multiplies a vector by a scalar. + public static ElectricField2D operator *(ElectricField2D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar }; + + /// Multiplies a scalar by a vector. + public static ElectricField2D operator *(T scalar, ElectricField2D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y }; + + /// Divides a vector by a scalar. + public static ElectricField2D operator /(ElectricField2D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar }; + + /// Negates a vector. + public static ElectricField2D operator -(ElectricField2D vector) => new() { X = -vector.X, Y = -vector.Y }; +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricField3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricField3D.g.cs new file mode 100644 index 0000000..5eaf5ae --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricField3D.g.cs @@ -0,0 +1,112 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of ElectricField. +/// +/// The numeric component type. +public partial record ElectricField3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static ElectricField3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static ElectricField3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static ElectricField3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static ElectricField3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static ElectricField3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public ElectricFieldMagnitude Magnitude() => ElectricFieldMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(ElectricField3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public ElectricField3D Cross(ElectricField3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(ElectricField3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(ElectricField3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public ElectricField3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static ElectricField3D operator +(ElectricField3D left, ElectricField3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static ElectricField3D operator -(ElectricField3D left, ElectricField3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static ElectricField3D operator *(ElectricField3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static ElectricField3D operator *(T scalar, ElectricField3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static ElectricField3D operator /(ElectricField3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static ElectricField3D operator -(ElectricField3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricFieldMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricFieldMagnitude.g.cs new file mode 100644 index 0000000..413a1ee --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricFieldMagnitude.g.cs @@ -0,0 +1,51 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ElectricField dimension. +/// +/// The numeric storage type. +public partial record ElectricFieldMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ElectricFieldMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricField; + + /// + /// Creates a new from a value in VoltPerMeter. + /// + /// The value in VoltPerMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ElectricFieldMagnitude FromVoltPerMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricField unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricFieldUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two ElectricFieldMagnitude values, returning the absolute difference as a non-negative ElectricFieldMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ElectricFieldMagnitude operator -(ElectricFieldMagnitude left, ElectricFieldMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies ElectricFieldMagnitude by Length to produce VoltageMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VoltageMagnitude operator *(ElectricFieldMagnitude left, Length right) => Multiply>(left, right); +/// + /// Multiplies ElectricFieldMagnitude by Area to produce ElectricFlux. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ElectricFlux operator *(ElectricFieldMagnitude left, Area right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricFlux.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricFlux.g.cs new file mode 100644 index 0000000..21a9043 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricFlux.g.cs @@ -0,0 +1,51 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ElectricFlux dimension. +/// +/// The numeric storage type. +public partial record ElectricFlux : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ElectricFlux Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricFlux; + + /// + /// Creates a new from a value in VoltMeter. + /// + /// The value in VoltMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ElectricFlux FromVoltMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricFlux unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricFluxUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two ElectricFlux values, returning the absolute difference as a non-negative ElectricFlux. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ElectricFlux operator -(ElectricFlux left, ElectricFlux right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides ElectricFlux by Area to produce ElectricFieldMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ElectricFieldMagnitude operator /(ElectricFlux left, Area right) => Divide>(left, right); +/// + /// Divides ElectricFlux by ElectricFieldMagnitude to produce Area. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Area operator /(ElectricFlux left, ElectricFieldMagnitude right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricPowerDensity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricPowerDensity.g.cs new file mode 100644 index 0000000..6f464f6 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ElectricPowerDensity.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ElectricPowerDensity dimension. +/// +/// The numeric storage type. +public partial record ElectricPowerDensity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ElectricPowerDensity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricPowerDensity; + + /// + /// Creates a new from a value in WattPerCubicMeter. + /// + /// The value in WattPerCubicMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ElectricPowerDensity FromWattPerCubicMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricPowerDensity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricPowerDensityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two ElectricPowerDensity values, returning the absolute difference as a non-negative ElectricPowerDensity. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ElectricPowerDensity operator -(ElectricPowerDensity left, ElectricPowerDensity right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies ElectricPowerDensity by Volume to produce Power. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Power operator *(ElectricPowerDensity left, Volume right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Energy.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Energy.g.cs new file mode 100644 index 0000000..d90d5f5 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Energy.g.cs @@ -0,0 +1,147 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Energy dimension. +/// +/// The numeric storage type. +public partial record Energy : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Energy Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// + /// Creates a new from a value in Joule. + /// + /// The value in Joule. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Energy FromJoule(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Kilojoule. + /// + /// The value in Kilojoule. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Energy FromKilojoule(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new from a value in ElectronVolt. + /// + /// The value in ElectronVolt. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Energy FromElectronVolt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ElectronVoltToJoules)), nameof(value))); +/// + /// Creates a new from a value in Calorie. + /// + /// The value in Calorie. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Energy FromCalorie(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CalorieToJoules)), nameof(value))); +/// + /// Creates a new from a value in Kilocalorie. + /// + /// The value in Kilocalorie. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Energy FromKilocalorie(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilocalorieToJoules)), nameof(value))); +/// + /// Creates a new from a value in KilowattHour. + /// + /// The value in KilowattHour. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Energy FromKilowattHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilowattHourToJoules)), nameof(value))); +/// + /// Creates a new from a value in WattHour. + /// + /// The value in WattHour. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Energy FromWattHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.WattHourToJoules)), nameof(value))); +/// + /// Creates a new from a value in Erg. + /// + /// The value in Erg. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Energy FromErg(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ErgToJoules)), nameof(value))); +/// + /// Creates a new from a value in Btu. + /// + /// The value in Btu. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Energy FromBtu(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BtuToJoules)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Energy unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IEnergyUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Energy values, returning the absolute difference as a non-negative Energy. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Energy operator -(Energy left, Energy right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides Energy by Angle to produce TorqueMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static TorqueMagnitude operator /(Energy left, Angle right) => Divide>(left, right); +/// + /// Divides Energy by TorqueMagnitude to produce Angle. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Angle operator /(Energy left, TorqueMagnitude right) => Divide>(left, right); +/// + /// Divides Energy by Length to produce ForceMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ForceMagnitude operator /(Energy left, Length right) => Divide>(left, right); +/// + /// Divides Energy by ForceMagnitude to produce Length. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Length operator /(Energy left, ForceMagnitude right) => Divide>(left, right); +/// + /// Divides Energy by Volume to produce Pressure. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Pressure operator /(Energy left, Volume right) => Divide>(left, right); +/// + /// Divides Energy by Pressure to produce Volume. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Volume operator /(Energy left, Pressure right) => Divide>(left, right); +/// + /// Divides Energy by Duration to produce Power. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Power operator /(Energy left, Duration right) => Divide>(left, right); +/// + /// Divides Energy by Power to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(Energy left, Power right) => Divide>(left, right); +/// + /// Divides Energy by Temperature to produce Entropy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Entropy operator /(Energy left, Temperature right) => Divide>(left, right); +/// + /// Divides Energy by Entropy to produce Temperature. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Temperature operator /(Energy left, Entropy right) => Divide>(left, right); +/// + /// Divides Energy by AmountOfSubstance to produce MolarEnergy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MolarEnergy operator /(Energy left, AmountOfSubstance right) => Divide>(left, right); +/// + /// Divides Energy by MolarEnergy to produce AmountOfSubstance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AmountOfSubstance operator /(Energy left, MolarEnergy right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/EnergyFluxDensity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/EnergyFluxDensity.g.cs new file mode 100644 index 0000000..f67c7e8 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/EnergyFluxDensity.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Rate of energy transfer per unit area. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record EnergyFluxDensity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static EnergyFluxDensity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Irradiance; + + /// + /// Creates a new EnergyFluxDensity from a value in WattPerSquareMeter. + /// + /// The value in WattPerSquareMeter. + /// A new EnergyFluxDensity instance. + /// Thrown when the resulting magnitude would be negative. + public static EnergyFluxDensity FromWattPerSquareMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Irradiance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IIrradianceUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Irradiance. + public static implicit operator Irradiance(EnergyFluxDensity value) => Irradiance.Create(value.Value); +/// Explicit conversion from Irradiance. + public static explicit operator EnergyFluxDensity(Irradiance value) => Create(value.Value); +/// Creates a EnergyFluxDensity from a Irradiance value. + public static EnergyFluxDensity From(Irradiance value) => Create(value.Value); +/// Subtracts two EnergyFluxDensity values, returning the absolute difference as a non-negative EnergyFluxDensity. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static EnergyFluxDensity operator -(EnergyFluxDensity left, EnergyFluxDensity right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Entropy.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Entropy.g.cs new file mode 100644 index 0000000..71c95e8 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Entropy.g.cs @@ -0,0 +1,55 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Entropy dimension. +/// +/// The numeric storage type. +public partial record Entropy : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Entropy Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Entropy; + + /// + /// Creates a new from a value in JoulePerKelvin. + /// + /// The value in JoulePerKelvin. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Entropy FromJoulePerKelvin(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Entropy unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IEntropyUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Entropy values, returning the absolute difference as a non-negative Entropy. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Entropy operator -(Entropy left, Entropy right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Entropy by Temperature to produce Energy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Energy operator *(Entropy left, Temperature right) => Multiply>(left, right); +/// + /// Divides Entropy by Mass to produce SpecificHeat. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SpecificHeat operator /(Entropy left, Mass right) => Divide>(left, right); +/// + /// Divides Entropy by SpecificHeat to produce Mass. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Mass operator /(Entropy left, SpecificHeat right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/EnzymeActivity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/EnzymeActivity.g.cs new file mode 100644 index 0000000..20415a3 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/EnzymeActivity.g.cs @@ -0,0 +1,54 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Rate of enzyme-catalyzed reaction. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record EnzymeActivity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static EnzymeActivity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.CatalyticActivity; + + /// + /// Creates a new EnzymeActivity from a value in Katal. + /// + /// The value in Katal. + /// A new EnzymeActivity instance. + /// Thrown when the resulting magnitude would be negative. + public static EnzymeActivity FromKatal(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new EnzymeActivity from a value in EnzymeUnit. + /// + /// The value in EnzymeUnit. + /// A new EnzymeActivity instance. + /// Thrown when the resulting magnitude would be negative. + public static EnzymeActivity FromEnzymeUnit(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.EnzymeUnitToKatals)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-CatalyticActivity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ICatalyticActivityUnit unit) => unit.FromBase(Value); +/// Implicit conversion to CatalyticActivity. + public static implicit operator CatalyticActivity(EnzymeActivity value) => CatalyticActivity.Create(value.Value); +/// Explicit conversion from CatalyticActivity. + public static explicit operator EnzymeActivity(CatalyticActivity value) => Create(value.Value); +/// Creates a EnzymeActivity from a CatalyticActivity value. + public static EnzymeActivity From(CatalyticActivity value) => Create(value.Value); +/// Subtracts two EnzymeActivity values, returning the absolute difference as a non-negative EnzymeActivity. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static EnzymeActivity operator -(EnzymeActivity left, EnzymeActivity right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/EquivalentDose.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/EquivalentDose.g.cs new file mode 100644 index 0000000..911bddc --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/EquivalentDose.g.cs @@ -0,0 +1,50 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the EquivalentDose dimension. +/// +/// The numeric storage type. +public partial record EquivalentDose : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static EquivalentDose Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.EquivalentDose; + + /// + /// Creates a new from a value in Sievert. + /// + /// The value in Sievert. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static EquivalentDose FromSievert(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Rem. + /// + /// The value in Rem. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static EquivalentDose FromRem(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RemToSieverts)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-EquivalentDose unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IEquivalentDoseUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two EquivalentDose values, returning the absolute difference as a non-negative EquivalentDose. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static EquivalentDose operator -(EquivalentDose left, EquivalentDose right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Exposure.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Exposure.g.cs new file mode 100644 index 0000000..d598238 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Exposure.g.cs @@ -0,0 +1,50 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Exposure dimension. +/// +/// The numeric storage type. +public partial record Exposure : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Exposure Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Exposure; + + /// + /// Creates a new from a value in CoulombPerKilogram. + /// + /// The value in CoulombPerKilogram. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Exposure FromCoulombPerKilogram(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Roentgen. + /// + /// The value in Roentgen. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Exposure FromRoentgen(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RoentgenToCoulombsPerKilogram)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Exposure unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IExposureUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Exposure values, returning the absolute difference as a non-negative Exposure. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Exposure operator -(Exposure left, Exposure right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/FieldOfView.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/FieldOfView.g.cs new file mode 100644 index 0000000..bc9b49d --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/FieldOfView.g.cs @@ -0,0 +1,75 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Angular extent of observable area. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record FieldOfView : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static FieldOfView Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularDisplacement; + + /// + /// Creates a new FieldOfView from a value in Radian. + /// + /// The value in Radian. + /// A new FieldOfView instance. + /// Thrown when the resulting magnitude would be negative. + public static FieldOfView FromRadian(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new FieldOfView from a value in Degree. + /// + /// The value in Degree. + /// A new FieldOfView instance. + /// Thrown when the resulting magnitude would be negative. + public static FieldOfView FromDegree(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians)), nameof(value))); +/// + /// Creates a new FieldOfView from a value in Gradian. + /// + /// The value in Gradian. + /// A new FieldOfView instance. + /// Thrown when the resulting magnitude would be negative. + public static FieldOfView FromGradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians)), nameof(value))); +/// + /// Creates a new FieldOfView from a value in Revolution. + /// + /// The value in Revolution. + /// A new FieldOfView instance. + /// Thrown when the resulting magnitude would be negative. + public static FieldOfView FromRevolution(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians)), nameof(value))); +/// + /// Creates a new FieldOfView from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new FieldOfView instance. + /// Thrown when the resulting magnitude would be negative. + public static FieldOfView FromMilliradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularDisplacement unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularDisplacementUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Angle. + public static implicit operator Angle(FieldOfView value) => Angle.Create(value.Value); +/// Explicit conversion from Angle. + public static explicit operator FieldOfView(Angle value) => Create(value.Value); +/// Creates a FieldOfView from a Angle value. + public static FieldOfView From(Angle value) => Create(value.Value); +/// Subtracts two FieldOfView values, returning the absolute difference as a non-negative FieldOfView. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static FieldOfView operator -(FieldOfView left, FieldOfView right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/FlowSpeed.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/FlowSpeed.g.cs new file mode 100644 index 0000000..6fbff78 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/FlowSpeed.g.cs @@ -0,0 +1,75 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Speed of fluid flow. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record FlowSpeed : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static FlowSpeed Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// + /// Creates a new FlowSpeed from a value in MeterPerSecond. + /// + /// The value in MeterPerSecond. + /// A new FlowSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static FlowSpeed FromMeterPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new FlowSpeed from a value in KilometerPerHour. + /// + /// The value in KilometerPerHour. + /// A new FlowSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static FlowSpeed FromKilometerPerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilometerPerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new FlowSpeed from a value in MilePerHour. + /// + /// The value in MilePerHour. + /// A new FlowSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static FlowSpeed FromMilePerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MilePerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new FlowSpeed from a value in FootPerSecond. + /// + /// The value in FootPerSecond. + /// A new FlowSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static FlowSpeed FromFootPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FootPerSecondToMeterPerSecond)), nameof(value))); +/// + /// Creates a new FlowSpeed from a value in Knot. + /// + /// The value in Knot. + /// A new FlowSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static FlowSpeed FromKnot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KnotToMeterPerSecond)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Velocity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IVelocityUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Speed. + public static implicit operator Speed(FlowSpeed value) => Speed.Create(value.Value); +/// Explicit conversion from Speed. + public static explicit operator FlowSpeed(Speed value) => Create(value.Value); +/// Creates a FlowSpeed from a Speed value. + public static FlowSpeed From(Speed value) => Create(value.Value); +/// Subtracts two FlowSpeed values, returning the absolute difference as a non-negative FlowSpeed. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static FlowSpeed operator -(FlowSpeed left, FlowSpeed right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Force1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Force1D.g.cs new file mode 100644 index 0000000..81b4d52 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Force1D.g.cs @@ -0,0 +1,64 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the Force dimension. +/// +/// The numeric storage type. +public partial record Force1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Force1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Force; + + /// + /// Creates a new from a value in Newton. + /// + /// The value in Newton. + /// A new instance. + public static Force1D FromNewton(T value) => Create(value); +/// + /// Creates a new from a value in Kilonewton. + /// + /// The value in Kilonewton. + /// A new instance. + public static Force1D FromKilonewton(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Kilo))); +/// + /// Creates a new from a value in Dyne. + /// + /// The value in Dyne. + /// A new instance. + public static Force1D FromDyne(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.DyneToNewtons))); +/// + /// Creates a new from a value in PoundForce. + /// + /// The value in PoundForce. + /// A new instance. + public static Force1D FromPoundForce(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.PoundForceToNewtons))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Force unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IForceUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public ForceMagnitude Magnitude() => ForceMagnitude.Create(T.Abs(Value)); +/// + /// Multiplies Force1D by Duration to produce Momentum1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Momentum1D operator *(Force1D left, Duration right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Force2D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Force2D.g.cs new file mode 100644 index 0000000..a2d2fe7 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Force2D.g.cs @@ -0,0 +1,104 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 2D vector representation of Force. +/// +/// The numeric component type. +public partial record Force2D : IVector2, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets a vector with all components set to zero. + public static Force2D Zero => new() { X = T.Zero, Y = T.Zero }; + + /// Gets a vector with all components set to one. + public static Force2D One => new() { X = T.One, Y = T.One }; + + /// Gets the unit vector for the X-axis. + public static Force2D UnitX => new() { X = T.One, Y = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Force2D UnitY => new() { X = T.Zero, Y = T.One }; + + /// Gets the magnitude as a . + public ForceMagnitude Magnitude() => ForceMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y); + + /// Calculates the dot product of two vectors. + public T Dot(Force2D other) => (X * other.X) + (Y * other.Y); + + /// Calculates the distance between two vectors. + public T Distance(Force2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T sum = (dX * dX) + (dY * dY); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Force2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + return (dX * dX) + (dY * dY); + } + + /// Returns a normalized version of the vector. + public Force2D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len }; + } + + /// Adds two vectors. + public static Force2D operator +(Force2D left, Force2D right) => new() { X = left.X + right.X, Y = left.Y + right.Y }; + + /// Subtracts two vectors. + public static Force2D operator -(Force2D left, Force2D right) => new() { X = left.X - right.X, Y = left.Y - right.Y }; + + /// Multiplies a vector by a scalar. + public static Force2D operator *(Force2D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar }; + + /// Multiplies a scalar by a vector. + public static Force2D operator *(T scalar, Force2D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y }; + + /// Divides a vector by a scalar. + public static Force2D operator /(Force2D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar }; + + /// Negates a vector. + public static Force2D operator -(Force2D vector) => new() { X = -vector.X, Y = -vector.Y }; + /// Force2D * Duration = Momentum2D. + public static Momentum2D operator *(Force2D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value }; + + /// Typed dot product: Force2D . Displacement2D = Energy. + public Energy Dot(Displacement2D other) => Energy.Create((X * other.X) + (Y * other.Y)); + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Force3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Force3D.g.cs new file mode 100644 index 0000000..bb8596b --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Force3D.g.cs @@ -0,0 +1,124 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of Force. +/// +/// The numeric component type. +public partial record Force3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static Force3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static Force3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static Force3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Force3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Force3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public ForceMagnitude Magnitude() => ForceMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(Force3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public Force3D Cross(Force3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(Force3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Force3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public Force3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static Force3D operator +(Force3D left, Force3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static Force3D operator -(Force3D left, Force3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static Force3D operator *(Force3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static Force3D operator *(T scalar, Force3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static Force3D operator /(Force3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static Force3D operator -(Force3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Force3D * Duration = Momentum3D. + public static Momentum3D operator *(Force3D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value }; + + /// Typed dot product: Force3D . Displacement3D = Energy. + public Energy Dot(Displacement3D other) => Energy.Create((X * other.X) + (Y * other.Y) + (Z * other.Z)); + + /// Typed cross product: Force3D x Displacement3D = Torque3D. + public Torque3D Cross(Displacement3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Force4D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Force4D.g.cs new file mode 100644 index 0000000..531e507 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Force4D.g.cs @@ -0,0 +1,120 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 4D vector representation of Force. +/// +/// The numeric component type. +public partial record Force4D : IVector4, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets the W component. + public T W { get; init; } + + /// Gets a vector with all components set to zero. + public static Force4D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets a vector with all components set to one. + public static Force4D One => new() { X = T.One, Y = T.One, Z = T.One, W = T.One }; + + /// Gets the unit vector for the X-axis. + public static Force4D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Force4D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Force4D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One, W = T.Zero }; + + /// Gets the unit vector for the W-axis. + public static Force4D UnitW => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.One }; + + /// Gets the magnitude as a . + public ForceMagnitude Magnitude() => ForceMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z) + (W * W); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z) + (W * W); + + /// Calculates the dot product of two vectors. + public T Dot(Force4D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z) + (W * other.W); + + /// Calculates the distance between two vectors. + public T Distance(Force4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Force4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + return (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + } + + /// Returns a normalized version of the vector. + public Force4D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len, W = W / len }; + } + + /// Adds two vectors. + public static Force4D operator +(Force4D left, Force4D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z, W = left.W + right.W }; + + /// Subtracts two vectors. + public static Force4D operator -(Force4D left, Force4D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z, W = left.W - right.W }; + + /// Multiplies a vector by a scalar. + public static Force4D operator *(Force4D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar, W = vector.W * scalar }; + + /// Multiplies a scalar by a vector. + public static Force4D operator *(T scalar, Force4D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z, W = scalar * vector.W }; + + /// Divides a vector by a scalar. + public static Force4D operator /(Force4D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar, W = vector.W / scalar }; + + /// Negates a vector. + public static Force4D operator -(Force4D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z, W = -vector.W }; + /// Force4D * Duration = Momentum4D. + public static Momentum4D operator *(Force4D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value, W = left.W * right.Value }; + + /// Typed dot product: Force4D . Displacement4D = Energy. + public Energy Dot(Displacement4D other) => Energy.Create((X * other.X) + (Y * other.Y) + (Z * other.Z) + (W * other.W)); + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ForceMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ForceMagnitude.g.cs new file mode 100644 index 0000000..251294d --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ForceMagnitude.g.cs @@ -0,0 +1,96 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Force dimension. +/// +/// The numeric storage type. +public partial record ForceMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ForceMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Force; + + /// + /// Creates a new from a value in Newton. + /// + /// The value in Newton. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ForceMagnitude FromNewton(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Kilonewton. + /// + /// The value in Kilonewton. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ForceMagnitude FromKilonewton(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new from a value in Dyne. + /// + /// The value in Dyne. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ForceMagnitude FromDyne(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DyneToNewtons)), nameof(value))); +/// + /// Creates a new from a value in PoundForce. + /// + /// The value in PoundForce. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ForceMagnitude FromPoundForce(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PoundForceToNewtons)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Force unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IForceUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two ForceMagnitude values, returning the absolute difference as a non-negative ForceMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ForceMagnitude operator -(ForceMagnitude left, ForceMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides ForceMagnitude by AccelerationMagnitude to produce Mass. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Mass operator /(ForceMagnitude left, AccelerationMagnitude right) => Divide>(left, right); +/// + /// Divides ForceMagnitude by Mass to produce AccelerationMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AccelerationMagnitude operator /(ForceMagnitude left, Mass right) => Divide>(left, right); +/// + /// Multiplies ForceMagnitude by Length to produce Energy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Energy operator *(ForceMagnitude left, Length right) => Multiply>(left, right); +/// + /// Multiplies ForceMagnitude by Duration to produce MomentumMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MomentumMagnitude operator *(ForceMagnitude left, Duration right) => Multiply>(left, right); +/// + /// Multiplies ForceMagnitude by Speed to produce Power. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Power operator *(ForceMagnitude left, Speed right) => Multiply>(left, right); +/// + /// Divides ForceMagnitude by Area to produce Pressure. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Pressure operator /(ForceMagnitude left, Area right) => Divide>(left, right); +/// + /// Divides ForceMagnitude by Length to produce SurfaceTension. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SurfaceTension operator /(ForceMagnitude left, Length right) => Divide>(left, right); +/// + /// Divides ForceMagnitude by SurfaceTension to produce Length. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Length operator /(ForceMagnitude left, SurfaceTension right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Frequency.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Frequency.g.cs new file mode 100644 index 0000000..5c2e9d1 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Frequency.g.cs @@ -0,0 +1,65 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Frequency dimension. +/// +/// The numeric storage type. +public partial record Frequency : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Frequency Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Frequency; + + /// + /// Creates a new from a value in Hertz. + /// + /// The value in Hertz. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Frequency FromHertz(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Kilohertz. + /// + /// The value in Kilohertz. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Frequency FromKilohertz(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new from a value in Megahertz. + /// + /// The value in Megahertz. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Frequency FromMegahertz(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Mega)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Frequency unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IFrequencyUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Frequency values, returning the absolute difference as a non-negative Frequency. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Frequency operator -(Frequency left, Frequency right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Frequency by Duration to produce Ratio. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Ratio operator *(Frequency left, Duration right) => Multiply>(left, right); +/// + /// Multiplies Frequency by Length to produce Speed. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Speed operator *(Frequency left, Length right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Friction.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Friction.g.cs new file mode 100644 index 0000000..06cb6a5 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Friction.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Force resisting relative motion between surfaces. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Friction : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Friction Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Force; + + /// + /// Creates a new Friction from a value in Newton. + /// + /// The value in Newton. + /// A new Friction instance. + /// Thrown when the resulting magnitude would be negative. + public static Friction FromNewton(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Friction from a value in Kilonewton. + /// + /// The value in Kilonewton. + /// A new Friction instance. + /// Thrown when the resulting magnitude would be negative. + public static Friction FromKilonewton(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Friction from a value in Dyne. + /// + /// The value in Dyne. + /// A new Friction instance. + /// Thrown when the resulting magnitude would be negative. + public static Friction FromDyne(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DyneToNewtons)), nameof(value))); +/// + /// Creates a new Friction from a value in PoundForce. + /// + /// The value in PoundForce. + /// A new Friction instance. + /// Thrown when the resulting magnitude would be negative. + public static Friction FromPoundForce(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PoundForceToNewtons)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Force unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IForceUnit unit) => unit.FromBase(Value); +/// Implicit conversion to ForceMagnitude. + public static implicit operator ForceMagnitude(Friction value) => ForceMagnitude.Create(value.Value); +/// Explicit conversion from ForceMagnitude. + public static explicit operator Friction(ForceMagnitude value) => Create(value.Value); +/// Creates a Friction from a ForceMagnitude value. + public static Friction From(ForceMagnitude value) => Create(value.Value); +/// Subtracts two Friction values, returning the absolute difference as a non-negative Friction. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Friction operator -(Friction left, Friction right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Gain.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Gain.g.cs new file mode 100644 index 0000000..a8cf06c --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Gain.g.cs @@ -0,0 +1,110 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Linear amplitude factor applied to a signal (1 = unity). +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Gain : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Gain Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// + /// Creates a new Gain from a value in Dimensionless. + /// + /// The value in Dimensionless. + /// A new Gain instance. + /// Thrown when the resulting magnitude would be negative. + public static Gain FromDimensionless(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Gain from a value in Radian. + /// + /// The value in Radian. + /// A new Gain instance. + /// Thrown when the resulting magnitude would be negative. + public static Gain FromRadian(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Gain from a value in Degree. + /// + /// The value in Degree. + /// A new Gain instance. + /// Thrown when the resulting magnitude would be negative. + public static Gain FromDegree(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians)), nameof(value))); +/// + /// Creates a new Gain from a value in Gradian. + /// + /// The value in Gradian. + /// A new Gain instance. + /// Thrown when the resulting magnitude would be negative. + public static Gain FromGradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians)), nameof(value))); +/// + /// Creates a new Gain from a value in Revolution. + /// + /// The value in Revolution. + /// A new Gain instance. + /// Thrown when the resulting magnitude would be negative. + public static Gain FromRevolution(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians)), nameof(value))); +/// + /// Creates a new Gain from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new Gain instance. + /// Thrown when the resulting magnitude would be negative. + public static Gain FromMilliradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Gain from a value in Percent. + /// + /// The value in Percent. + /// A new Gain instance. + /// Thrown when the resulting magnitude would be negative. + public static Gain FromPercent(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentToRatio)), nameof(value))); +/// + /// Creates a new Gain from a value in PartPerMillion. + /// + /// The value in PartPerMillion. + /// A new Gain instance. + /// Thrown when the resulting magnitude would be negative. + public static Gain FromPartPerMillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerMillionToRatio)), nameof(value))); +/// + /// Creates a new Gain from a value in PartPerBillion. + /// + /// The value in PartPerBillion. + /// A new Gain instance. + /// Thrown when the resulting magnitude would be negative. + public static Gain FromPartPerBillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerBillionToRatio)), nameof(value))); +/// + /// Creates a new Gain from a value in PercentByWeight. + /// + /// The value in PercentByWeight. + /// A new Gain instance. + /// Thrown when the resulting magnitude would be negative. + public static Gain FromPercentByWeight(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentByWeightToRatio)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Dimensionless unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IDimensionlessUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Ratio. + public static implicit operator Ratio(Gain value) => Ratio.Create(value.Value); +/// Explicit conversion from Ratio. + public static explicit operator Gain(Ratio value) => Create(value.Value); +/// Creates a Gain from a Ratio value. + public static Gain From(Ratio value) => Create(value.Value); +/// Subtracts two Gain values, returning the absolute difference as a non-negative Gain. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Gain operator -(Gain left, Gain right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GaugePressure.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GaugePressure.g.cs new file mode 100644 index 0000000..0c87461 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GaugePressure.g.cs @@ -0,0 +1,82 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Pressure relative to atmospheric pressure. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record GaugePressure : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static GaugePressure Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// + /// Creates a new GaugePressure from a value in Pascal. + /// + /// The value in Pascal. + /// A new GaugePressure instance. + /// Thrown when the resulting magnitude would be negative. + public static GaugePressure FromPascal(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new GaugePressure from a value in Kilopascal. + /// + /// The value in Kilopascal. + /// A new GaugePressure instance. + /// Thrown when the resulting magnitude would be negative. + public static GaugePressure FromKilopascal(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new GaugePressure from a value in Bar. + /// + /// The value in Bar. + /// A new GaugePressure instance. + /// Thrown when the resulting magnitude would be negative. + public static GaugePressure FromBar(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BarToPascals)), nameof(value))); +/// + /// Creates a new GaugePressure from a value in Atmosphere. + /// + /// The value in Atmosphere. + /// A new GaugePressure instance. + /// Thrown when the resulting magnitude would be negative. + public static GaugePressure FromAtmosphere(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AtmosphereToPascals)), nameof(value))); +/// + /// Creates a new GaugePressure from a value in Psi. + /// + /// The value in Psi. + /// A new GaugePressure instance. + /// Thrown when the resulting magnitude would be negative. + public static GaugePressure FromPsi(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PsiToPascals)), nameof(value))); +/// + /// Creates a new GaugePressure from a value in Torr. + /// + /// The value in Torr. + /// A new GaugePressure instance. + /// Thrown when the resulting magnitude would be negative. + public static GaugePressure FromTorr(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.TorrToPascals)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Pressure unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IPressureUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Pressure. + public static implicit operator Pressure(GaugePressure value) => Pressure.Create(value.Value); +/// Explicit conversion from Pressure. + public static explicit operator GaugePressure(Pressure value) => Create(value.Value); +/// Creates a GaugePressure from a Pressure value. + public static GaugePressure From(Pressure value) => Create(value.Value); +/// Subtracts two GaugePressure values, returning the absolute difference as a non-negative GaugePressure. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static GaugePressure operator -(GaugePressure left, GaugePressure right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GravitationalAcceleration.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GravitationalAcceleration.g.cs new file mode 100644 index 0000000..88ef1c3 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GravitationalAcceleration.g.cs @@ -0,0 +1,54 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Acceleration due to gravity. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record GravitationalAcceleration : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static GravitationalAcceleration Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Acceleration; + + /// + /// Creates a new GravitationalAcceleration from a value in MeterPerSecondSquared. + /// + /// The value in MeterPerSecondSquared. + /// A new GravitationalAcceleration instance. + /// Thrown when the resulting magnitude would be negative. + public static GravitationalAcceleration FromMeterPerSecondSquared(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new GravitationalAcceleration from a value in StandardGravity. + /// + /// The value in StandardGravity. + /// A new GravitationalAcceleration instance. + /// Thrown when the resulting magnitude would be negative. + public static GravitationalAcceleration FromStandardGravity(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.StandardGravityToMeterPerSecondSquared)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Acceleration unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAccelerationUnit unit) => unit.FromBase(Value); +/// Implicit conversion to AccelerationMagnitude. + public static implicit operator AccelerationMagnitude(GravitationalAcceleration value) => AccelerationMagnitude.Create(value.Value); +/// Explicit conversion from AccelerationMagnitude. + public static explicit operator GravitationalAcceleration(AccelerationMagnitude value) => Create(value.Value); +/// Creates a GravitationalAcceleration from a AccelerationMagnitude value. + public static GravitationalAcceleration From(AccelerationMagnitude value) => Create(value.Value); +/// Subtracts two GravitationalAcceleration values, returning the absolute difference as a non-negative GravitationalAcceleration. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static GravitationalAcceleration operator -(GravitationalAcceleration left, GravitationalAcceleration right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GravitationalField3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GravitationalField3D.g.cs new file mode 100644 index 0000000..17bee24 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GravitationalField3D.g.cs @@ -0,0 +1,115 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// Gravitational acceleration field in 3D. +/// Semantic overload of . +/// +public partial record GravitationalField3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static GravitationalField3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static GravitationalField3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static GravitationalField3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static GravitationalField3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static GravitationalField3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(GravitationalField3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public GravitationalField3D Cross(GravitationalField3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(GravitationalField3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(GravitationalField3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public GravitationalField3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static GravitationalField3D operator +(GravitationalField3D left, GravitationalField3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static GravitationalField3D operator -(GravitationalField3D left, GravitationalField3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static GravitationalField3D operator *(GravitationalField3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static GravitationalField3D operator *(T scalar, GravitationalField3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static GravitationalField3D operator /(GravitationalField3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static GravitationalField3D operator -(GravitationalField3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Implicit conversion to Acceleration3D. + public static implicit operator Acceleration3D(GravitationalField3D value) => new() { X = value.X, Y = value.Y, Z = value.Z }; + + /// Explicit conversion from Acceleration3D. + public static explicit operator GravitationalField3D(Acceleration3D value) => new() { X = value.X, Y = value.Y, Z = value.Z }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GroundSpeed.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GroundSpeed.g.cs new file mode 100644 index 0000000..21afffd --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GroundSpeed.g.cs @@ -0,0 +1,75 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Speed relative to the ground. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record GroundSpeed : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static GroundSpeed Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// + /// Creates a new GroundSpeed from a value in MeterPerSecond. + /// + /// The value in MeterPerSecond. + /// A new GroundSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static GroundSpeed FromMeterPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new GroundSpeed from a value in KilometerPerHour. + /// + /// The value in KilometerPerHour. + /// A new GroundSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static GroundSpeed FromKilometerPerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilometerPerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new GroundSpeed from a value in MilePerHour. + /// + /// The value in MilePerHour. + /// A new GroundSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static GroundSpeed FromMilePerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MilePerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new GroundSpeed from a value in FootPerSecond. + /// + /// The value in FootPerSecond. + /// A new GroundSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static GroundSpeed FromFootPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FootPerSecondToMeterPerSecond)), nameof(value))); +/// + /// Creates a new GroundSpeed from a value in Knot. + /// + /// The value in Knot. + /// A new GroundSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static GroundSpeed FromKnot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KnotToMeterPerSecond)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Velocity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IVelocityUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Speed. + public static implicit operator Speed(GroundSpeed value) => Speed.Create(value.Value); +/// Explicit conversion from Speed. + public static explicit operator GroundSpeed(Speed value) => Create(value.Value); +/// Creates a GroundSpeed from a Speed value. + public static GroundSpeed From(Speed value) => Create(value.Value); +/// Subtracts two GroundSpeed values, returning the absolute difference as a non-negative GroundSpeed. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static GroundSpeed operator -(GroundSpeed left, GroundSpeed right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GroupVelocity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GroupVelocity.g.cs new file mode 100644 index 0000000..cacfab0 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/GroupVelocity.g.cs @@ -0,0 +1,75 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Speed at which the wave envelope propagates. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record GroupVelocity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static GroupVelocity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// + /// Creates a new GroupVelocity from a value in MeterPerSecond. + /// + /// The value in MeterPerSecond. + /// A new GroupVelocity instance. + /// Thrown when the resulting magnitude would be negative. + public static GroupVelocity FromMeterPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new GroupVelocity from a value in KilometerPerHour. + /// + /// The value in KilometerPerHour. + /// A new GroupVelocity instance. + /// Thrown when the resulting magnitude would be negative. + public static GroupVelocity FromKilometerPerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilometerPerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new GroupVelocity from a value in MilePerHour. + /// + /// The value in MilePerHour. + /// A new GroupVelocity instance. + /// Thrown when the resulting magnitude would be negative. + public static GroupVelocity FromMilePerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MilePerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new GroupVelocity from a value in FootPerSecond. + /// + /// The value in FootPerSecond. + /// A new GroupVelocity instance. + /// Thrown when the resulting magnitude would be negative. + public static GroupVelocity FromFootPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FootPerSecondToMeterPerSecond)), nameof(value))); +/// + /// Creates a new GroupVelocity from a value in Knot. + /// + /// The value in Knot. + /// A new GroupVelocity instance. + /// Thrown when the resulting magnitude would be negative. + public static GroupVelocity FromKnot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KnotToMeterPerSecond)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Velocity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IVelocityUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Speed. + public static implicit operator Speed(GroupVelocity value) => Speed.Create(value.Value); +/// Explicit conversion from Speed. + public static explicit operator GroupVelocity(Speed value) => Create(value.Value); +/// Creates a GroupVelocity from a Speed value. + public static GroupVelocity From(Speed value) => Create(value.Value); +/// Subtracts two GroupVelocity values, returning the absolute difference as a non-negative GroupVelocity. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static GroupVelocity operator -(GroupVelocity left, GroupVelocity right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HalfLife.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HalfLife.g.cs new file mode 100644 index 0000000..0fc2e83 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HalfLife.g.cs @@ -0,0 +1,103 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Time for half of a substance to decay. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record HalfLife : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static HalfLife Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Time; + + /// + /// Creates a new HalfLife from a value in Second. + /// + /// The value in Second. + /// A new HalfLife instance. + /// Thrown when the resulting magnitude would be negative. + public static HalfLife FromSecond(T value) => Create(Vector0Guards.EnsurePositive(value, nameof(value))); +/// + /// Creates a new HalfLife from a value in Millisecond. + /// + /// The value in Millisecond. + /// A new HalfLife instance. + /// Thrown when the resulting magnitude would be negative. + public static HalfLife FromMillisecond(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new HalfLife from a value in Microsecond. + /// + /// The value in Microsecond. + /// A new HalfLife instance. + /// Thrown when the resulting magnitude would be negative. + public static HalfLife FromMicrosecond(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new HalfLife from a value in Minute. + /// + /// The value in Minute. + /// A new HalfLife instance. + /// Thrown when the resulting magnitude would be negative. + public static HalfLife FromMinute(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.MinuteToSeconds)), nameof(value))); +/// + /// Creates a new HalfLife from a value in Hour. + /// + /// The value in Hour. + /// A new HalfLife instance. + /// Thrown when the resulting magnitude would be negative. + public static HalfLife FromHour(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.HourToSeconds)), nameof(value))); +/// + /// Creates a new HalfLife from a value in Day. + /// + /// The value in Day. + /// A new HalfLife instance. + /// Thrown when the resulting magnitude would be negative. + public static HalfLife FromDay(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.DayToSeconds)), nameof(value))); +/// + /// Creates a new HalfLife from a value in Year. + /// + /// The value in Year. + /// A new HalfLife instance. + /// Thrown when the resulting magnitude would be negative. + public static HalfLife FromYear(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.YearToSeconds)), nameof(value))); +/// + /// Creates a new HalfLife from a value in Week. + /// + /// The value in Week. + /// A new HalfLife instance. + /// Thrown when the resulting magnitude would be negative. + public static HalfLife FromWeek(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.WeekToSeconds)), nameof(value))); +/// + /// Creates a new HalfLife from a value in Nanosecond. + /// + /// The value in Nanosecond. + /// A new HalfLife instance. + /// Thrown when the resulting magnitude would be negative. + public static HalfLife FromNanosecond(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Time unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ITimeUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Duration. + public static implicit operator Duration(HalfLife value) => Duration.Create(value.Value); +/// Explicit conversion from Duration. + public static explicit operator HalfLife(Duration value) => Create(value.Value); +/// Creates a HalfLife from a Duration value. + public static HalfLife From(Duration value) => Create(value.Value); +/// Subtracts two HalfLife values, returning the absolute difference as a non-negative HalfLife. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static HalfLife operator -(HalfLife left, HalfLife right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Heading.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Heading.g.cs new file mode 100644 index 0000000..5eb0a35 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Heading.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Direction of travel or orientation. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Heading : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Heading Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularDisplacement; + + /// + /// Creates a new Heading from a value in Radian. + /// + /// The value in Radian. + /// A new Heading instance. + public static Heading FromRadian(T value) => Create(value); +/// + /// Creates a new Heading from a value in Degree. + /// + /// The value in Degree. + /// A new Heading instance. + public static Heading FromDegree(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians))); +/// + /// Creates a new Heading from a value in Gradian. + /// + /// The value in Gradian. + /// A new Heading instance. + public static Heading FromGradian(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians))); +/// + /// Creates a new Heading from a value in Revolution. + /// + /// The value in Revolution. + /// A new Heading instance. + public static Heading FromRevolution(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians))); +/// + /// Creates a new Heading from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new Heading instance. + public static Heading FromMilliradian(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Milli))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularDisplacement unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularDisplacementUnit unit) => unit.FromBase(Value); +/// Implicit conversion to SignedAngle. + public static implicit operator SignedAngle(Heading value) => SignedAngle.Create(value.Value); +/// Explicit conversion from SignedAngle. + public static explicit operator Heading(SignedAngle value) => Create(value.Value); +/// Creates a Heading from a SignedAngle value. + public static Heading From(SignedAngle value) => Create(value.Value); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Heat.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Heat.g.cs new file mode 100644 index 0000000..121cea9 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Heat.g.cs @@ -0,0 +1,103 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Energy transferred due to temperature difference. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Heat : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Heat Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// + /// Creates a new Heat from a value in Joule. + /// + /// The value in Joule. + /// A new Heat instance. + /// Thrown when the resulting magnitude would be negative. + public static Heat FromJoule(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Heat from a value in Kilojoule. + /// + /// The value in Kilojoule. + /// A new Heat instance. + /// Thrown when the resulting magnitude would be negative. + public static Heat FromKilojoule(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Heat from a value in ElectronVolt. + /// + /// The value in ElectronVolt. + /// A new Heat instance. + /// Thrown when the resulting magnitude would be negative. + public static Heat FromElectronVolt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ElectronVoltToJoules)), nameof(value))); +/// + /// Creates a new Heat from a value in Calorie. + /// + /// The value in Calorie. + /// A new Heat instance. + /// Thrown when the resulting magnitude would be negative. + public static Heat FromCalorie(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CalorieToJoules)), nameof(value))); +/// + /// Creates a new Heat from a value in Kilocalorie. + /// + /// The value in Kilocalorie. + /// A new Heat instance. + /// Thrown when the resulting magnitude would be negative. + public static Heat FromKilocalorie(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilocalorieToJoules)), nameof(value))); +/// + /// Creates a new Heat from a value in KilowattHour. + /// + /// The value in KilowattHour. + /// A new Heat instance. + /// Thrown when the resulting magnitude would be negative. + public static Heat FromKilowattHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilowattHourToJoules)), nameof(value))); +/// + /// Creates a new Heat from a value in WattHour. + /// + /// The value in WattHour. + /// A new Heat instance. + /// Thrown when the resulting magnitude would be negative. + public static Heat FromWattHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.WattHourToJoules)), nameof(value))); +/// + /// Creates a new Heat from a value in Erg. + /// + /// The value in Erg. + /// A new Heat instance. + /// Thrown when the resulting magnitude would be negative. + public static Heat FromErg(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ErgToJoules)), nameof(value))); +/// + /// Creates a new Heat from a value in Btu. + /// + /// The value in Btu. + /// A new Heat instance. + /// Thrown when the resulting magnitude would be negative. + public static Heat FromBtu(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BtuToJoules)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Energy unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IEnergyUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Energy. + public static implicit operator Energy(Heat value) => Energy.Create(value.Value); +/// Explicit conversion from Energy. + public static explicit operator Heat(Energy value) => Create(value.Value); +/// Creates a Heat from a Energy value. + public static Heat From(Energy value) => Create(value.Value); +/// Subtracts two Heat values, returning the absolute difference as a non-negative Heat. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Heat operator -(Heat left, Heat right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HeatCapacity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HeatCapacity.g.cs new file mode 100644 index 0000000..4443f1b --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HeatCapacity.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Amount of heat required to change temperature by one unit. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record HeatCapacity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static HeatCapacity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Entropy; + + /// + /// Creates a new HeatCapacity from a value in JoulePerKelvin. + /// + /// The value in JoulePerKelvin. + /// A new HeatCapacity instance. + /// Thrown when the resulting magnitude would be negative. + public static HeatCapacity FromJoulePerKelvin(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Entropy unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IEntropyUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Entropy. + public static implicit operator Entropy(HeatCapacity value) => Entropy.Create(value.Value); +/// Explicit conversion from Entropy. + public static explicit operator HeatCapacity(Entropy value) => Create(value.Value); +/// Creates a HeatCapacity from a Entropy value. + public static HeatCapacity From(Entropy value) => Create(value.Value); +/// Subtracts two HeatCapacity values, returning the absolute difference as a non-negative HeatCapacity. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static HeatCapacity operator -(HeatCapacity left, HeatCapacity right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HeatFlowRate.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HeatFlowRate.g.cs new file mode 100644 index 0000000..49ee3d8 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HeatFlowRate.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Rate of heat energy transfer. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record HeatFlowRate : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static HeatFlowRate Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Power; + + /// + /// Creates a new HeatFlowRate from a value in Watt. + /// + /// The value in Watt. + /// A new HeatFlowRate instance. + /// Thrown when the resulting magnitude would be negative. + public static HeatFlowRate FromWatt(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new HeatFlowRate from a value in Kilowatt. + /// + /// The value in Kilowatt. + /// A new HeatFlowRate instance. + /// Thrown when the resulting magnitude would be negative. + public static HeatFlowRate FromKilowatt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new HeatFlowRate from a value in Megawatt. + /// + /// The value in Megawatt. + /// A new HeatFlowRate instance. + /// Thrown when the resulting magnitude would be negative. + public static HeatFlowRate FromMegawatt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Mega)), nameof(value))); +/// + /// Creates a new HeatFlowRate from a value in Horsepower. + /// + /// The value in Horsepower. + /// A new HeatFlowRate instance. + /// Thrown when the resulting magnitude would be negative. + public static HeatFlowRate FromHorsepower(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.HorsepowerToWatts)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Power unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IPowerUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Power. + public static implicit operator Power(HeatFlowRate value) => Power.Create(value.Value); +/// Explicit conversion from Power. + public static explicit operator HeatFlowRate(Power value) => Create(value.Value); +/// Creates a HeatFlowRate from a Power value. + public static HeatFlowRate From(Power value) => Create(value.Value); +/// Subtracts two HeatFlowRate values, returning the absolute difference as a non-negative HeatFlowRate. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static HeatFlowRate operator -(HeatFlowRate left, HeatFlowRate right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HeatFlux.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HeatFlux.g.cs new file mode 100644 index 0000000..07e54c5 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HeatFlux.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Rate of heat energy transfer per unit area. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record HeatFlux : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static HeatFlux Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Irradiance; + + /// + /// Creates a new HeatFlux from a value in WattPerSquareMeter. + /// + /// The value in WattPerSquareMeter. + /// A new HeatFlux instance. + /// Thrown when the resulting magnitude would be negative. + public static HeatFlux FromWattPerSquareMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Irradiance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IIrradianceUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Irradiance. + public static implicit operator Irradiance(HeatFlux value) => Irradiance.Create(value.Value); +/// Explicit conversion from Irradiance. + public static explicit operator HeatFlux(Irradiance value) => Create(value.Value); +/// Creates a HeatFlux from a Irradiance value. + public static HeatFlux From(Irradiance value) => Create(value.Value); +/// Subtracts two HeatFlux values, returning the absolute difference as a non-negative HeatFlux. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static HeatFlux operator -(HeatFlux left, HeatFlux right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HeatTransferCoefficient.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HeatTransferCoefficient.g.cs new file mode 100644 index 0000000..21011b9 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/HeatTransferCoefficient.g.cs @@ -0,0 +1,43 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the HeatTransferCoefficient dimension. +/// +/// The numeric storage type. +public partial record HeatTransferCoefficient : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static HeatTransferCoefficient Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.HeatTransferCoefficient; + + /// + /// Creates a new from a value in WattPerSquareMeterKelvin. + /// + /// The value in WattPerSquareMeterKelvin. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static HeatTransferCoefficient FromWattPerSquareMeterKelvin(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-HeatTransferCoefficient unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IHeatTransferCoefficientUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two HeatTransferCoefficient values, returning the absolute difference as a non-negative HeatTransferCoefficient. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static HeatTransferCoefficient operator -(HeatTransferCoefficient left, HeatTransferCoefficient right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Height.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Height.g.cs new file mode 100644 index 0000000..d0c7b9a --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Height.g.cs @@ -0,0 +1,124 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Vertical extent of an object or space. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Height : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Height Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Length; + + /// + /// Creates a new Height from a value in Meter. + /// + /// The value in Meter. + /// A new Height instance. + /// Thrown when the resulting magnitude would be negative. + public static Height FromMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Height from a value in Kilometer. + /// + /// The value in Kilometer. + /// A new Height instance. + /// Thrown when the resulting magnitude would be negative. + public static Height FromKilometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Height from a value in Centimeter. + /// + /// The value in Centimeter. + /// A new Height instance. + /// Thrown when the resulting magnitude would be negative. + public static Height FromCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Centi)), nameof(value))); +/// + /// Creates a new Height from a value in Millimeter. + /// + /// The value in Millimeter. + /// A new Height instance. + /// Thrown when the resulting magnitude would be negative. + public static Height FromMillimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Height from a value in Micrometer. + /// + /// The value in Micrometer. + /// A new Height instance. + /// Thrown when the resulting magnitude would be negative. + public static Height FromMicrometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new Height from a value in Nanometer. + /// + /// The value in Nanometer. + /// A new Height instance. + /// Thrown when the resulting magnitude would be negative. + public static Height FromNanometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Creates a new Height from a value in Angstrom. + /// + /// The value in Angstrom. + /// A new Height instance. + /// Thrown when the resulting magnitude would be negative. + public static Height FromAngstrom(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AngstromToMeters)), nameof(value))); +/// + /// Creates a new Height from a value in Foot. + /// + /// The value in Foot. + /// A new Height instance. + /// Thrown when the resulting magnitude would be negative. + public static Height FromFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FeetToMeters)), nameof(value))); +/// + /// Creates a new Height from a value in Inch. + /// + /// The value in Inch. + /// A new Height instance. + /// Thrown when the resulting magnitude would be negative. + public static Height FromInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.InchesToMeters)), nameof(value))); +/// + /// Creates a new Height from a value in Yard. + /// + /// The value in Yard. + /// A new Height instance. + /// Thrown when the resulting magnitude would be negative. + public static Height FromYard(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YardToMeters)), nameof(value))); +/// + /// Creates a new Height from a value in Mile. + /// + /// The value in Mile. + /// A new Height instance. + /// Thrown when the resulting magnitude would be negative. + public static Height FromMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MileToMeters)), nameof(value))); +/// + /// Creates a new Height from a value in NauticalMile. + /// + /// The value in NauticalMile. + /// A new Height instance. + /// Thrown when the resulting magnitude would be negative. + public static Height FromNauticalMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.NauticalMileToMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Length unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILengthUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Length. + public static implicit operator Length(Height value) => Length.Create(value.Value); +/// Explicit conversion from Length. + public static explicit operator Height(Length value) => Create(value.Value); +/// Creates a Height from a Length value. + public static Height From(Length value) => Create(value.Value); +/// Subtracts two Height values, returning the absolute difference as a non-negative Height. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Height operator -(Height left, Height right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Illuminance.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Illuminance.g.cs new file mode 100644 index 0000000..2428e11 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Illuminance.g.cs @@ -0,0 +1,54 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Illuminance dimension. +/// +/// The numeric storage type. +public partial record Illuminance : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Illuminance Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Illuminance; + + /// + /// Creates a new from a value in Lux. + /// + /// The value in Lux. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Illuminance FromLux(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in FootCandle. + /// + /// The value in FootCandle. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Illuminance FromFootCandle(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FootCandleToLux)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Illuminance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IIlluminanceUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Illuminance values, returning the absolute difference as a non-negative Illuminance. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Illuminance operator -(Illuminance left, Illuminance right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Illuminance by Area to produce LuminousFlux. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static LuminousFlux operator *(Illuminance left, Area right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Impedance.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Impedance.g.cs new file mode 100644 index 0000000..707b29d --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Impedance.g.cs @@ -0,0 +1,61 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude of total opposition to alternating current. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Impedance : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Impedance Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricResistance; + + /// + /// Creates a new Impedance from a value in Ohm. + /// + /// The value in Ohm. + /// A new Impedance instance. + /// Thrown when the resulting magnitude would be negative. + public static Impedance FromOhm(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Impedance from a value in Kilohm. + /// + /// The value in Kilohm. + /// A new Impedance instance. + /// Thrown when the resulting magnitude would be negative. + public static Impedance FromKilohm(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Impedance from a value in Megohm. + /// + /// The value in Megohm. + /// A new Impedance instance. + /// Thrown when the resulting magnitude would be negative. + public static Impedance FromMegohm(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Mega)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricResistance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricResistanceUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Resistance. + public static implicit operator Resistance(Impedance value) => Resistance.Create(value.Value); +/// Explicit conversion from Resistance. + public static explicit operator Impedance(Resistance value) => Create(value.Value); +/// Creates a Impedance from a Resistance value. + public static Impedance From(Resistance value) => Create(value.Value); +/// Subtracts two Impedance values, returning the absolute difference as a non-negative Impedance. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Impedance operator -(Impedance left, Impedance right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Inductance.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Inductance.g.cs new file mode 100644 index 0000000..35793d6 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Inductance.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Inductance dimension. +/// +/// The numeric storage type. +public partial record Inductance : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Inductance Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Inductance; + + /// + /// Creates a new from a value in Henry. + /// + /// The value in Henry. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Inductance FromHenry(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Inductance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IInductanceUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Inductance values, returning the absolute difference as a non-negative Inductance. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Inductance operator -(Inductance left, Inductance right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Inductance by CurrentMagnitude to produce MagneticFlux. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MagneticFlux operator *(Inductance left, CurrentMagnitude right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Irradiance.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Irradiance.g.cs new file mode 100644 index 0000000..367de02 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Irradiance.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Irradiance dimension. +/// +/// The numeric storage type. +public partial record Irradiance : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Irradiance Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Irradiance; + + /// + /// Creates a new from a value in WattPerSquareMeter. + /// + /// The value in WattPerSquareMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Irradiance FromWattPerSquareMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Irradiance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IIrradianceUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Irradiance values, returning the absolute difference as a non-negative Irradiance. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Irradiance operator -(Irradiance left, Irradiance right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Irradiance by Area to produce Power. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Power operator *(Irradiance left, Area right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Jerk1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Jerk1D.g.cs new file mode 100644 index 0000000..5f0f0ac --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Jerk1D.g.cs @@ -0,0 +1,50 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the Jerk dimension. +/// +/// The numeric storage type. +public partial record Jerk1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Jerk1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Jerk; + + /// + /// Creates a new from a value in MeterPerSecondCubed. + /// + /// The value in MeterPerSecondCubed. + /// A new instance. + public static Jerk1D FromMeterPerSecondCubed(T value) => Create(value); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Jerk unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IJerkUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public JerkMagnitude Magnitude() => JerkMagnitude.Create(T.Abs(Value)); +/// + /// Multiplies Jerk1D by Duration to produce Acceleration1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Acceleration1D operator *(Jerk1D left, Duration right) => Multiply>(left, right); +/// + /// Divides Jerk1D by Duration to produce Snap1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Snap1D operator /(Jerk1D left, Duration right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Jerk2D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Jerk2D.g.cs new file mode 100644 index 0000000..3ffc3e9 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Jerk2D.g.cs @@ -0,0 +1,104 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 2D vector representation of Jerk. +/// +/// The numeric component type. +public partial record Jerk2D : IVector2, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets a vector with all components set to zero. + public static Jerk2D Zero => new() { X = T.Zero, Y = T.Zero }; + + /// Gets a vector with all components set to one. + public static Jerk2D One => new() { X = T.One, Y = T.One }; + + /// Gets the unit vector for the X-axis. + public static Jerk2D UnitX => new() { X = T.One, Y = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Jerk2D UnitY => new() { X = T.Zero, Y = T.One }; + + /// Gets the magnitude as a . + public JerkMagnitude Magnitude() => JerkMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y); + + /// Calculates the dot product of two vectors. + public T Dot(Jerk2D other) => (X * other.X) + (Y * other.Y); + + /// Calculates the distance between two vectors. + public T Distance(Jerk2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T sum = (dX * dX) + (dY * dY); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Jerk2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + return (dX * dX) + (dY * dY); + } + + /// Returns a normalized version of the vector. + public Jerk2D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len }; + } + + /// Adds two vectors. + public static Jerk2D operator +(Jerk2D left, Jerk2D right) => new() { X = left.X + right.X, Y = left.Y + right.Y }; + + /// Subtracts two vectors. + public static Jerk2D operator -(Jerk2D left, Jerk2D right) => new() { X = left.X - right.X, Y = left.Y - right.Y }; + + /// Multiplies a vector by a scalar. + public static Jerk2D operator *(Jerk2D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar }; + + /// Multiplies a scalar by a vector. + public static Jerk2D operator *(T scalar, Jerk2D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y }; + + /// Divides a vector by a scalar. + public static Jerk2D operator /(Jerk2D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar }; + + /// Negates a vector. + public static Jerk2D operator -(Jerk2D vector) => new() { X = -vector.X, Y = -vector.Y }; + /// Jerk2D * Duration = Acceleration2D. + public static Acceleration2D operator *(Jerk2D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value }; + + /// Jerk2D / Duration = Snap2D. + public static Snap2D operator /(Jerk2D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Jerk3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Jerk3D.g.cs new file mode 100644 index 0000000..9b23e91 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Jerk3D.g.cs @@ -0,0 +1,118 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of Jerk. +/// +/// The numeric component type. +public partial record Jerk3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static Jerk3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static Jerk3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static Jerk3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Jerk3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Jerk3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public JerkMagnitude Magnitude() => JerkMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(Jerk3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public Jerk3D Cross(Jerk3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(Jerk3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Jerk3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public Jerk3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static Jerk3D operator +(Jerk3D left, Jerk3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static Jerk3D operator -(Jerk3D left, Jerk3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static Jerk3D operator *(Jerk3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static Jerk3D operator *(T scalar, Jerk3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static Jerk3D operator /(Jerk3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static Jerk3D operator -(Jerk3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Jerk3D * Duration = Acceleration3D. + public static Acceleration3D operator *(Jerk3D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value }; + + /// Jerk3D / Duration = Snap3D. + public static Snap3D operator /(Jerk3D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Jerk4D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Jerk4D.g.cs new file mode 100644 index 0000000..c4872ac --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Jerk4D.g.cs @@ -0,0 +1,120 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 4D vector representation of Jerk. +/// +/// The numeric component type. +public partial record Jerk4D : IVector4, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets the W component. + public T W { get; init; } + + /// Gets a vector with all components set to zero. + public static Jerk4D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets a vector with all components set to one. + public static Jerk4D One => new() { X = T.One, Y = T.One, Z = T.One, W = T.One }; + + /// Gets the unit vector for the X-axis. + public static Jerk4D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Jerk4D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Jerk4D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One, W = T.Zero }; + + /// Gets the unit vector for the W-axis. + public static Jerk4D UnitW => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.One }; + + /// Gets the magnitude as a . + public JerkMagnitude Magnitude() => JerkMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z) + (W * W); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z) + (W * W); + + /// Calculates the dot product of two vectors. + public T Dot(Jerk4D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z) + (W * other.W); + + /// Calculates the distance between two vectors. + public T Distance(Jerk4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Jerk4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + return (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + } + + /// Returns a normalized version of the vector. + public Jerk4D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len, W = W / len }; + } + + /// Adds two vectors. + public static Jerk4D operator +(Jerk4D left, Jerk4D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z, W = left.W + right.W }; + + /// Subtracts two vectors. + public static Jerk4D operator -(Jerk4D left, Jerk4D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z, W = left.W - right.W }; + + /// Multiplies a vector by a scalar. + public static Jerk4D operator *(Jerk4D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar, W = vector.W * scalar }; + + /// Multiplies a scalar by a vector. + public static Jerk4D operator *(T scalar, Jerk4D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z, W = scalar * vector.W }; + + /// Divides a vector by a scalar. + public static Jerk4D operator /(Jerk4D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar, W = vector.W / scalar }; + + /// Negates a vector. + public static Jerk4D operator -(Jerk4D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z, W = -vector.W }; + /// Jerk4D * Duration = Acceleration4D. + public static Acceleration4D operator *(Jerk4D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value, W = left.W * right.Value }; + + /// Jerk4D / Duration = Snap4D. + public static Snap4D operator /(Jerk4D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value, W = left.W / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/JerkMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/JerkMagnitude.g.cs new file mode 100644 index 0000000..20f4019 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/JerkMagnitude.g.cs @@ -0,0 +1,55 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Jerk dimension. +/// +/// The numeric storage type. +public partial record JerkMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static JerkMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Jerk; + + /// + /// Creates a new from a value in MeterPerSecondCubed. + /// + /// The value in MeterPerSecondCubed. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static JerkMagnitude FromMeterPerSecondCubed(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Jerk unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IJerkUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two JerkMagnitude values, returning the absolute difference as a non-negative JerkMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static JerkMagnitude operator -(JerkMagnitude left, JerkMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies JerkMagnitude by Duration to produce AccelerationMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AccelerationMagnitude operator *(JerkMagnitude left, Duration right) => Multiply>(left, right); +/// + /// Divides JerkMagnitude by Duration to produce SnapMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SnapMagnitude operator /(JerkMagnitude left, Duration right) => Divide>(left, right); +/// + /// Divides JerkMagnitude by SnapMagnitude to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(JerkMagnitude left, SnapMagnitude right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/KinematicViscosity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/KinematicViscosity.g.cs new file mode 100644 index 0000000..d0a29e1 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/KinematicViscosity.g.cs @@ -0,0 +1,54 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the KinematicViscosity dimension. +/// +/// The numeric storage type. +public partial record KinematicViscosity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static KinematicViscosity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.KinematicViscosity; + + /// + /// Creates a new from a value in SquareMeterPerSecond. + /// + /// The value in SquareMeterPerSecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static KinematicViscosity FromSquareMeterPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Stokes. + /// + /// The value in Stokes. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static KinematicViscosity FromStokes(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.StokesToSquareMeterPerSecond)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-KinematicViscosity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IKinematicViscosityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two KinematicViscosity values, returning the absolute difference as a non-negative KinematicViscosity. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static KinematicViscosity operator -(KinematicViscosity left, KinematicViscosity right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies KinematicViscosity by Density to produce DynamicViscosity. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static DynamicViscosity operator *(KinematicViscosity left, Density right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/KineticEnergy.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/KineticEnergy.g.cs new file mode 100644 index 0000000..9bdc054 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/KineticEnergy.g.cs @@ -0,0 +1,103 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Energy of motion. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record KineticEnergy : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static KineticEnergy Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// + /// Creates a new KineticEnergy from a value in Joule. + /// + /// The value in Joule. + /// A new KineticEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static KineticEnergy FromJoule(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new KineticEnergy from a value in Kilojoule. + /// + /// The value in Kilojoule. + /// A new KineticEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static KineticEnergy FromKilojoule(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new KineticEnergy from a value in ElectronVolt. + /// + /// The value in ElectronVolt. + /// A new KineticEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static KineticEnergy FromElectronVolt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ElectronVoltToJoules)), nameof(value))); +/// + /// Creates a new KineticEnergy from a value in Calorie. + /// + /// The value in Calorie. + /// A new KineticEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static KineticEnergy FromCalorie(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CalorieToJoules)), nameof(value))); +/// + /// Creates a new KineticEnergy from a value in Kilocalorie. + /// + /// The value in Kilocalorie. + /// A new KineticEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static KineticEnergy FromKilocalorie(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilocalorieToJoules)), nameof(value))); +/// + /// Creates a new KineticEnergy from a value in KilowattHour. + /// + /// The value in KilowattHour. + /// A new KineticEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static KineticEnergy FromKilowattHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilowattHourToJoules)), nameof(value))); +/// + /// Creates a new KineticEnergy from a value in WattHour. + /// + /// The value in WattHour. + /// A new KineticEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static KineticEnergy FromWattHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.WattHourToJoules)), nameof(value))); +/// + /// Creates a new KineticEnergy from a value in Erg. + /// + /// The value in Erg. + /// A new KineticEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static KineticEnergy FromErg(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ErgToJoules)), nameof(value))); +/// + /// Creates a new KineticEnergy from a value in Btu. + /// + /// The value in Btu. + /// A new KineticEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static KineticEnergy FromBtu(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BtuToJoules)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Energy unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IEnergyUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Energy. + public static implicit operator Energy(KineticEnergy value) => Energy.Create(value.Value); +/// Explicit conversion from Energy. + public static explicit operator KineticEnergy(Energy value) => Create(value.Value); +/// Creates a KineticEnergy from a Energy value. + public static KineticEnergy From(Energy value) => Create(value.Value); +/// Subtracts two KineticEnergy values, returning the absolute difference as a non-negative KineticEnergy. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static KineticEnergy operator -(KineticEnergy left, KineticEnergy right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Latency.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Latency.g.cs new file mode 100644 index 0000000..0330162 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Latency.g.cs @@ -0,0 +1,103 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Delay before a response begins. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Latency : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Latency Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Time; + + /// + /// Creates a new Latency from a value in Second. + /// + /// The value in Second. + /// A new Latency instance. + /// Thrown when the resulting magnitude would be negative. + public static Latency FromSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Latency from a value in Millisecond. + /// + /// The value in Millisecond. + /// A new Latency instance. + /// Thrown when the resulting magnitude would be negative. + public static Latency FromMillisecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Latency from a value in Microsecond. + /// + /// The value in Microsecond. + /// A new Latency instance. + /// Thrown when the resulting magnitude would be negative. + public static Latency FromMicrosecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new Latency from a value in Minute. + /// + /// The value in Minute. + /// A new Latency instance. + /// Thrown when the resulting magnitude would be negative. + public static Latency FromMinute(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MinuteToSeconds)), nameof(value))); +/// + /// Creates a new Latency from a value in Hour. + /// + /// The value in Hour. + /// A new Latency instance. + /// Thrown when the resulting magnitude would be negative. + public static Latency FromHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.HourToSeconds)), nameof(value))); +/// + /// Creates a new Latency from a value in Day. + /// + /// The value in Day. + /// A new Latency instance. + /// Thrown when the resulting magnitude would be negative. + public static Latency FromDay(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DayToSeconds)), nameof(value))); +/// + /// Creates a new Latency from a value in Year. + /// + /// The value in Year. + /// A new Latency instance. + /// Thrown when the resulting magnitude would be negative. + public static Latency FromYear(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YearToSeconds)), nameof(value))); +/// + /// Creates a new Latency from a value in Week. + /// + /// The value in Week. + /// A new Latency instance. + /// Thrown when the resulting magnitude would be negative. + public static Latency FromWeek(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.WeekToSeconds)), nameof(value))); +/// + /// Creates a new Latency from a value in Nanosecond. + /// + /// The value in Nanosecond. + /// A new Latency instance. + /// Thrown when the resulting magnitude would be negative. + public static Latency FromNanosecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Time unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ITimeUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Duration. + public static implicit operator Duration(Latency value) => Duration.Create(value.Value); +/// Explicit conversion from Duration. + public static explicit operator Latency(Duration value) => Create(value.Value); +/// Creates a Latency from a Duration value. + public static Latency From(Duration value) => Create(value.Value); +/// Subtracts two Latency values, returning the absolute difference as a non-negative Latency. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Latency operator -(Latency left, Latency right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Length.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Length.g.cs new file mode 100644 index 0000000..e78c441 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Length.g.cs @@ -0,0 +1,156 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Length dimension. +/// +/// The numeric storage type. +public partial record Length : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Length Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Length; + + /// + /// Creates a new from a value in Meter. + /// + /// The value in Meter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Length FromMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Kilometer. + /// + /// The value in Kilometer. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Length FromKilometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new from a value in Centimeter. + /// + /// The value in Centimeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Length FromCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Centi)), nameof(value))); +/// + /// Creates a new from a value in Millimeter. + /// + /// The value in Millimeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Length FromMillimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new from a value in Micrometer. + /// + /// The value in Micrometer. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Length FromMicrometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new from a value in Nanometer. + /// + /// The value in Nanometer. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Length FromNanometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Creates a new from a value in Angstrom. + /// + /// The value in Angstrom. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Length FromAngstrom(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AngstromToMeters)), nameof(value))); +/// + /// Creates a new from a value in Foot. + /// + /// The value in Foot. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Length FromFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FeetToMeters)), nameof(value))); +/// + /// Creates a new from a value in Inch. + /// + /// The value in Inch. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Length FromInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.InchesToMeters)), nameof(value))); +/// + /// Creates a new from a value in Yard. + /// + /// The value in Yard. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Length FromYard(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YardToMeters)), nameof(value))); +/// + /// Creates a new from a value in Mile. + /// + /// The value in Mile. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Length FromMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MileToMeters)), nameof(value))); +/// + /// Creates a new from a value in NauticalMile. + /// + /// The value in NauticalMile. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Length FromNauticalMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.NauticalMileToMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Length unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILengthUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Length values, returning the absolute difference as a non-negative Length. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Length operator -(Length left, Length right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Length by Length to produce Area. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Area operator *(Length left, Length right) => Multiply>(left, right); +/// + /// Multiplies Length by Area to produce Volume. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Volume operator *(Length left, Area right) => Multiply>(left, right); +/// + /// Divides Length by Duration to produce Speed. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Speed operator /(Length left, Duration right) => Divide>(left, right); +/// + /// Divides Length by Speed to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(Length left, Speed right) => Divide>(left, right); +/// + /// Multiplies Length by Frequency to produce Speed. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Speed operator *(Length left, Frequency right) => Multiply>(left, right); +/// + /// Multiplies Length by ForceMagnitude to produce Energy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Energy operator *(Length left, ForceMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Length by ElectricFieldMagnitude to produce VoltageMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VoltageMagnitude operator *(Length left, ElectricFieldMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Length by ElectricField1D to produce Voltage. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Voltage operator *(Length left, ElectricField1D right) => Multiply>(left, right); +/// + /// Multiplies Length by SurfaceTension to produce ForceMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ForceMagnitude operator *(Length left, SurfaceTension right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Lift.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Lift.g.cs new file mode 100644 index 0000000..2ba8f63 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Lift.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Force perpendicular to flow direction. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Lift : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Lift Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Force; + + /// + /// Creates a new Lift from a value in Newton. + /// + /// The value in Newton. + /// A new Lift instance. + /// Thrown when the resulting magnitude would be negative. + public static Lift FromNewton(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Lift from a value in Kilonewton. + /// + /// The value in Kilonewton. + /// A new Lift instance. + /// Thrown when the resulting magnitude would be negative. + public static Lift FromKilonewton(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Lift from a value in Dyne. + /// + /// The value in Dyne. + /// A new Lift instance. + /// Thrown when the resulting magnitude would be negative. + public static Lift FromDyne(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DyneToNewtons)), nameof(value))); +/// + /// Creates a new Lift from a value in PoundForce. + /// + /// The value in PoundForce. + /// A new Lift instance. + /// Thrown when the resulting magnitude would be negative. + public static Lift FromPoundForce(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PoundForceToNewtons)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Force unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IForceUnit unit) => unit.FromBase(Value); +/// Implicit conversion to ForceMagnitude. + public static implicit operator ForceMagnitude(Lift value) => ForceMagnitude.Create(value.Value); +/// Explicit conversion from ForceMagnitude. + public static explicit operator Lift(ForceMagnitude value) => Create(value.Value); +/// Creates a Lift from a ForceMagnitude value. + public static Lift From(ForceMagnitude value) => Create(value.Value); +/// Subtracts two Lift values, returning the absolute difference as a non-negative Lift. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Lift operator -(Lift left, Lift right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Loudness.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Loudness.g.cs new file mode 100644 index 0000000..64acf6d --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Loudness.g.cs @@ -0,0 +1,43 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Loudness dimension. +/// +/// The numeric storage type. +public partial record Loudness : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Loudness Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Loudness; + + /// + /// Creates a new from a value in Sone. + /// + /// The value in Sone. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Loudness FromSone(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Loudness unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILoudnessUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Loudness values, returning the absolute difference as a non-negative Loudness. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Loudness operator -(Loudness left, Loudness right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Luminance.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Luminance.g.cs new file mode 100644 index 0000000..ccc32d6 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Luminance.g.cs @@ -0,0 +1,61 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Luminance dimension. +/// +/// The numeric storage type. +public partial record Luminance : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Luminance Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Luminance; + + /// + /// Creates a new from a value in CandelaPerSquareMeter. + /// + /// The value in CandelaPerSquareMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Luminance FromCandelaPerSquareMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Nit. + /// + /// The value in Nit. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Luminance FromNit(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in FootLambert. + /// + /// The value in FootLambert. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Luminance FromFootLambert(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FootLambertToCandelaPerSquareMeter)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Luminance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILuminanceUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Luminance values, returning the absolute difference as a non-negative Luminance. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Luminance operator -(Luminance left, Luminance right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Luminance by Area to produce LuminousIntensity. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static LuminousIntensity operator *(Luminance left, Area right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/LuminousFlux.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/LuminousFlux.g.cs new file mode 100644 index 0000000..10202b3 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/LuminousFlux.g.cs @@ -0,0 +1,51 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the LuminousFlux dimension. +/// +/// The numeric storage type. +public partial record LuminousFlux : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static LuminousFlux Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.LuminousFlux; + + /// + /// Creates a new from a value in Lumen. + /// + /// The value in Lumen. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static LuminousFlux FromLumen(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-LuminousFlux unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILuminousFluxUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two LuminousFlux values, returning the absolute difference as a non-negative LuminousFlux. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static LuminousFlux operator -(LuminousFlux left, LuminousFlux right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides LuminousFlux by Area to produce Illuminance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Illuminance operator /(LuminousFlux left, Area right) => Divide>(left, right); +/// + /// Divides LuminousFlux by Illuminance to produce Area. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Area operator /(LuminousFlux left, Illuminance right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/LuminousIntensity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/LuminousIntensity.g.cs new file mode 100644 index 0000000..eaad46b --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/LuminousIntensity.g.cs @@ -0,0 +1,58 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the LuminousIntensity dimension. +/// +/// The numeric storage type. +public partial record LuminousIntensity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static LuminousIntensity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.LuminousIntensity; + + /// + /// Creates a new from a value in Candela. + /// + /// The value in Candela. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static LuminousIntensity FromCandela(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Millicandela. + /// + /// The value in Millicandela. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static LuminousIntensity FromMillicandela(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-LuminousIntensity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILuminousIntensityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two LuminousIntensity values, returning the absolute difference as a non-negative LuminousIntensity. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static LuminousIntensity operator -(LuminousIntensity left, LuminousIntensity right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides LuminousIntensity by Area to produce Luminance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Luminance operator /(LuminousIntensity left, Area right) => Divide>(left, right); +/// + /// Divides LuminousIntensity by Luminance to produce Area. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Area operator /(LuminousIntensity left, Luminance right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MachNumber.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MachNumber.g.cs new file mode 100644 index 0000000..82e06c8 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MachNumber.g.cs @@ -0,0 +1,110 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Ratio of flow velocity to local speed of sound. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record MachNumber : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static MachNumber Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// + /// Creates a new MachNumber from a value in Dimensionless. + /// + /// The value in Dimensionless. + /// A new MachNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static MachNumber FromDimensionless(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new MachNumber from a value in Radian. + /// + /// The value in Radian. + /// A new MachNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static MachNumber FromRadian(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new MachNumber from a value in Degree. + /// + /// The value in Degree. + /// A new MachNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static MachNumber FromDegree(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians)), nameof(value))); +/// + /// Creates a new MachNumber from a value in Gradian. + /// + /// The value in Gradian. + /// A new MachNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static MachNumber FromGradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians)), nameof(value))); +/// + /// Creates a new MachNumber from a value in Revolution. + /// + /// The value in Revolution. + /// A new MachNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static MachNumber FromRevolution(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians)), nameof(value))); +/// + /// Creates a new MachNumber from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new MachNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static MachNumber FromMilliradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new MachNumber from a value in Percent. + /// + /// The value in Percent. + /// A new MachNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static MachNumber FromPercent(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentToRatio)), nameof(value))); +/// + /// Creates a new MachNumber from a value in PartPerMillion. + /// + /// The value in PartPerMillion. + /// A new MachNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static MachNumber FromPartPerMillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerMillionToRatio)), nameof(value))); +/// + /// Creates a new MachNumber from a value in PartPerBillion. + /// + /// The value in PartPerBillion. + /// A new MachNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static MachNumber FromPartPerBillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerBillionToRatio)), nameof(value))); +/// + /// Creates a new MachNumber from a value in PercentByWeight. + /// + /// The value in PercentByWeight. + /// A new MachNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static MachNumber FromPercentByWeight(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentByWeightToRatio)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Dimensionless unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IDimensionlessUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Ratio. + public static implicit operator Ratio(MachNumber value) => Ratio.Create(value.Value); +/// Explicit conversion from Ratio. + public static explicit operator MachNumber(Ratio value) => Create(value.Value); +/// Creates a MachNumber from a Ratio value. + public static MachNumber From(Ratio value) => Create(value.Value); +/// Subtracts two MachNumber values, returning the absolute difference as a non-negative MachNumber. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MachNumber operator -(MachNumber left, MachNumber right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MagneticFlux.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MagneticFlux.g.cs new file mode 100644 index 0000000..0adf4c0 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MagneticFlux.g.cs @@ -0,0 +1,63 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the MagneticFlux dimension. +/// +/// The numeric storage type. +public partial record MagneticFlux : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static MagneticFlux Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.MagneticFlux; + + /// + /// Creates a new from a value in Weber. + /// + /// The value in Weber. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static MagneticFlux FromWeber(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-MagneticFlux unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IMagneticFluxUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two MagneticFlux values, returning the absolute difference as a non-negative MagneticFlux. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MagneticFlux operator -(MagneticFlux left, MagneticFlux right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides MagneticFlux by Area to produce MagneticFluxDensityMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MagneticFluxDensityMagnitude operator /(MagneticFlux left, Area right) => Divide>(left, right); +/// + /// Divides MagneticFlux by MagneticFluxDensityMagnitude to produce Area. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Area operator /(MagneticFlux left, MagneticFluxDensityMagnitude right) => Divide>(left, right); +/// + /// Divides MagneticFlux by Duration to produce VoltageMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VoltageMagnitude operator /(MagneticFlux left, Duration right) => Divide>(left, right); +/// + /// Divides MagneticFlux by CurrentMagnitude to produce Inductance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Inductance operator /(MagneticFlux left, CurrentMagnitude right) => Divide>(left, right); +/// + /// Divides MagneticFlux by Inductance to produce CurrentMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static CurrentMagnitude operator /(MagneticFlux left, Inductance right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MagneticFluxDensity3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MagneticFluxDensity3D.g.cs new file mode 100644 index 0000000..860734e --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MagneticFluxDensity3D.g.cs @@ -0,0 +1,112 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of MagneticFluxDensity. +/// +/// The numeric component type. +public partial record MagneticFluxDensity3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static MagneticFluxDensity3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static MagneticFluxDensity3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static MagneticFluxDensity3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static MagneticFluxDensity3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static MagneticFluxDensity3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public MagneticFluxDensityMagnitude Magnitude() => MagneticFluxDensityMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(MagneticFluxDensity3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public MagneticFluxDensity3D Cross(MagneticFluxDensity3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(MagneticFluxDensity3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(MagneticFluxDensity3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public MagneticFluxDensity3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static MagneticFluxDensity3D operator +(MagneticFluxDensity3D left, MagneticFluxDensity3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static MagneticFluxDensity3D operator -(MagneticFluxDensity3D left, MagneticFluxDensity3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static MagneticFluxDensity3D operator *(MagneticFluxDensity3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static MagneticFluxDensity3D operator *(T scalar, MagneticFluxDensity3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static MagneticFluxDensity3D operator /(MagneticFluxDensity3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static MagneticFluxDensity3D operator -(MagneticFluxDensity3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MagneticFluxDensityMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MagneticFluxDensityMagnitude.g.cs new file mode 100644 index 0000000..7b938c9 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MagneticFluxDensityMagnitude.g.cs @@ -0,0 +1,54 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the MagneticFluxDensity dimension. +/// +/// The numeric storage type. +public partial record MagneticFluxDensityMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static MagneticFluxDensityMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.MagneticFluxDensity; + + /// + /// Creates a new from a value in Tesla. + /// + /// The value in Tesla. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static MagneticFluxDensityMagnitude FromTesla(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Gauss. + /// + /// The value in Gauss. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static MagneticFluxDensityMagnitude FromGauss(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GaussToTesla)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-MagneticFluxDensity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IMagneticFluxDensityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two MagneticFluxDensityMagnitude values, returning the absolute difference as a non-negative MagneticFluxDensityMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MagneticFluxDensityMagnitude operator -(MagneticFluxDensityMagnitude left, MagneticFluxDensityMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies MagneticFluxDensityMagnitude by Area to produce MagneticFlux. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MagneticFlux operator *(MagneticFluxDensityMagnitude left, Area right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Mass.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Mass.g.cs new file mode 100644 index 0000000..24e5830 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Mass.g.cs @@ -0,0 +1,128 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Mass dimension. +/// +/// The numeric storage type. +public partial record Mass : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Mass Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Mass; + + /// + /// Creates a new from a value in Kilogram. + /// + /// The value in Kilogram. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Mass FromKilogram(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Gram. + /// + /// The value in Gram. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Mass FromGram(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new from a value in Ton. + /// + /// The value in Ton. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Mass FromTon(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.TonToKilograms)), nameof(value))); +/// + /// Creates a new from a value in Pound. + /// + /// The value in Pound. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Mass FromPound(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PoundToKilograms)), nameof(value))); +/// + /// Creates a new from a value in Ounce. + /// + /// The value in Ounce. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Mass FromOunce(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.OunceToKilograms)), nameof(value))); +/// + /// Creates a new from a value in Stone. + /// + /// The value in Stone. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Mass FromStone(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.StoneToKilograms)), nameof(value))); +/// + /// Creates a new from a value in ShortTon. + /// + /// The value in ShortTon. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Mass FromShortTon(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ShortTonToKilograms)), nameof(value))); +/// + /// Creates a new from a value in AtomicMassUnit. + /// + /// The value in AtomicMassUnit. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Mass FromAtomicMassUnit(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AtomicMassUnitToKilograms)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Mass unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IMassUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Mass values, returning the absolute difference as a non-negative Mass. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Mass operator -(Mass left, Mass right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Mass by AccelerationMagnitude to produce ForceMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ForceMagnitude operator *(Mass left, AccelerationMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Mass by Speed to produce MomentumMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MomentumMagnitude operator *(Mass left, Speed right) => Multiply>(left, right); +/// + /// Divides Mass by Volume to produce Density. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Density operator /(Mass left, Volume right) => Divide>(left, right); +/// + /// Divides Mass by Density to produce Volume. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Volume operator /(Mass left, Density right) => Divide>(left, right); +/// + /// Multiplies Mass by SpecificHeat to produce Entropy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Entropy operator *(Mass left, SpecificHeat right) => Multiply>(left, right); +/// + /// Divides Mass by Duration to produce MassFlowRate. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MassFlowRate operator /(Mass left, Duration right) => Divide>(left, right); +/// + /// Divides Mass by MassFlowRate to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(Mass left, MassFlowRate right) => Divide>(left, right); +/// + /// Divides Mass by AmountOfSubstance to produce MolarMass. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MolarMass operator /(Mass left, AmountOfSubstance right) => Divide>(left, right); +/// + /// Divides Mass by MolarMass to produce AmountOfSubstance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AmountOfSubstance operator /(Mass left, MolarMass right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MassFlowRate.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MassFlowRate.g.cs new file mode 100644 index 0000000..1f4fcc3 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MassFlowRate.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the MassFlowRate dimension. +/// +/// The numeric storage type. +public partial record MassFlowRate : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static MassFlowRate Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.MassFlowRate; + + /// + /// Creates a new from a value in KilogramPerSecond. + /// + /// The value in KilogramPerSecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static MassFlowRate FromKilogramPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-MassFlowRate unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IMassFlowRateUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two MassFlowRate values, returning the absolute difference as a non-negative MassFlowRate. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MassFlowRate operator -(MassFlowRate left, MassFlowRate right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies MassFlowRate by Duration to produce Mass. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Mass operator *(MassFlowRate left, Duration right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MolarEnergy.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MolarEnergy.g.cs new file mode 100644 index 0000000..762220f --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MolarEnergy.g.cs @@ -0,0 +1,61 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the MolarEnergy dimension. +/// +/// The numeric storage type. +public partial record MolarEnergy : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static MolarEnergy Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.MolarEnergy; + + /// + /// Creates a new from a value in JoulePerMole. + /// + /// The value in JoulePerMole. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static MolarEnergy FromJoulePerMole(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in KilojoulePerMole. + /// + /// The value in KilojoulePerMole. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static MolarEnergy FromKilojoulePerMole(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilojoulePerMoleToJoulePerMole)), nameof(value))); +/// + /// Creates a new from a value in CaloriePerMole. + /// + /// The value in CaloriePerMole. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static MolarEnergy FromCaloriePerMole(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CaloriePerMoleToJoulePerMole)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-MolarEnergy unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IMolarEnergyUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two MolarEnergy values, returning the absolute difference as a non-negative MolarEnergy. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MolarEnergy operator -(MolarEnergy left, MolarEnergy right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies MolarEnergy by AmountOfSubstance to produce Energy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Energy operator *(MolarEnergy left, AmountOfSubstance right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MolarEnthalpy.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MolarEnthalpy.g.cs new file mode 100644 index 0000000..8661b52 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MolarEnthalpy.g.cs @@ -0,0 +1,61 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Enthalpy change per mole of substance. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record MolarEnthalpy : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static MolarEnthalpy Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.MolarEnergy; + + /// + /// Creates a new MolarEnthalpy from a value in JoulePerMole. + /// + /// The value in JoulePerMole. + /// A new MolarEnthalpy instance. + /// Thrown when the resulting magnitude would be negative. + public static MolarEnthalpy FromJoulePerMole(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new MolarEnthalpy from a value in KilojoulePerMole. + /// + /// The value in KilojoulePerMole. + /// A new MolarEnthalpy instance. + /// Thrown when the resulting magnitude would be negative. + public static MolarEnthalpy FromKilojoulePerMole(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilojoulePerMoleToJoulePerMole)), nameof(value))); +/// + /// Creates a new MolarEnthalpy from a value in CaloriePerMole. + /// + /// The value in CaloriePerMole. + /// A new MolarEnthalpy instance. + /// Thrown when the resulting magnitude would be negative. + public static MolarEnthalpy FromCaloriePerMole(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CaloriePerMoleToJoulePerMole)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-MolarEnergy unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IMolarEnergyUnit unit) => unit.FromBase(Value); +/// Implicit conversion to MolarEnergy. + public static implicit operator MolarEnergy(MolarEnthalpy value) => MolarEnergy.Create(value.Value); +/// Explicit conversion from MolarEnergy. + public static explicit operator MolarEnthalpy(MolarEnergy value) => Create(value.Value); +/// Creates a MolarEnthalpy from a MolarEnergy value. + public static MolarEnthalpy From(MolarEnergy value) => Create(value.Value); +/// Subtracts two MolarEnthalpy values, returning the absolute difference as a non-negative MolarEnthalpy. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MolarEnthalpy operator -(MolarEnthalpy left, MolarEnthalpy right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MolarMass.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MolarMass.g.cs new file mode 100644 index 0000000..672b880 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MolarMass.g.cs @@ -0,0 +1,61 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the MolarMass dimension. +/// +/// The numeric storage type. +public partial record MolarMass : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static MolarMass Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.MolarMass; + + /// + /// Creates a new from a value in KilogramPerMole. + /// + /// The value in KilogramPerMole. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static MolarMass FromKilogramPerMole(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in GramPerMole. + /// + /// The value in GramPerMole. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static MolarMass FromGramPerMole(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GramPerMoleToKilogramPerMole)), nameof(value))); +/// + /// Creates a new from a value in Dalton. + /// + /// The value in Dalton. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static MolarMass FromDalton(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GramPerMoleToKilogramPerMole)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-MolarMass unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IMolarMassUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two MolarMass values, returning the absolute difference as a non-negative MolarMass. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MolarMass operator -(MolarMass left, MolarMass right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies MolarMass by AmountOfSubstance to produce Mass. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Mass operator *(MolarMass left, AmountOfSubstance right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MomentOfInertia.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MomentOfInertia.g.cs new file mode 100644 index 0000000..c228f0d --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MomentOfInertia.g.cs @@ -0,0 +1,51 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the MomentOfInertia dimension. +/// +/// The numeric storage type. +public partial record MomentOfInertia : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static MomentOfInertia Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.MomentOfInertia; + + /// + /// Creates a new from a value in KilogramMeterSquared. + /// + /// The value in KilogramMeterSquared. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static MomentOfInertia FromKilogramMeterSquared(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-MomentOfInertia unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IMomentOfInertiaUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two MomentOfInertia values, returning the absolute difference as a non-negative MomentOfInertia. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MomentOfInertia operator -(MomentOfInertia left, MomentOfInertia right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies MomentOfInertia by AngularSpeed to produce AngularMomentumMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularMomentumMagnitude operator *(MomentOfInertia left, AngularSpeed right) => Multiply>(left, right); +/// + /// Multiplies MomentOfInertia by AngularAccelerationMagnitude to produce TorqueMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static TorqueMagnitude operator *(MomentOfInertia left, AngularAccelerationMagnitude right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Momentum1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Momentum1D.g.cs new file mode 100644 index 0000000..4982360 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Momentum1D.g.cs @@ -0,0 +1,46 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the Momentum dimension. +/// +/// The numeric storage type. +public partial record Momentum1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Momentum1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Momentum; + + /// + /// Creates a new from a value in NewtonSecond. + /// + /// The value in NewtonSecond. + /// A new instance. + public static Momentum1D FromNewtonSecond(T value) => Create(value); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Momentum unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IMomentumUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public MomentumMagnitude Magnitude() => MomentumMagnitude.Create(T.Abs(Value)); +/// + /// Divides Momentum1D by Duration to produce Force1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Force1D operator /(Momentum1D left, Duration right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Momentum2D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Momentum2D.g.cs new file mode 100644 index 0000000..c9abeef --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Momentum2D.g.cs @@ -0,0 +1,101 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 2D vector representation of Momentum. +/// +/// The numeric component type. +public partial record Momentum2D : IVector2, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets a vector with all components set to zero. + public static Momentum2D Zero => new() { X = T.Zero, Y = T.Zero }; + + /// Gets a vector with all components set to one. + public static Momentum2D One => new() { X = T.One, Y = T.One }; + + /// Gets the unit vector for the X-axis. + public static Momentum2D UnitX => new() { X = T.One, Y = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Momentum2D UnitY => new() { X = T.Zero, Y = T.One }; + + /// Gets the magnitude as a . + public MomentumMagnitude Magnitude() => MomentumMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y); + + /// Calculates the dot product of two vectors. + public T Dot(Momentum2D other) => (X * other.X) + (Y * other.Y); + + /// Calculates the distance between two vectors. + public T Distance(Momentum2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T sum = (dX * dX) + (dY * dY); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Momentum2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + return (dX * dX) + (dY * dY); + } + + /// Returns a normalized version of the vector. + public Momentum2D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len }; + } + + /// Adds two vectors. + public static Momentum2D operator +(Momentum2D left, Momentum2D right) => new() { X = left.X + right.X, Y = left.Y + right.Y }; + + /// Subtracts two vectors. + public static Momentum2D operator -(Momentum2D left, Momentum2D right) => new() { X = left.X - right.X, Y = left.Y - right.Y }; + + /// Multiplies a vector by a scalar. + public static Momentum2D operator *(Momentum2D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar }; + + /// Multiplies a scalar by a vector. + public static Momentum2D operator *(T scalar, Momentum2D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y }; + + /// Divides a vector by a scalar. + public static Momentum2D operator /(Momentum2D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar }; + + /// Negates a vector. + public static Momentum2D operator -(Momentum2D vector) => new() { X = -vector.X, Y = -vector.Y }; + /// Momentum2D / Duration = Force2D. + public static Force2D operator /(Momentum2D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Momentum3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Momentum3D.g.cs new file mode 100644 index 0000000..aea4961 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Momentum3D.g.cs @@ -0,0 +1,115 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of Momentum. +/// +/// The numeric component type. +public partial record Momentum3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static Momentum3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static Momentum3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static Momentum3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Momentum3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Momentum3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public MomentumMagnitude Magnitude() => MomentumMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(Momentum3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public Momentum3D Cross(Momentum3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(Momentum3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Momentum3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public Momentum3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static Momentum3D operator +(Momentum3D left, Momentum3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static Momentum3D operator -(Momentum3D left, Momentum3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static Momentum3D operator *(Momentum3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static Momentum3D operator *(T scalar, Momentum3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static Momentum3D operator /(Momentum3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static Momentum3D operator -(Momentum3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Momentum3D / Duration = Force3D. + public static Force3D operator /(Momentum3D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Momentum4D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Momentum4D.g.cs new file mode 100644 index 0000000..85136d7 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Momentum4D.g.cs @@ -0,0 +1,117 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 4D vector representation of Momentum. +/// +/// The numeric component type. +public partial record Momentum4D : IVector4, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets the W component. + public T W { get; init; } + + /// Gets a vector with all components set to zero. + public static Momentum4D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets a vector with all components set to one. + public static Momentum4D One => new() { X = T.One, Y = T.One, Z = T.One, W = T.One }; + + /// Gets the unit vector for the X-axis. + public static Momentum4D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Momentum4D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Momentum4D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One, W = T.Zero }; + + /// Gets the unit vector for the W-axis. + public static Momentum4D UnitW => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.One }; + + /// Gets the magnitude as a . + public MomentumMagnitude Magnitude() => MomentumMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z) + (W * W); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z) + (W * W); + + /// Calculates the dot product of two vectors. + public T Dot(Momentum4D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z) + (W * other.W); + + /// Calculates the distance between two vectors. + public T Distance(Momentum4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Momentum4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + return (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + } + + /// Returns a normalized version of the vector. + public Momentum4D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len, W = W / len }; + } + + /// Adds two vectors. + public static Momentum4D operator +(Momentum4D left, Momentum4D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z, W = left.W + right.W }; + + /// Subtracts two vectors. + public static Momentum4D operator -(Momentum4D left, Momentum4D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z, W = left.W - right.W }; + + /// Multiplies a vector by a scalar. + public static Momentum4D operator *(Momentum4D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar, W = vector.W * scalar }; + + /// Multiplies a scalar by a vector. + public static Momentum4D operator *(T scalar, Momentum4D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z, W = scalar * vector.W }; + + /// Divides a vector by a scalar. + public static Momentum4D operator /(Momentum4D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar, W = vector.W / scalar }; + + /// Negates a vector. + public static Momentum4D operator -(Momentum4D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z, W = -vector.W }; + /// Momentum4D / Duration = Force4D. + public static Force4D operator /(Momentum4D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value, W = left.W / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MomentumMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MomentumMagnitude.g.cs new file mode 100644 index 0000000..93a621a --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/MomentumMagnitude.g.cs @@ -0,0 +1,59 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Momentum dimension. +/// +/// The numeric storage type. +public partial record MomentumMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static MomentumMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Momentum; + + /// + /// Creates a new from a value in NewtonSecond. + /// + /// The value in NewtonSecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static MomentumMagnitude FromNewtonSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Momentum unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IMomentumUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two MomentumMagnitude values, returning the absolute difference as a non-negative MomentumMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MomentumMagnitude operator -(MomentumMagnitude left, MomentumMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides MomentumMagnitude by Speed to produce Mass. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Mass operator /(MomentumMagnitude left, Speed right) => Divide>(left, right); +/// + /// Divides MomentumMagnitude by Mass to produce Speed. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Speed operator /(MomentumMagnitude left, Mass right) => Divide>(left, right); +/// + /// Divides MomentumMagnitude by Duration to produce ForceMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ForceMagnitude operator /(MomentumMagnitude left, Duration right) => Divide>(left, right); +/// + /// Divides MomentumMagnitude by ForceMagnitude to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(MomentumMagnitude left, ForceMagnitude right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/NoiseReductionCoefficient.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/NoiseReductionCoefficient.g.cs new file mode 100644 index 0000000..1fb6cfa --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/NoiseReductionCoefficient.g.cs @@ -0,0 +1,110 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Average sound absorption across standard frequencies. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record NoiseReductionCoefficient : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static NoiseReductionCoefficient Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// + /// Creates a new NoiseReductionCoefficient from a value in Dimensionless. + /// + /// The value in Dimensionless. + /// A new NoiseReductionCoefficient instance. + /// Thrown when the resulting magnitude would be negative. + public static NoiseReductionCoefficient FromDimensionless(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new NoiseReductionCoefficient from a value in Radian. + /// + /// The value in Radian. + /// A new NoiseReductionCoefficient instance. + /// Thrown when the resulting magnitude would be negative. + public static NoiseReductionCoefficient FromRadian(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new NoiseReductionCoefficient from a value in Degree. + /// + /// The value in Degree. + /// A new NoiseReductionCoefficient instance. + /// Thrown when the resulting magnitude would be negative. + public static NoiseReductionCoefficient FromDegree(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians)), nameof(value))); +/// + /// Creates a new NoiseReductionCoefficient from a value in Gradian. + /// + /// The value in Gradian. + /// A new NoiseReductionCoefficient instance. + /// Thrown when the resulting magnitude would be negative. + public static NoiseReductionCoefficient FromGradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians)), nameof(value))); +/// + /// Creates a new NoiseReductionCoefficient from a value in Revolution. + /// + /// The value in Revolution. + /// A new NoiseReductionCoefficient instance. + /// Thrown when the resulting magnitude would be negative. + public static NoiseReductionCoefficient FromRevolution(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians)), nameof(value))); +/// + /// Creates a new NoiseReductionCoefficient from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new NoiseReductionCoefficient instance. + /// Thrown when the resulting magnitude would be negative. + public static NoiseReductionCoefficient FromMilliradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new NoiseReductionCoefficient from a value in Percent. + /// + /// The value in Percent. + /// A new NoiseReductionCoefficient instance. + /// Thrown when the resulting magnitude would be negative. + public static NoiseReductionCoefficient FromPercent(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentToRatio)), nameof(value))); +/// + /// Creates a new NoiseReductionCoefficient from a value in PartPerMillion. + /// + /// The value in PartPerMillion. + /// A new NoiseReductionCoefficient instance. + /// Thrown when the resulting magnitude would be negative. + public static NoiseReductionCoefficient FromPartPerMillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerMillionToRatio)), nameof(value))); +/// + /// Creates a new NoiseReductionCoefficient from a value in PartPerBillion. + /// + /// The value in PartPerBillion. + /// A new NoiseReductionCoefficient instance. + /// Thrown when the resulting magnitude would be negative. + public static NoiseReductionCoefficient FromPartPerBillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerBillionToRatio)), nameof(value))); +/// + /// Creates a new NoiseReductionCoefficient from a value in PercentByWeight. + /// + /// The value in PercentByWeight. + /// A new NoiseReductionCoefficient instance. + /// Thrown when the resulting magnitude would be negative. + public static NoiseReductionCoefficient FromPercentByWeight(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentByWeightToRatio)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Dimensionless unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IDimensionlessUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Ratio. + public static implicit operator Ratio(NoiseReductionCoefficient value) => Ratio.Create(value.Value); +/// Explicit conversion from Ratio. + public static explicit operator NoiseReductionCoefficient(Ratio value) => Create(value.Value); +/// Creates a NoiseReductionCoefficient from a Ratio value. + public static NoiseReductionCoefficient From(Ratio value) => Create(value.Value); +/// Subtracts two NoiseReductionCoefficient values, returning the absolute difference as a non-negative NoiseReductionCoefficient. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static NoiseReductionCoefficient operator -(NoiseReductionCoefficient left, NoiseReductionCoefficient right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/NormalForce.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/NormalForce.g.cs new file mode 100644 index 0000000..eb55464 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/NormalForce.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Contact force perpendicular to a surface. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record NormalForce : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static NormalForce Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Force; + + /// + /// Creates a new NormalForce from a value in Newton. + /// + /// The value in Newton. + /// A new NormalForce instance. + /// Thrown when the resulting magnitude would be negative. + public static NormalForce FromNewton(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new NormalForce from a value in Kilonewton. + /// + /// The value in Kilonewton. + /// A new NormalForce instance. + /// Thrown when the resulting magnitude would be negative. + public static NormalForce FromKilonewton(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new NormalForce from a value in Dyne. + /// + /// The value in Dyne. + /// A new NormalForce instance. + /// Thrown when the resulting magnitude would be negative. + public static NormalForce FromDyne(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DyneToNewtons)), nameof(value))); +/// + /// Creates a new NormalForce from a value in PoundForce. + /// + /// The value in PoundForce. + /// A new NormalForce instance. + /// Thrown when the resulting magnitude would be negative. + public static NormalForce FromPoundForce(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PoundForceToNewtons)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Force unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IForceUnit unit) => unit.FromBase(Value); +/// Implicit conversion to ForceMagnitude. + public static implicit operator ForceMagnitude(NormalForce value) => ForceMagnitude.Create(value.Value); +/// Explicit conversion from ForceMagnitude. + public static explicit operator NormalForce(ForceMagnitude value) => Create(value.Value); +/// Creates a NormalForce from a ForceMagnitude value. + public static NormalForce From(ForceMagnitude value) => Create(value.Value); +/// Subtracts two NormalForce values, returning the absolute difference as a non-negative NormalForce. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static NormalForce operator -(NormalForce left, NormalForce right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/NuclearCrossSection.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/NuclearCrossSection.g.cs new file mode 100644 index 0000000..e7eba36 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/NuclearCrossSection.g.cs @@ -0,0 +1,50 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the NuclearCrossSection dimension. +/// +/// The numeric storage type. +public partial record NuclearCrossSection : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static NuclearCrossSection Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.NuclearCrossSection; + + /// + /// Creates a new from a value in SquareMeter. + /// + /// The value in SquareMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static NuclearCrossSection FromSquareMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Barn. + /// + /// The value in Barn. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static NuclearCrossSection FromBarn(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BarnToSquareMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-NuclearCrossSection unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.INuclearCrossSectionUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two NuclearCrossSection values, returning the absolute difference as a non-negative NuclearCrossSection. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static NuclearCrossSection operator -(NuclearCrossSection left, NuclearCrossSection right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Offset.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Offset.g.cs new file mode 100644 index 0000000..af7d0ce --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Offset.g.cs @@ -0,0 +1,110 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed distance from a reference point along one axis. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Offset : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Offset Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Length; + + /// + /// Creates a new Offset from a value in Meter. + /// + /// The value in Meter. + /// A new Offset instance. + public static Offset FromMeter(T value) => Create(value); +/// + /// Creates a new Offset from a value in Kilometer. + /// + /// The value in Kilometer. + /// A new Offset instance. + public static Offset FromKilometer(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Kilo))); +/// + /// Creates a new Offset from a value in Centimeter. + /// + /// The value in Centimeter. + /// A new Offset instance. + public static Offset FromCentimeter(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Centi))); +/// + /// Creates a new Offset from a value in Millimeter. + /// + /// The value in Millimeter. + /// A new Offset instance. + public static Offset FromMillimeter(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Milli))); +/// + /// Creates a new Offset from a value in Micrometer. + /// + /// The value in Micrometer. + /// A new Offset instance. + public static Offset FromMicrometer(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Micro))); +/// + /// Creates a new Offset from a value in Nanometer. + /// + /// The value in Nanometer. + /// A new Offset instance. + public static Offset FromNanometer(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Nano))); +/// + /// Creates a new Offset from a value in Angstrom. + /// + /// The value in Angstrom. + /// A new Offset instance. + public static Offset FromAngstrom(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.AngstromToMeters))); +/// + /// Creates a new Offset from a value in Foot. + /// + /// The value in Foot. + /// A new Offset instance. + public static Offset FromFoot(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.FeetToMeters))); +/// + /// Creates a new Offset from a value in Inch. + /// + /// The value in Inch. + /// A new Offset instance. + public static Offset FromInch(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.InchesToMeters))); +/// + /// Creates a new Offset from a value in Yard. + /// + /// The value in Yard. + /// A new Offset instance. + public static Offset FromYard(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.YardToMeters))); +/// + /// Creates a new Offset from a value in Mile. + /// + /// The value in Mile. + /// A new Offset instance. + public static Offset FromMile(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.MileToMeters))); +/// + /// Creates a new Offset from a value in NauticalMile. + /// + /// The value in NauticalMile. + /// A new Offset instance. + public static Offset FromNauticalMile(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.NauticalMileToMeters))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Length unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILengthUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Displacement1D. + public static implicit operator Displacement1D(Offset value) => Displacement1D.Create(value.Value); +/// Explicit conversion from Displacement1D. + public static explicit operator Offset(Displacement1D value) => Create(value.Value); +/// Creates a Offset from a Displacement1D value. + public static Offset From(Displacement1D value) => Create(value.Value); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/OpticalPower.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/OpticalPower.g.cs new file mode 100644 index 0000000..cceebbe --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/OpticalPower.g.cs @@ -0,0 +1,43 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the OpticalPower dimension. +/// +/// The numeric storage type. +public partial record OpticalPower : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static OpticalPower Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.OpticalPower; + + /// + /// Creates a new from a value in Diopter. + /// + /// The value in Diopter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static OpticalPower FromDiopter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-OpticalPower unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IOpticalPowerUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two OpticalPower values, returning the absolute difference as a non-negative OpticalPower. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static OpticalPower operator -(OpticalPower left, OpticalPower right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Perimeter.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Perimeter.g.cs new file mode 100644 index 0000000..fa6f0c0 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Perimeter.g.cs @@ -0,0 +1,124 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Total boundary length of a 2D shape. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Perimeter : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Perimeter Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Length; + + /// + /// Creates a new Perimeter from a value in Meter. + /// + /// The value in Meter. + /// A new Perimeter instance. + /// Thrown when the resulting magnitude would be negative. + public static Perimeter FromMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Perimeter from a value in Kilometer. + /// + /// The value in Kilometer. + /// A new Perimeter instance. + /// Thrown when the resulting magnitude would be negative. + public static Perimeter FromKilometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Perimeter from a value in Centimeter. + /// + /// The value in Centimeter. + /// A new Perimeter instance. + /// Thrown when the resulting magnitude would be negative. + public static Perimeter FromCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Centi)), nameof(value))); +/// + /// Creates a new Perimeter from a value in Millimeter. + /// + /// The value in Millimeter. + /// A new Perimeter instance. + /// Thrown when the resulting magnitude would be negative. + public static Perimeter FromMillimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Perimeter from a value in Micrometer. + /// + /// The value in Micrometer. + /// A new Perimeter instance. + /// Thrown when the resulting magnitude would be negative. + public static Perimeter FromMicrometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new Perimeter from a value in Nanometer. + /// + /// The value in Nanometer. + /// A new Perimeter instance. + /// Thrown when the resulting magnitude would be negative. + public static Perimeter FromNanometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Creates a new Perimeter from a value in Angstrom. + /// + /// The value in Angstrom. + /// A new Perimeter instance. + /// Thrown when the resulting magnitude would be negative. + public static Perimeter FromAngstrom(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AngstromToMeters)), nameof(value))); +/// + /// Creates a new Perimeter from a value in Foot. + /// + /// The value in Foot. + /// A new Perimeter instance. + /// Thrown when the resulting magnitude would be negative. + public static Perimeter FromFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FeetToMeters)), nameof(value))); +/// + /// Creates a new Perimeter from a value in Inch. + /// + /// The value in Inch. + /// A new Perimeter instance. + /// Thrown when the resulting magnitude would be negative. + public static Perimeter FromInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.InchesToMeters)), nameof(value))); +/// + /// Creates a new Perimeter from a value in Yard. + /// + /// The value in Yard. + /// A new Perimeter instance. + /// Thrown when the resulting magnitude would be negative. + public static Perimeter FromYard(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YardToMeters)), nameof(value))); +/// + /// Creates a new Perimeter from a value in Mile. + /// + /// The value in Mile. + /// A new Perimeter instance. + /// Thrown when the resulting magnitude would be negative. + public static Perimeter FromMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MileToMeters)), nameof(value))); +/// + /// Creates a new Perimeter from a value in NauticalMile. + /// + /// The value in NauticalMile. + /// A new Perimeter instance. + /// Thrown when the resulting magnitude would be negative. + public static Perimeter FromNauticalMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.NauticalMileToMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Length unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILengthUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Length. + public static implicit operator Length(Perimeter value) => Length.Create(value.Value); +/// Explicit conversion from Length. + public static explicit operator Perimeter(Length value) => Create(value.Value); +/// Creates a Perimeter from a Length value. + public static Perimeter From(Length value) => Create(value.Value); +/// Subtracts two Perimeter values, returning the absolute difference as a non-negative Perimeter. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Perimeter operator -(Perimeter left, Perimeter right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Period.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Period.g.cs new file mode 100644 index 0000000..99f31e9 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Period.g.cs @@ -0,0 +1,103 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Time interval for one complete cycle. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Period : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Period Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Time; + + /// + /// Creates a new Period from a value in Second. + /// + /// The value in Second. + /// A new Period instance. + /// Thrown when the resulting magnitude would be negative. + public static Period FromSecond(T value) => Create(Vector0Guards.EnsurePositive(value, nameof(value))); +/// + /// Creates a new Period from a value in Millisecond. + /// + /// The value in Millisecond. + /// A new Period instance. + /// Thrown when the resulting magnitude would be negative. + public static Period FromMillisecond(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Period from a value in Microsecond. + /// + /// The value in Microsecond. + /// A new Period instance. + /// Thrown when the resulting magnitude would be negative. + public static Period FromMicrosecond(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new Period from a value in Minute. + /// + /// The value in Minute. + /// A new Period instance. + /// Thrown when the resulting magnitude would be negative. + public static Period FromMinute(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.MinuteToSeconds)), nameof(value))); +/// + /// Creates a new Period from a value in Hour. + /// + /// The value in Hour. + /// A new Period instance. + /// Thrown when the resulting magnitude would be negative. + public static Period FromHour(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.HourToSeconds)), nameof(value))); +/// + /// Creates a new Period from a value in Day. + /// + /// The value in Day. + /// A new Period instance. + /// Thrown when the resulting magnitude would be negative. + public static Period FromDay(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.DayToSeconds)), nameof(value))); +/// + /// Creates a new Period from a value in Year. + /// + /// The value in Year. + /// A new Period instance. + /// Thrown when the resulting magnitude would be negative. + public static Period FromYear(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.YearToSeconds)), nameof(value))); +/// + /// Creates a new Period from a value in Week. + /// + /// The value in Week. + /// A new Period instance. + /// Thrown when the resulting magnitude would be negative. + public static Period FromWeek(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.WeekToSeconds)), nameof(value))); +/// + /// Creates a new Period from a value in Nanosecond. + /// + /// The value in Nanosecond. + /// A new Period instance. + /// Thrown when the resulting magnitude would be negative. + public static Period FromNanosecond(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Time unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ITimeUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Duration. + public static implicit operator Duration(Period value) => Duration.Create(value.Value); +/// Explicit conversion from Duration. + public static explicit operator Period(Duration value) => Create(value.Value); +/// Creates a Period from a Duration value. + public static Period From(Duration value) => Create(value.Value); +/// Subtracts two Period values, returning the absolute difference as a non-negative Period. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Period operator -(Period left, Period right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Permittivity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Permittivity.g.cs new file mode 100644 index 0000000..ad31c13 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Permittivity.g.cs @@ -0,0 +1,43 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Permittivity dimension. +/// +/// The numeric storage type. +public partial record Permittivity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Permittivity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Permittivity; + + /// + /// Creates a new from a value in FaradPerMeter. + /// + /// The value in FaradPerMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Permittivity FromFaradPerMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Permittivity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IPermittivityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Permittivity values, returning the absolute difference as a non-negative Permittivity. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Permittivity operator -(Permittivity left, Permittivity right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Phase.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Phase.g.cs new file mode 100644 index 0000000..03d67b5 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Phase.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Phase angle of a periodic signal. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Phase : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Phase Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularDisplacement; + + /// + /// Creates a new Phase from a value in Radian. + /// + /// The value in Radian. + /// A new Phase instance. + public static Phase FromRadian(T value) => Create(value); +/// + /// Creates a new Phase from a value in Degree. + /// + /// The value in Degree. + /// A new Phase instance. + public static Phase FromDegree(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians))); +/// + /// Creates a new Phase from a value in Gradian. + /// + /// The value in Gradian. + /// A new Phase instance. + public static Phase FromGradian(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians))); +/// + /// Creates a new Phase from a value in Revolution. + /// + /// The value in Revolution. + /// A new Phase instance. + public static Phase FromRevolution(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians))); +/// + /// Creates a new Phase from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new Phase instance. + public static Phase FromMilliradian(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Milli))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularDisplacement unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularDisplacementUnit unit) => unit.FromBase(Value); +/// Implicit conversion to SignedAngle. + public static implicit operator SignedAngle(Phase value) => SignedAngle.Create(value.Value); +/// Explicit conversion from SignedAngle. + public static explicit operator Phase(SignedAngle value) => Create(value.Value); +/// Creates a Phase from a SignedAngle value. + public static Phase From(SignedAngle value) => Create(value.Value); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/PhaseVelocity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/PhaseVelocity.g.cs new file mode 100644 index 0000000..33fffad --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/PhaseVelocity.g.cs @@ -0,0 +1,75 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Speed at which a wave phase propagates. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record PhaseVelocity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static PhaseVelocity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// + /// Creates a new PhaseVelocity from a value in MeterPerSecond. + /// + /// The value in MeterPerSecond. + /// A new PhaseVelocity instance. + /// Thrown when the resulting magnitude would be negative. + public static PhaseVelocity FromMeterPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new PhaseVelocity from a value in KilometerPerHour. + /// + /// The value in KilometerPerHour. + /// A new PhaseVelocity instance. + /// Thrown when the resulting magnitude would be negative. + public static PhaseVelocity FromKilometerPerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilometerPerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new PhaseVelocity from a value in MilePerHour. + /// + /// The value in MilePerHour. + /// A new PhaseVelocity instance. + /// Thrown when the resulting magnitude would be negative. + public static PhaseVelocity FromMilePerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MilePerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new PhaseVelocity from a value in FootPerSecond. + /// + /// The value in FootPerSecond. + /// A new PhaseVelocity instance. + /// Thrown when the resulting magnitude would be negative. + public static PhaseVelocity FromFootPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FootPerSecondToMeterPerSecond)), nameof(value))); +/// + /// Creates a new PhaseVelocity from a value in Knot. + /// + /// The value in Knot. + /// A new PhaseVelocity instance. + /// Thrown when the resulting magnitude would be negative. + public static PhaseVelocity FromKnot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KnotToMeterPerSecond)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Velocity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IVelocityUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Speed. + public static implicit operator Speed(PhaseVelocity value) => Speed.Create(value.Value); +/// Explicit conversion from Speed. + public static explicit operator PhaseVelocity(Speed value) => Create(value.Value); +/// Creates a PhaseVelocity from a Speed value. + public static PhaseVelocity From(Speed value) => Create(value.Value); +/// Subtracts two PhaseVelocity values, returning the absolute difference as a non-negative PhaseVelocity. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static PhaseVelocity operator -(PhaseVelocity left, PhaseVelocity right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Pitch.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Pitch.g.cs new file mode 100644 index 0000000..f4be1a9 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Pitch.g.cs @@ -0,0 +1,61 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Perceived fundamental frequency of a sound. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Pitch : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Pitch Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Frequency; + + /// + /// Creates a new Pitch from a value in Hertz. + /// + /// The value in Hertz. + /// A new Pitch instance. + /// Thrown when the resulting magnitude would be negative. + public static Pitch FromHertz(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Pitch from a value in Kilohertz. + /// + /// The value in Kilohertz. + /// A new Pitch instance. + /// Thrown when the resulting magnitude would be negative. + public static Pitch FromKilohertz(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Pitch from a value in Megahertz. + /// + /// The value in Megahertz. + /// A new Pitch instance. + /// Thrown when the resulting magnitude would be negative. + public static Pitch FromMegahertz(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Mega)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Frequency unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IFrequencyUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Frequency. + public static implicit operator Frequency(Pitch value) => Frequency.Create(value.Value); +/// Explicit conversion from Frequency. + public static explicit operator Pitch(Frequency value) => Create(value.Value); +/// Creates a Pitch from a Frequency value. + public static Pitch From(Frequency value) => Create(value.Value); +/// Subtracts two Pitch values, returning the absolute difference as a non-negative Pitch. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Pitch operator -(Pitch left, Pitch right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Position3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Position3D.g.cs new file mode 100644 index 0000000..bec9fe4 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Position3D.g.cs @@ -0,0 +1,115 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// Location in 3D space relative to an origin. +/// Semantic overload of . +/// +public partial record Position3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static Position3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static Position3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static Position3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Position3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Position3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(Position3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public Position3D Cross(Position3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(Position3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Position3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public Position3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static Position3D operator +(Position3D left, Position3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static Position3D operator -(Position3D left, Position3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static Position3D operator *(Position3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static Position3D operator *(T scalar, Position3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static Position3D operator /(Position3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static Position3D operator -(Position3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Implicit conversion to Displacement3D. + public static implicit operator Displacement3D(Position3D value) => new() { X = value.X, Y = value.Y, Z = value.Z }; + + /// Explicit conversion from Displacement3D. + public static explicit operator Position3D(Displacement3D value) => new() { X = value.X, Y = value.Y, Z = value.Z }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/PotentialEnergy.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/PotentialEnergy.g.cs new file mode 100644 index 0000000..78ed67a --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/PotentialEnergy.g.cs @@ -0,0 +1,103 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Energy of position or configuration. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record PotentialEnergy : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static PotentialEnergy Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// + /// Creates a new PotentialEnergy from a value in Joule. + /// + /// The value in Joule. + /// A new PotentialEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static PotentialEnergy FromJoule(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new PotentialEnergy from a value in Kilojoule. + /// + /// The value in Kilojoule. + /// A new PotentialEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static PotentialEnergy FromKilojoule(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new PotentialEnergy from a value in ElectronVolt. + /// + /// The value in ElectronVolt. + /// A new PotentialEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static PotentialEnergy FromElectronVolt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ElectronVoltToJoules)), nameof(value))); +/// + /// Creates a new PotentialEnergy from a value in Calorie. + /// + /// The value in Calorie. + /// A new PotentialEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static PotentialEnergy FromCalorie(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CalorieToJoules)), nameof(value))); +/// + /// Creates a new PotentialEnergy from a value in Kilocalorie. + /// + /// The value in Kilocalorie. + /// A new PotentialEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static PotentialEnergy FromKilocalorie(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilocalorieToJoules)), nameof(value))); +/// + /// Creates a new PotentialEnergy from a value in KilowattHour. + /// + /// The value in KilowattHour. + /// A new PotentialEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static PotentialEnergy FromKilowattHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilowattHourToJoules)), nameof(value))); +/// + /// Creates a new PotentialEnergy from a value in WattHour. + /// + /// The value in WattHour. + /// A new PotentialEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static PotentialEnergy FromWattHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.WattHourToJoules)), nameof(value))); +/// + /// Creates a new PotentialEnergy from a value in Erg. + /// + /// The value in Erg. + /// A new PotentialEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static PotentialEnergy FromErg(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ErgToJoules)), nameof(value))); +/// + /// Creates a new PotentialEnergy from a value in Btu. + /// + /// The value in Btu. + /// A new PotentialEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static PotentialEnergy FromBtu(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BtuToJoules)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Energy unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IEnergyUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Energy. + public static implicit operator Energy(PotentialEnergy value) => Energy.Create(value.Value); +/// Explicit conversion from Energy. + public static explicit operator PotentialEnergy(Energy value) => Create(value.Value); +/// Creates a PotentialEnergy from a Energy value. + public static PotentialEnergy From(Energy value) => Create(value.Value); +/// Subtracts two PotentialEnergy values, returning the absolute difference as a non-negative PotentialEnergy. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static PotentialEnergy operator -(PotentialEnergy left, PotentialEnergy right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Power.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Power.g.cs new file mode 100644 index 0000000..d9b2c72 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Power.g.cs @@ -0,0 +1,100 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Power dimension. +/// +/// The numeric storage type. +public partial record Power : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Power Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Power; + + /// + /// Creates a new from a value in Watt. + /// + /// The value in Watt. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Power FromWatt(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Kilowatt. + /// + /// The value in Kilowatt. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Power FromKilowatt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new from a value in Megawatt. + /// + /// The value in Megawatt. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Power FromMegawatt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Mega)), nameof(value))); +/// + /// Creates a new from a value in Horsepower. + /// + /// The value in Horsepower. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Power FromHorsepower(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.HorsepowerToWatts)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Power unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IPowerUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Power values, returning the absolute difference as a non-negative Power. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Power operator -(Power left, Power right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides Power by VoltageMagnitude to produce CurrentMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static CurrentMagnitude operator /(Power left, VoltageMagnitude right) => Divide>(left, right); +/// + /// Divides Power by CurrentMagnitude to produce VoltageMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VoltageMagnitude operator /(Power left, CurrentMagnitude right) => Divide>(left, right); +/// + /// Divides Power by Speed to produce ForceMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ForceMagnitude operator /(Power left, Speed right) => Divide>(left, right); +/// + /// Divides Power by ForceMagnitude to produce Speed. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Speed operator /(Power left, ForceMagnitude right) => Divide>(left, right); +/// + /// Multiplies Power by Duration to produce Energy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Energy operator *(Power left, Duration right) => Multiply>(left, right); +/// + /// Divides Power by Area to produce Irradiance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Irradiance operator /(Power left, Area right) => Divide>(left, right); +/// + /// Divides Power by Irradiance to produce Area. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Area operator /(Power left, Irradiance right) => Divide>(left, right); +/// + /// Divides Power by Volume to produce ElectricPowerDensity. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ElectricPowerDensity operator /(Power left, Volume right) => Divide>(left, right); +/// + /// Divides Power by ElectricPowerDensity to produce Volume. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Volume operator /(Power left, ElectricPowerDensity right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Pressure.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Pressure.g.cs new file mode 100644 index 0000000..8384da1 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Pressure.g.cs @@ -0,0 +1,90 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Pressure dimension. +/// +/// The numeric storage type. +public partial record Pressure : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Pressure Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// + /// Creates a new from a value in Pascal. + /// + /// The value in Pascal. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Pressure FromPascal(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Kilopascal. + /// + /// The value in Kilopascal. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Pressure FromKilopascal(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new from a value in Bar. + /// + /// The value in Bar. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Pressure FromBar(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BarToPascals)), nameof(value))); +/// + /// Creates a new from a value in Atmosphere. + /// + /// The value in Atmosphere. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Pressure FromAtmosphere(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AtmosphereToPascals)), nameof(value))); +/// + /// Creates a new from a value in Psi. + /// + /// The value in Psi. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Pressure FromPsi(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PsiToPascals)), nameof(value))); +/// + /// Creates a new from a value in Torr. + /// + /// The value in Torr. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Pressure FromTorr(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.TorrToPascals)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Pressure unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IPressureUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Pressure values, returning the absolute difference as a non-negative Pressure. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Pressure operator -(Pressure left, Pressure right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Pressure by Area to produce ForceMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ForceMagnitude operator *(Pressure left, Area right) => Multiply>(left, right); +/// + /// Multiplies Pressure by Volume to produce Energy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Energy operator *(Pressure left, Volume right) => Multiply>(left, right); +/// + /// Multiplies Pressure by Sensitivity to produce VoltageMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VoltageMagnitude operator *(Pressure left, Sensitivity right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/RadioactiveActivity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/RadioactiveActivity.g.cs new file mode 100644 index 0000000..4b7f81a --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/RadioactiveActivity.g.cs @@ -0,0 +1,50 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the RadioactiveActivity dimension. +/// +/// The numeric storage type. +public partial record RadioactiveActivity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static RadioactiveActivity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.RadioactiveActivity; + + /// + /// Creates a new from a value in Becquerel. + /// + /// The value in Becquerel. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static RadioactiveActivity FromBecquerel(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Curie. + /// + /// The value in Curie. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static RadioactiveActivity FromCurie(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CurieToBecquerels)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-RadioactiveActivity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IRadioactiveActivityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two RadioactiveActivity values, returning the absolute difference as a non-negative RadioactiveActivity. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static RadioactiveActivity operator -(RadioactiveActivity left, RadioactiveActivity right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Radius.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Radius.g.cs new file mode 100644 index 0000000..c3272c2 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Radius.g.cs @@ -0,0 +1,124 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Distance from center to edge of a circle or sphere. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Radius : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Radius Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Length; + + /// + /// Creates a new Radius from a value in Meter. + /// + /// The value in Meter. + /// A new Radius instance. + /// Thrown when the resulting magnitude would be negative. + public static Radius FromMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Radius from a value in Kilometer. + /// + /// The value in Kilometer. + /// A new Radius instance. + /// Thrown when the resulting magnitude would be negative. + public static Radius FromKilometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Radius from a value in Centimeter. + /// + /// The value in Centimeter. + /// A new Radius instance. + /// Thrown when the resulting magnitude would be negative. + public static Radius FromCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Centi)), nameof(value))); +/// + /// Creates a new Radius from a value in Millimeter. + /// + /// The value in Millimeter. + /// A new Radius instance. + /// Thrown when the resulting magnitude would be negative. + public static Radius FromMillimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Radius from a value in Micrometer. + /// + /// The value in Micrometer. + /// A new Radius instance. + /// Thrown when the resulting magnitude would be negative. + public static Radius FromMicrometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new Radius from a value in Nanometer. + /// + /// The value in Nanometer. + /// A new Radius instance. + /// Thrown when the resulting magnitude would be negative. + public static Radius FromNanometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Creates a new Radius from a value in Angstrom. + /// + /// The value in Angstrom. + /// A new Radius instance. + /// Thrown when the resulting magnitude would be negative. + public static Radius FromAngstrom(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AngstromToMeters)), nameof(value))); +/// + /// Creates a new Radius from a value in Foot. + /// + /// The value in Foot. + /// A new Radius instance. + /// Thrown when the resulting magnitude would be negative. + public static Radius FromFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FeetToMeters)), nameof(value))); +/// + /// Creates a new Radius from a value in Inch. + /// + /// The value in Inch. + /// A new Radius instance. + /// Thrown when the resulting magnitude would be negative. + public static Radius FromInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.InchesToMeters)), nameof(value))); +/// + /// Creates a new Radius from a value in Yard. + /// + /// The value in Yard. + /// A new Radius instance. + /// Thrown when the resulting magnitude would be negative. + public static Radius FromYard(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YardToMeters)), nameof(value))); +/// + /// Creates a new Radius from a value in Mile. + /// + /// The value in Mile. + /// A new Radius instance. + /// Thrown when the resulting magnitude would be negative. + public static Radius FromMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MileToMeters)), nameof(value))); +/// + /// Creates a new Radius from a value in NauticalMile. + /// + /// The value in NauticalMile. + /// A new Radius instance. + /// Thrown when the resulting magnitude would be negative. + public static Radius FromNauticalMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.NauticalMileToMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Length unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILengthUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Length. + public static implicit operator Length(Radius value) => Length.Create(value.Value); +/// Explicit conversion from Length. + public static explicit operator Radius(Length value) => Create(value.Value); +/// Creates a Radius from a Length value. + public static Radius From(Length value) => Create(value.Value); +/// Subtracts two Radius values, returning the absolute difference as a non-negative Radius. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Radius operator -(Radius left, Radius right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/RateConstant.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/RateConstant.g.cs new file mode 100644 index 0000000..ac67f4e --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/RateConstant.g.cs @@ -0,0 +1,43 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the RateConstant dimension. +/// +/// The numeric storage type. +public partial record RateConstant : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static RateConstant Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.RateConstant; + + /// + /// Creates a new from a value in PerSecond. + /// + /// The value in PerSecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static RateConstant FromPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-RateConstant unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IRateConstantUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two RateConstant values, returning the absolute difference as a non-negative RateConstant. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static RateConstant operator -(RateConstant left, RateConstant right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Ratio.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Ratio.g.cs new file mode 100644 index 0000000..e59afe3 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Ratio.g.cs @@ -0,0 +1,130 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Dimensionless dimension. +/// +/// The numeric storage type. +public partial record Ratio : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Ratio Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// + /// Creates a new from a value in Dimensionless. + /// + /// The value in Dimensionless. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Ratio FromDimensionless(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Radian. + /// + /// The value in Radian. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Ratio FromRadian(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Degree. + /// + /// The value in Degree. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Ratio FromDegree(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians)), nameof(value))); +/// + /// Creates a new from a value in Gradian. + /// + /// The value in Gradian. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Ratio FromGradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians)), nameof(value))); +/// + /// Creates a new from a value in Revolution. + /// + /// The value in Revolution. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Ratio FromRevolution(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians)), nameof(value))); +/// + /// Creates a new from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Ratio FromMilliradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new from a value in Percent. + /// + /// The value in Percent. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Ratio FromPercent(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentToRatio)), nameof(value))); +/// + /// Creates a new from a value in PartPerMillion. + /// + /// The value in PartPerMillion. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Ratio FromPartPerMillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerMillionToRatio)), nameof(value))); +/// + /// Creates a new from a value in PartPerBillion. + /// + /// The value in PartPerBillion. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Ratio FromPartPerBillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerBillionToRatio)), nameof(value))); +/// + /// Creates a new from a value in PercentByWeight. + /// + /// The value in PercentByWeight. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Ratio FromPercentByWeight(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentByWeightToRatio)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Dimensionless unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IDimensionlessUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Ratio values, returning the absolute difference as a non-negative Ratio. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Ratio operator -(Ratio left, Ratio right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Ratio by Ratio to produce Ratio. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Ratio operator *(Ratio left, Ratio right) => Multiply>(left, right); +/// + /// Multiplies Ratio by SignedRatio to produce SignedRatio. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SignedRatio operator *(Ratio left, SignedRatio right) => Multiply>(left, right); +/// + /// Divides Ratio by Duration to produce Frequency. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Frequency operator /(Ratio left, Duration right) => Divide>(left, right); +/// + /// Divides Ratio by Frequency to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(Ratio left, Frequency right) => Divide>(left, right); +/// + /// Divides Ratio by Temperature to produce ThermalExpansionCoefficient. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ThermalExpansionCoefficient operator /(Ratio left, Temperature right) => Divide>(left, right); +/// + /// Divides Ratio by ThermalExpansionCoefficient to produce Temperature. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Temperature operator /(Ratio left, ThermalExpansionCoefficient right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ReactionRate.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ReactionRate.g.cs new file mode 100644 index 0000000..1a78545 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ReactionRate.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ReactionRate dimension. +/// +/// The numeric storage type. +public partial record ReactionRate : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ReactionRate Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ReactionRate; + + /// + /// Creates a new from a value in MolePerCubicMeterSecond. + /// + /// The value in MolePerCubicMeterSecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ReactionRate FromMolePerCubicMeterSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ReactionRate unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IReactionRateUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two ReactionRate values, returning the absolute difference as a non-negative ReactionRate. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ReactionRate operator -(ReactionRate left, ReactionRate right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies ReactionRate by Duration to produce Concentration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Concentration operator *(ReactionRate left, Duration right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ReflectionCoefficient.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ReflectionCoefficient.g.cs new file mode 100644 index 0000000..23df483 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ReflectionCoefficient.g.cs @@ -0,0 +1,98 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed ratio of reflected to incident wave amplitude. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record ReflectionCoefficient : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ReflectionCoefficient Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// + /// Creates a new ReflectionCoefficient from a value in Dimensionless. + /// + /// The value in Dimensionless. + /// A new ReflectionCoefficient instance. + public static ReflectionCoefficient FromDimensionless(T value) => Create(value); +/// + /// Creates a new ReflectionCoefficient from a value in Radian. + /// + /// The value in Radian. + /// A new ReflectionCoefficient instance. + public static ReflectionCoefficient FromRadian(T value) => Create(value); +/// + /// Creates a new ReflectionCoefficient from a value in Degree. + /// + /// The value in Degree. + /// A new ReflectionCoefficient instance. + public static ReflectionCoefficient FromDegree(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians))); +/// + /// Creates a new ReflectionCoefficient from a value in Gradian. + /// + /// The value in Gradian. + /// A new ReflectionCoefficient instance. + public static ReflectionCoefficient FromGradian(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians))); +/// + /// Creates a new ReflectionCoefficient from a value in Revolution. + /// + /// The value in Revolution. + /// A new ReflectionCoefficient instance. + public static ReflectionCoefficient FromRevolution(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians))); +/// + /// Creates a new ReflectionCoefficient from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new ReflectionCoefficient instance. + public static ReflectionCoefficient FromMilliradian(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Milli))); +/// + /// Creates a new ReflectionCoefficient from a value in Percent. + /// + /// The value in Percent. + /// A new ReflectionCoefficient instance. + public static ReflectionCoefficient FromPercent(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.PercentToRatio))); +/// + /// Creates a new ReflectionCoefficient from a value in PartPerMillion. + /// + /// The value in PartPerMillion. + /// A new ReflectionCoefficient instance. + public static ReflectionCoefficient FromPartPerMillion(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.PartPerMillionToRatio))); +/// + /// Creates a new ReflectionCoefficient from a value in PartPerBillion. + /// + /// The value in PartPerBillion. + /// A new ReflectionCoefficient instance. + public static ReflectionCoefficient FromPartPerBillion(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.PartPerBillionToRatio))); +/// + /// Creates a new ReflectionCoefficient from a value in PercentByWeight. + /// + /// The value in PercentByWeight. + /// A new ReflectionCoefficient instance. + public static ReflectionCoefficient FromPercentByWeight(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.PercentByWeightToRatio))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Dimensionless unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IDimensionlessUnit unit) => unit.FromBase(Value); +/// Implicit conversion to SignedRatio. + public static implicit operator SignedRatio(ReflectionCoefficient value) => SignedRatio.Create(value.Value); +/// Explicit conversion from SignedRatio. + public static explicit operator ReflectionCoefficient(SignedRatio value) => Create(value.Value); +/// Creates a ReflectionCoefficient from a SignedRatio value. + public static ReflectionCoefficient From(SignedRatio value) => Create(value.Value); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/RefractiveIndex.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/RefractiveIndex.g.cs new file mode 100644 index 0000000..ff719be --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/RefractiveIndex.g.cs @@ -0,0 +1,110 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Ratio of speed of light in vacuum to speed in a medium. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record RefractiveIndex : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static RefractiveIndex Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// + /// Creates a new RefractiveIndex from a value in Dimensionless. + /// + /// The value in Dimensionless. + /// A new RefractiveIndex instance. + /// Thrown when the resulting magnitude would be negative. + public static RefractiveIndex FromDimensionless(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new RefractiveIndex from a value in Radian. + /// + /// The value in Radian. + /// A new RefractiveIndex instance. + /// Thrown when the resulting magnitude would be negative. + public static RefractiveIndex FromRadian(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new RefractiveIndex from a value in Degree. + /// + /// The value in Degree. + /// A new RefractiveIndex instance. + /// Thrown when the resulting magnitude would be negative. + public static RefractiveIndex FromDegree(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians)), nameof(value))); +/// + /// Creates a new RefractiveIndex from a value in Gradian. + /// + /// The value in Gradian. + /// A new RefractiveIndex instance. + /// Thrown when the resulting magnitude would be negative. + public static RefractiveIndex FromGradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians)), nameof(value))); +/// + /// Creates a new RefractiveIndex from a value in Revolution. + /// + /// The value in Revolution. + /// A new RefractiveIndex instance. + /// Thrown when the resulting magnitude would be negative. + public static RefractiveIndex FromRevolution(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians)), nameof(value))); +/// + /// Creates a new RefractiveIndex from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new RefractiveIndex instance. + /// Thrown when the resulting magnitude would be negative. + public static RefractiveIndex FromMilliradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new RefractiveIndex from a value in Percent. + /// + /// The value in Percent. + /// A new RefractiveIndex instance. + /// Thrown when the resulting magnitude would be negative. + public static RefractiveIndex FromPercent(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentToRatio)), nameof(value))); +/// + /// Creates a new RefractiveIndex from a value in PartPerMillion. + /// + /// The value in PartPerMillion. + /// A new RefractiveIndex instance. + /// Thrown when the resulting magnitude would be negative. + public static RefractiveIndex FromPartPerMillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerMillionToRatio)), nameof(value))); +/// + /// Creates a new RefractiveIndex from a value in PartPerBillion. + /// + /// The value in PartPerBillion. + /// A new RefractiveIndex instance. + /// Thrown when the resulting magnitude would be negative. + public static RefractiveIndex FromPartPerBillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerBillionToRatio)), nameof(value))); +/// + /// Creates a new RefractiveIndex from a value in PercentByWeight. + /// + /// The value in PercentByWeight. + /// A new RefractiveIndex instance. + /// Thrown when the resulting magnitude would be negative. + public static RefractiveIndex FromPercentByWeight(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentByWeightToRatio)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Dimensionless unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IDimensionlessUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Ratio. + public static implicit operator Ratio(RefractiveIndex value) => Ratio.Create(value.Value); +/// Explicit conversion from Ratio. + public static explicit operator RefractiveIndex(Ratio value) => Create(value.Value); +/// Creates a RefractiveIndex from a Ratio value. + public static RefractiveIndex From(Ratio value) => Create(value.Value); +/// Subtracts two RefractiveIndex values, returning the absolute difference as a non-negative RefractiveIndex. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static RefractiveIndex operator -(RefractiveIndex left, RefractiveIndex right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Resistance.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Resistance.g.cs new file mode 100644 index 0000000..1e5c204 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Resistance.g.cs @@ -0,0 +1,65 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ElectricResistance dimension. +/// +/// The numeric storage type. +public partial record Resistance : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Resistance Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricResistance; + + /// + /// Creates a new from a value in Ohm. + /// + /// The value in Ohm. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Resistance FromOhm(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Kilohm. + /// + /// The value in Kilohm. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Resistance FromKilohm(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new from a value in Megohm. + /// + /// The value in Megohm. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Resistance FromMegohm(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Mega)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricResistance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricResistanceUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Resistance values, returning the absolute difference as a non-negative Resistance. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Resistance operator -(Resistance left, Resistance right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Resistance by CurrentMagnitude to produce VoltageMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VoltageMagnitude operator *(Resistance left, CurrentMagnitude right) => Multiply>(left, right); +/// + /// Multiplies Resistance by Current1D to produce Voltage. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Voltage operator *(Resistance left, Current1D right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ReverberationTime.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ReverberationTime.g.cs new file mode 100644 index 0000000..718461c --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ReverberationTime.g.cs @@ -0,0 +1,103 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Time for sound to decay by 60 dB in an enclosed space. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record ReverberationTime : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ReverberationTime Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Time; + + /// + /// Creates a new ReverberationTime from a value in Second. + /// + /// The value in Second. + /// A new ReverberationTime instance. + /// Thrown when the resulting magnitude would be negative. + public static ReverberationTime FromSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new ReverberationTime from a value in Millisecond. + /// + /// The value in Millisecond. + /// A new ReverberationTime instance. + /// Thrown when the resulting magnitude would be negative. + public static ReverberationTime FromMillisecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new ReverberationTime from a value in Microsecond. + /// + /// The value in Microsecond. + /// A new ReverberationTime instance. + /// Thrown when the resulting magnitude would be negative. + public static ReverberationTime FromMicrosecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new ReverberationTime from a value in Minute. + /// + /// The value in Minute. + /// A new ReverberationTime instance. + /// Thrown when the resulting magnitude would be negative. + public static ReverberationTime FromMinute(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MinuteToSeconds)), nameof(value))); +/// + /// Creates a new ReverberationTime from a value in Hour. + /// + /// The value in Hour. + /// A new ReverberationTime instance. + /// Thrown when the resulting magnitude would be negative. + public static ReverberationTime FromHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.HourToSeconds)), nameof(value))); +/// + /// Creates a new ReverberationTime from a value in Day. + /// + /// The value in Day. + /// A new ReverberationTime instance. + /// Thrown when the resulting magnitude would be negative. + public static ReverberationTime FromDay(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DayToSeconds)), nameof(value))); +/// + /// Creates a new ReverberationTime from a value in Year. + /// + /// The value in Year. + /// A new ReverberationTime instance. + /// Thrown when the resulting magnitude would be negative. + public static ReverberationTime FromYear(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YearToSeconds)), nameof(value))); +/// + /// Creates a new ReverberationTime from a value in Week. + /// + /// The value in Week. + /// A new ReverberationTime instance. + /// Thrown when the resulting magnitude would be negative. + public static ReverberationTime FromWeek(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.WeekToSeconds)), nameof(value))); +/// + /// Creates a new ReverberationTime from a value in Nanosecond. + /// + /// The value in Nanosecond. + /// A new ReverberationTime instance. + /// Thrown when the resulting magnitude would be negative. + public static ReverberationTime FromNanosecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Time unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ITimeUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Duration. + public static implicit operator Duration(ReverberationTime value) => Duration.Create(value.Value); +/// Explicit conversion from Duration. + public static explicit operator ReverberationTime(Duration value) => Create(value.Value); +/// Creates a ReverberationTime from a Duration value. + public static ReverberationTime From(Duration value) => Create(value.Value); +/// Subtracts two ReverberationTime values, returning the absolute difference as a non-negative ReverberationTime. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ReverberationTime operator -(ReverberationTime left, ReverberationTime right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ReynoldsNumber.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ReynoldsNumber.g.cs new file mode 100644 index 0000000..faed2fd --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ReynoldsNumber.g.cs @@ -0,0 +1,110 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Ratio of inertial to viscous forces in fluid flow. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record ReynoldsNumber : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ReynoldsNumber Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// + /// Creates a new ReynoldsNumber from a value in Dimensionless. + /// + /// The value in Dimensionless. + /// A new ReynoldsNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static ReynoldsNumber FromDimensionless(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new ReynoldsNumber from a value in Radian. + /// + /// The value in Radian. + /// A new ReynoldsNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static ReynoldsNumber FromRadian(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new ReynoldsNumber from a value in Degree. + /// + /// The value in Degree. + /// A new ReynoldsNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static ReynoldsNumber FromDegree(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians)), nameof(value))); +/// + /// Creates a new ReynoldsNumber from a value in Gradian. + /// + /// The value in Gradian. + /// A new ReynoldsNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static ReynoldsNumber FromGradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians)), nameof(value))); +/// + /// Creates a new ReynoldsNumber from a value in Revolution. + /// + /// The value in Revolution. + /// A new ReynoldsNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static ReynoldsNumber FromRevolution(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians)), nameof(value))); +/// + /// Creates a new ReynoldsNumber from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new ReynoldsNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static ReynoldsNumber FromMilliradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new ReynoldsNumber from a value in Percent. + /// + /// The value in Percent. + /// A new ReynoldsNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static ReynoldsNumber FromPercent(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentToRatio)), nameof(value))); +/// + /// Creates a new ReynoldsNumber from a value in PartPerMillion. + /// + /// The value in PartPerMillion. + /// A new ReynoldsNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static ReynoldsNumber FromPartPerMillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerMillionToRatio)), nameof(value))); +/// + /// Creates a new ReynoldsNumber from a value in PartPerBillion. + /// + /// The value in PartPerBillion. + /// A new ReynoldsNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static ReynoldsNumber FromPartPerBillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerBillionToRatio)), nameof(value))); +/// + /// Creates a new ReynoldsNumber from a value in PercentByWeight. + /// + /// The value in PercentByWeight. + /// A new ReynoldsNumber instance. + /// Thrown when the resulting magnitude would be negative. + public static ReynoldsNumber FromPercentByWeight(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentByWeightToRatio)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Dimensionless unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IDimensionlessUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Ratio. + public static implicit operator Ratio(ReynoldsNumber value) => Ratio.Create(value.Value); +/// Explicit conversion from Ratio. + public static explicit operator ReynoldsNumber(Ratio value) => Create(value.Value); +/// Creates a ReynoldsNumber from a Ratio value. + public static ReynoldsNumber From(Ratio value) => Create(value.Value); +/// Subtracts two ReynoldsNumber values, returning the absolute difference as a non-negative ReynoldsNumber. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ReynoldsNumber operator -(ReynoldsNumber left, ReynoldsNumber right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Rotation.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Rotation.g.cs new file mode 100644 index 0000000..ecd74a8 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Rotation.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Amount of angular rotation applied. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Rotation : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Rotation Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularDisplacement; + + /// + /// Creates a new Rotation from a value in Radian. + /// + /// The value in Radian. + /// A new Rotation instance. + public static Rotation FromRadian(T value) => Create(value); +/// + /// Creates a new Rotation from a value in Degree. + /// + /// The value in Degree. + /// A new Rotation instance. + public static Rotation FromDegree(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians))); +/// + /// Creates a new Rotation from a value in Gradian. + /// + /// The value in Gradian. + /// A new Rotation instance. + public static Rotation FromGradian(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians))); +/// + /// Creates a new Rotation from a value in Revolution. + /// + /// The value in Revolution. + /// A new Rotation instance. + public static Rotation FromRevolution(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians))); +/// + /// Creates a new Rotation from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new Rotation instance. + public static Rotation FromMilliradian(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Milli))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularDisplacement unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularDisplacementUnit unit) => unit.FromBase(Value); +/// Implicit conversion to SignedAngle. + public static implicit operator SignedAngle(Rotation value) => SignedAngle.Create(value.Value); +/// Explicit conversion from SignedAngle. + public static explicit operator Rotation(SignedAngle value) => Create(value.Value); +/// Creates a Rotation from a SignedAngle value. + public static Rotation From(SignedAngle value) => Create(value.Value); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SamplingRate.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SamplingRate.g.cs new file mode 100644 index 0000000..0ea8d5f --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SamplingRate.g.cs @@ -0,0 +1,61 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Rate at which samples are taken. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record SamplingRate : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SamplingRate Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Frequency; + + /// + /// Creates a new SamplingRate from a value in Hertz. + /// + /// The value in Hertz. + /// A new SamplingRate instance. + /// Thrown when the resulting magnitude would be negative. + public static SamplingRate FromHertz(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new SamplingRate from a value in Kilohertz. + /// + /// The value in Kilohertz. + /// A new SamplingRate instance. + /// Thrown when the resulting magnitude would be negative. + public static SamplingRate FromKilohertz(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new SamplingRate from a value in Megahertz. + /// + /// The value in Megahertz. + /// A new SamplingRate instance. + /// Thrown when the resulting magnitude would be negative. + public static SamplingRate FromMegahertz(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Mega)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Frequency unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IFrequencyUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Frequency. + public static implicit operator Frequency(SamplingRate value) => Frequency.Create(value.Value); +/// Explicit conversion from Frequency. + public static explicit operator SamplingRate(Frequency value) => Create(value.Value); +/// Creates a SamplingRate from a Frequency value. + public static SamplingRate From(Frequency value) => Create(value.Value); +/// Subtracts two SamplingRate values, returning the absolute difference as a non-negative SamplingRate. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SamplingRate operator -(SamplingRate left, SamplingRate right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Sensitivity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Sensitivity.g.cs new file mode 100644 index 0000000..6303db7 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Sensitivity.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Sensitivity dimension. +/// +/// The numeric storage type. +public partial record Sensitivity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Sensitivity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Sensitivity; + + /// + /// Creates a new from a value in VoltPerPascal. + /// + /// The value in VoltPerPascal. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Sensitivity FromVoltPerPascal(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Sensitivity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ISensitivityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Sensitivity values, returning the absolute difference as a non-negative Sensitivity. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Sensitivity operator -(Sensitivity left, Sensitivity right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Sensitivity by Pressure to produce VoltageMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VoltageMagnitude operator *(Sensitivity left, Pressure right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Sharpness.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Sharpness.g.cs new file mode 100644 index 0000000..49acb1c --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Sharpness.g.cs @@ -0,0 +1,43 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Sharpness dimension. +/// +/// The numeric storage type. +public partial record Sharpness : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Sharpness Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Sharpness; + + /// + /// Creates a new from a value in Acum. + /// + /// The value in Acum. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Sharpness FromAcum(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Sharpness unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ISharpnessUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Sharpness values, returning the absolute difference as a non-negative Sharpness. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Sharpness operator -(Sharpness left, Sharpness right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ShearModulus.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ShearModulus.g.cs new file mode 100644 index 0000000..046008a --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ShearModulus.g.cs @@ -0,0 +1,82 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Ratio of shear stress to shear strain. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record ShearModulus : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ShearModulus Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// + /// Creates a new ShearModulus from a value in Pascal. + /// + /// The value in Pascal. + /// A new ShearModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static ShearModulus FromPascal(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new ShearModulus from a value in Kilopascal. + /// + /// The value in Kilopascal. + /// A new ShearModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static ShearModulus FromKilopascal(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new ShearModulus from a value in Bar. + /// + /// The value in Bar. + /// A new ShearModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static ShearModulus FromBar(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BarToPascals)), nameof(value))); +/// + /// Creates a new ShearModulus from a value in Atmosphere. + /// + /// The value in Atmosphere. + /// A new ShearModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static ShearModulus FromAtmosphere(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AtmosphereToPascals)), nameof(value))); +/// + /// Creates a new ShearModulus from a value in Psi. + /// + /// The value in Psi. + /// A new ShearModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static ShearModulus FromPsi(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PsiToPascals)), nameof(value))); +/// + /// Creates a new ShearModulus from a value in Torr. + /// + /// The value in Torr. + /// A new ShearModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static ShearModulus FromTorr(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.TorrToPascals)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Pressure unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IPressureUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Pressure. + public static implicit operator Pressure(ShearModulus value) => Pressure.Create(value.Value); +/// Explicit conversion from Pressure. + public static explicit operator ShearModulus(Pressure value) => Create(value.Value); +/// Creates a ShearModulus from a Pressure value. + public static ShearModulus From(Pressure value) => Create(value.Value); +/// Subtracts two ShearModulus values, returning the absolute difference as a non-negative ShearModulus. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ShearModulus operator -(ShearModulus left, ShearModulus right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SignedAngle.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SignedAngle.g.cs new file mode 100644 index 0000000..1ae6e90 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SignedAngle.g.cs @@ -0,0 +1,70 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the AngularDisplacement dimension. +/// +/// The numeric storage type. +public partial record SignedAngle : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SignedAngle Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.AngularDisplacement; + + /// + /// Creates a new from a value in Radian. + /// + /// The value in Radian. + /// A new instance. + public static SignedAngle FromRadian(T value) => Create(value); +/// + /// Creates a new from a value in Degree. + /// + /// The value in Degree. + /// A new instance. + public static SignedAngle FromDegree(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians))); +/// + /// Creates a new from a value in Gradian. + /// + /// The value in Gradian. + /// A new instance. + public static SignedAngle FromGradian(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians))); +/// + /// Creates a new from a value in Revolution. + /// + /// The value in Revolution. + /// A new instance. + public static SignedAngle FromRevolution(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians))); +/// + /// Creates a new from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new instance. + public static SignedAngle FromMilliradian(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Milli))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-AngularDisplacement unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAngularDisplacementUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public Angle Magnitude() => Angle.Create(T.Abs(Value)); +/// + /// Divides SignedAngle by Duration to produce AngularVelocity1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularVelocity1D operator /(SignedAngle left, Duration right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SignedRatio.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SignedRatio.g.cs new file mode 100644 index 0000000..4a83966 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SignedRatio.g.cs @@ -0,0 +1,104 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the Dimensionless dimension. +/// +/// The numeric storage type. +public partial record SignedRatio : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SignedRatio Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// + /// Creates a new from a value in Dimensionless. + /// + /// The value in Dimensionless. + /// A new instance. + public static SignedRatio FromDimensionless(T value) => Create(value); +/// + /// Creates a new from a value in Radian. + /// + /// The value in Radian. + /// A new instance. + public static SignedRatio FromRadian(T value) => Create(value); +/// + /// Creates a new from a value in Degree. + /// + /// The value in Degree. + /// A new instance. + public static SignedRatio FromDegree(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians))); +/// + /// Creates a new from a value in Gradian. + /// + /// The value in Gradian. + /// A new instance. + public static SignedRatio FromGradian(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians))); +/// + /// Creates a new from a value in Revolution. + /// + /// The value in Revolution. + /// A new instance. + public static SignedRatio FromRevolution(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians))); +/// + /// Creates a new from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new instance. + public static SignedRatio FromMilliradian(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Milli))); +/// + /// Creates a new from a value in Percent. + /// + /// The value in Percent. + /// A new instance. + public static SignedRatio FromPercent(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.PercentToRatio))); +/// + /// Creates a new from a value in PartPerMillion. + /// + /// The value in PartPerMillion. + /// A new instance. + public static SignedRatio FromPartPerMillion(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.PartPerMillionToRatio))); +/// + /// Creates a new from a value in PartPerBillion. + /// + /// The value in PartPerBillion. + /// A new instance. + public static SignedRatio FromPartPerBillion(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.PartPerBillionToRatio))); +/// + /// Creates a new from a value in PercentByWeight. + /// + /// The value in PercentByWeight. + /// A new instance. + public static SignedRatio FromPercentByWeight(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.PercentByWeightToRatio))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Dimensionless unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IDimensionlessUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public Ratio Magnitude() => Ratio.Create(T.Abs(Value)); +/// + /// Multiplies SignedRatio by Ratio to produce SignedRatio. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SignedRatio operator *(SignedRatio left, Ratio right) => Multiply>(left, right); +/// + /// Divides SignedRatio by Ratio to produce SignedRatio. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SignedRatio operator /(SignedRatio left, Ratio right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Snap1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Snap1D.g.cs new file mode 100644 index 0000000..3a15627 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Snap1D.g.cs @@ -0,0 +1,46 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the Snap dimension. +/// +/// The numeric storage type. +public partial record Snap1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Snap1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Snap; + + /// + /// Creates a new from a value in MeterPerSecondQuartic. + /// + /// The value in MeterPerSecondQuartic. + /// A new instance. + public static Snap1D FromMeterPerSecondQuartic(T value) => Create(value); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Snap unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ISnapUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public SnapMagnitude Magnitude() => SnapMagnitude.Create(T.Abs(Value)); +/// + /// Multiplies Snap1D by Duration to produce Jerk1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Jerk1D operator *(Snap1D left, Duration right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Snap2D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Snap2D.g.cs new file mode 100644 index 0000000..71d31b5 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Snap2D.g.cs @@ -0,0 +1,101 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 2D vector representation of Snap. +/// +/// The numeric component type. +public partial record Snap2D : IVector2, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets a vector with all components set to zero. + public static Snap2D Zero => new() { X = T.Zero, Y = T.Zero }; + + /// Gets a vector with all components set to one. + public static Snap2D One => new() { X = T.One, Y = T.One }; + + /// Gets the unit vector for the X-axis. + public static Snap2D UnitX => new() { X = T.One, Y = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Snap2D UnitY => new() { X = T.Zero, Y = T.One }; + + /// Gets the magnitude as a . + public SnapMagnitude Magnitude() => SnapMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y); + + /// Calculates the dot product of two vectors. + public T Dot(Snap2D other) => (X * other.X) + (Y * other.Y); + + /// Calculates the distance between two vectors. + public T Distance(Snap2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T sum = (dX * dX) + (dY * dY); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Snap2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + return (dX * dX) + (dY * dY); + } + + /// Returns a normalized version of the vector. + public Snap2D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len }; + } + + /// Adds two vectors. + public static Snap2D operator +(Snap2D left, Snap2D right) => new() { X = left.X + right.X, Y = left.Y + right.Y }; + + /// Subtracts two vectors. + public static Snap2D operator -(Snap2D left, Snap2D right) => new() { X = left.X - right.X, Y = left.Y - right.Y }; + + /// Multiplies a vector by a scalar. + public static Snap2D operator *(Snap2D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar }; + + /// Multiplies a scalar by a vector. + public static Snap2D operator *(T scalar, Snap2D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y }; + + /// Divides a vector by a scalar. + public static Snap2D operator /(Snap2D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar }; + + /// Negates a vector. + public static Snap2D operator -(Snap2D vector) => new() { X = -vector.X, Y = -vector.Y }; + /// Snap2D * Duration = Jerk2D. + public static Jerk2D operator *(Snap2D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Snap3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Snap3D.g.cs new file mode 100644 index 0000000..cdc26c0 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Snap3D.g.cs @@ -0,0 +1,115 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of Snap. +/// +/// The numeric component type. +public partial record Snap3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static Snap3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static Snap3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static Snap3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Snap3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Snap3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public SnapMagnitude Magnitude() => SnapMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(Snap3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public Snap3D Cross(Snap3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(Snap3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Snap3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public Snap3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static Snap3D operator +(Snap3D left, Snap3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static Snap3D operator -(Snap3D left, Snap3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static Snap3D operator *(Snap3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static Snap3D operator *(T scalar, Snap3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static Snap3D operator /(Snap3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static Snap3D operator -(Snap3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Snap3D * Duration = Jerk3D. + public static Jerk3D operator *(Snap3D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Snap4D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Snap4D.g.cs new file mode 100644 index 0000000..91afaef --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Snap4D.g.cs @@ -0,0 +1,117 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 4D vector representation of Snap. +/// +/// The numeric component type. +public partial record Snap4D : IVector4, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets the W component. + public T W { get; init; } + + /// Gets a vector with all components set to zero. + public static Snap4D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets a vector with all components set to one. + public static Snap4D One => new() { X = T.One, Y = T.One, Z = T.One, W = T.One }; + + /// Gets the unit vector for the X-axis. + public static Snap4D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Snap4D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Snap4D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One, W = T.Zero }; + + /// Gets the unit vector for the W-axis. + public static Snap4D UnitW => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.One }; + + /// Gets the magnitude as a . + public SnapMagnitude Magnitude() => SnapMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z) + (W * W); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z) + (W * W); + + /// Calculates the dot product of two vectors. + public T Dot(Snap4D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z) + (W * other.W); + + /// Calculates the distance between two vectors. + public T Distance(Snap4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Snap4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + return (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + } + + /// Returns a normalized version of the vector. + public Snap4D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len, W = W / len }; + } + + /// Adds two vectors. + public static Snap4D operator +(Snap4D left, Snap4D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z, W = left.W + right.W }; + + /// Subtracts two vectors. + public static Snap4D operator -(Snap4D left, Snap4D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z, W = left.W - right.W }; + + /// Multiplies a vector by a scalar. + public static Snap4D operator *(Snap4D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar, W = vector.W * scalar }; + + /// Multiplies a scalar by a vector. + public static Snap4D operator *(T scalar, Snap4D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z, W = scalar * vector.W }; + + /// Divides a vector by a scalar. + public static Snap4D operator /(Snap4D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar, W = vector.W / scalar }; + + /// Negates a vector. + public static Snap4D operator -(Snap4D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z, W = -vector.W }; + /// Snap4D * Duration = Jerk4D. + public static Jerk4D operator *(Snap4D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value, W = left.W * right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SnapMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SnapMagnitude.g.cs new file mode 100644 index 0000000..537ac4d --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SnapMagnitude.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Snap dimension. +/// +/// The numeric storage type. +public partial record SnapMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SnapMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Snap; + + /// + /// Creates a new from a value in MeterPerSecondQuartic. + /// + /// The value in MeterPerSecondQuartic. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static SnapMagnitude FromMeterPerSecondQuartic(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Snap unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ISnapUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two SnapMagnitude values, returning the absolute difference as a non-negative SnapMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SnapMagnitude operator -(SnapMagnitude left, SnapMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies SnapMagnitude by Duration to produce JerkMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static JerkMagnitude operator *(SnapMagnitude left, Duration right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundAbsorption.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundAbsorption.g.cs new file mode 100644 index 0000000..e795318 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundAbsorption.g.cs @@ -0,0 +1,110 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Fraction of incident sound energy absorbed by a surface. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record SoundAbsorption : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SoundAbsorption Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// + /// Creates a new SoundAbsorption from a value in Dimensionless. + /// + /// The value in Dimensionless. + /// A new SoundAbsorption instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundAbsorption FromDimensionless(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new SoundAbsorption from a value in Radian. + /// + /// The value in Radian. + /// A new SoundAbsorption instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundAbsorption FromRadian(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new SoundAbsorption from a value in Degree. + /// + /// The value in Degree. + /// A new SoundAbsorption instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundAbsorption FromDegree(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians)), nameof(value))); +/// + /// Creates a new SoundAbsorption from a value in Gradian. + /// + /// The value in Gradian. + /// A new SoundAbsorption instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundAbsorption FromGradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians)), nameof(value))); +/// + /// Creates a new SoundAbsorption from a value in Revolution. + /// + /// The value in Revolution. + /// A new SoundAbsorption instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundAbsorption FromRevolution(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians)), nameof(value))); +/// + /// Creates a new SoundAbsorption from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new SoundAbsorption instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundAbsorption FromMilliradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new SoundAbsorption from a value in Percent. + /// + /// The value in Percent. + /// A new SoundAbsorption instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundAbsorption FromPercent(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentToRatio)), nameof(value))); +/// + /// Creates a new SoundAbsorption from a value in PartPerMillion. + /// + /// The value in PartPerMillion. + /// A new SoundAbsorption instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundAbsorption FromPartPerMillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerMillionToRatio)), nameof(value))); +/// + /// Creates a new SoundAbsorption from a value in PartPerBillion. + /// + /// The value in PartPerBillion. + /// A new SoundAbsorption instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundAbsorption FromPartPerBillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerBillionToRatio)), nameof(value))); +/// + /// Creates a new SoundAbsorption from a value in PercentByWeight. + /// + /// The value in PercentByWeight. + /// A new SoundAbsorption instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundAbsorption FromPercentByWeight(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentByWeightToRatio)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Dimensionless unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IDimensionlessUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Ratio. + public static implicit operator Ratio(SoundAbsorption value) => Ratio.Create(value.Value); +/// Explicit conversion from Ratio. + public static explicit operator SoundAbsorption(Ratio value) => Create(value.Value); +/// Creates a SoundAbsorption from a Ratio value. + public static SoundAbsorption From(Ratio value) => Create(value.Value); +/// Subtracts two SoundAbsorption values, returning the absolute difference as a non-negative SoundAbsorption. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SoundAbsorption operator -(SoundAbsorption left, SoundAbsorption right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundIntensity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundIntensity.g.cs new file mode 100644 index 0000000..ae77a06 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundIntensity.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Power per unit area carried by a sound wave. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record SoundIntensity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SoundIntensity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Irradiance; + + /// + /// Creates a new SoundIntensity from a value in WattPerSquareMeter. + /// + /// The value in WattPerSquareMeter. + /// A new SoundIntensity instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundIntensity FromWattPerSquareMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Irradiance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IIrradianceUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Irradiance. + public static implicit operator Irradiance(SoundIntensity value) => Irradiance.Create(value.Value); +/// Explicit conversion from Irradiance. + public static explicit operator SoundIntensity(Irradiance value) => Create(value.Value); +/// Creates a SoundIntensity from a Irradiance value. + public static SoundIntensity From(Irradiance value) => Create(value.Value); +/// Subtracts two SoundIntensity values, returning the absolute difference as a non-negative SoundIntensity. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SoundIntensity operator -(SoundIntensity left, SoundIntensity right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundPower.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundPower.g.cs new file mode 100644 index 0000000..60f344f --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundPower.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Acoustic energy emitted by a source per unit time. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record SoundPower : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SoundPower Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Power; + + /// + /// Creates a new SoundPower from a value in Watt. + /// + /// The value in Watt. + /// A new SoundPower instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundPower FromWatt(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new SoundPower from a value in Kilowatt. + /// + /// The value in Kilowatt. + /// A new SoundPower instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundPower FromKilowatt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new SoundPower from a value in Megawatt. + /// + /// The value in Megawatt. + /// A new SoundPower instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundPower FromMegawatt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Mega)), nameof(value))); +/// + /// Creates a new SoundPower from a value in Horsepower. + /// + /// The value in Horsepower. + /// A new SoundPower instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundPower FromHorsepower(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.HorsepowerToWatts)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Power unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IPowerUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Power. + public static implicit operator Power(SoundPower value) => Power.Create(value.Value); +/// Explicit conversion from Power. + public static explicit operator SoundPower(Power value) => Create(value.Value); +/// Creates a SoundPower from a Power value. + public static SoundPower From(Power value) => Create(value.Value); +/// Subtracts two SoundPower values, returning the absolute difference as a non-negative SoundPower. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SoundPower operator -(SoundPower left, SoundPower right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundPressure.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundPressure.g.cs new file mode 100644 index 0000000..7cab401 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundPressure.g.cs @@ -0,0 +1,82 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// RMS pressure deviation caused by a sound wave. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record SoundPressure : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SoundPressure Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// + /// Creates a new SoundPressure from a value in Pascal. + /// + /// The value in Pascal. + /// A new SoundPressure instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundPressure FromPascal(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new SoundPressure from a value in Kilopascal. + /// + /// The value in Kilopascal. + /// A new SoundPressure instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundPressure FromKilopascal(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new SoundPressure from a value in Bar. + /// + /// The value in Bar. + /// A new SoundPressure instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundPressure FromBar(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BarToPascals)), nameof(value))); +/// + /// Creates a new SoundPressure from a value in Atmosphere. + /// + /// The value in Atmosphere. + /// A new SoundPressure instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundPressure FromAtmosphere(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AtmosphereToPascals)), nameof(value))); +/// + /// Creates a new SoundPressure from a value in Psi. + /// + /// The value in Psi. + /// A new SoundPressure instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundPressure FromPsi(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PsiToPascals)), nameof(value))); +/// + /// Creates a new SoundPressure from a value in Torr. + /// + /// The value in Torr. + /// A new SoundPressure instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundPressure FromTorr(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.TorrToPascals)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Pressure unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IPressureUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Pressure. + public static implicit operator Pressure(SoundPressure value) => Pressure.Create(value.Value); +/// Explicit conversion from Pressure. + public static explicit operator SoundPressure(Pressure value) => Create(value.Value); +/// Creates a SoundPressure from a Pressure value. + public static SoundPressure From(Pressure value) => Create(value.Value); +/// Subtracts two SoundPressure values, returning the absolute difference as a non-negative SoundPressure. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SoundPressure operator -(SoundPressure left, SoundPressure right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundSpeed.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundSpeed.g.cs new file mode 100644 index 0000000..ed1f667 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundSpeed.g.cs @@ -0,0 +1,75 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Speed of sound propagation in a medium. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record SoundSpeed : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SoundSpeed Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// + /// Creates a new SoundSpeed from a value in MeterPerSecond. + /// + /// The value in MeterPerSecond. + /// A new SoundSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundSpeed FromMeterPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new SoundSpeed from a value in KilometerPerHour. + /// + /// The value in KilometerPerHour. + /// A new SoundSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundSpeed FromKilometerPerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilometerPerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new SoundSpeed from a value in MilePerHour. + /// + /// The value in MilePerHour. + /// A new SoundSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundSpeed FromMilePerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MilePerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new SoundSpeed from a value in FootPerSecond. + /// + /// The value in FootPerSecond. + /// A new SoundSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundSpeed FromFootPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FootPerSecondToMeterPerSecond)), nameof(value))); +/// + /// Creates a new SoundSpeed from a value in Knot. + /// + /// The value in Knot. + /// A new SoundSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundSpeed FromKnot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KnotToMeterPerSecond)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Velocity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IVelocityUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Speed. + public static implicit operator Speed(SoundSpeed value) => Speed.Create(value.Value); +/// Explicit conversion from Speed. + public static explicit operator SoundSpeed(Speed value) => Create(value.Value); +/// Creates a SoundSpeed from a Speed value. + public static SoundSpeed From(Speed value) => Create(value.Value); +/// Subtracts two SoundSpeed values, returning the absolute difference as a non-negative SoundSpeed. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SoundSpeed operator -(SoundSpeed left, SoundSpeed right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundTransmissionClass.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundTransmissionClass.g.cs new file mode 100644 index 0000000..fc59f3f --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SoundTransmissionClass.g.cs @@ -0,0 +1,110 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Single-number rating of airborne sound insulation. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record SoundTransmissionClass : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SoundTransmissionClass Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// + /// Creates a new SoundTransmissionClass from a value in Dimensionless. + /// + /// The value in Dimensionless. + /// A new SoundTransmissionClass instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundTransmissionClass FromDimensionless(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new SoundTransmissionClass from a value in Radian. + /// + /// The value in Radian. + /// A new SoundTransmissionClass instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundTransmissionClass FromRadian(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new SoundTransmissionClass from a value in Degree. + /// + /// The value in Degree. + /// A new SoundTransmissionClass instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundTransmissionClass FromDegree(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians)), nameof(value))); +/// + /// Creates a new SoundTransmissionClass from a value in Gradian. + /// + /// The value in Gradian. + /// A new SoundTransmissionClass instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundTransmissionClass FromGradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians)), nameof(value))); +/// + /// Creates a new SoundTransmissionClass from a value in Revolution. + /// + /// The value in Revolution. + /// A new SoundTransmissionClass instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundTransmissionClass FromRevolution(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians)), nameof(value))); +/// + /// Creates a new SoundTransmissionClass from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new SoundTransmissionClass instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundTransmissionClass FromMilliradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new SoundTransmissionClass from a value in Percent. + /// + /// The value in Percent. + /// A new SoundTransmissionClass instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundTransmissionClass FromPercent(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentToRatio)), nameof(value))); +/// + /// Creates a new SoundTransmissionClass from a value in PartPerMillion. + /// + /// The value in PartPerMillion. + /// A new SoundTransmissionClass instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundTransmissionClass FromPartPerMillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerMillionToRatio)), nameof(value))); +/// + /// Creates a new SoundTransmissionClass from a value in PartPerBillion. + /// + /// The value in PartPerBillion. + /// A new SoundTransmissionClass instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundTransmissionClass FromPartPerBillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerBillionToRatio)), nameof(value))); +/// + /// Creates a new SoundTransmissionClass from a value in PercentByWeight. + /// + /// The value in PercentByWeight. + /// A new SoundTransmissionClass instance. + /// Thrown when the resulting magnitude would be negative. + public static SoundTransmissionClass FromPercentByWeight(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentByWeightToRatio)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Dimensionless unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IDimensionlessUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Ratio. + public static implicit operator Ratio(SoundTransmissionClass value) => Ratio.Create(value.Value); +/// Explicit conversion from Ratio. + public static explicit operator SoundTransmissionClass(Ratio value) => Create(value.Value); +/// Creates a SoundTransmissionClass from a Ratio value. + public static SoundTransmissionClass From(Ratio value) => Create(value.Value); +/// Subtracts two SoundTransmissionClass values, returning the absolute difference as a non-negative SoundTransmissionClass. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SoundTransmissionClass operator -(SoundTransmissionClass left, SoundTransmissionClass right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SpecificEntropy.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SpecificEntropy.g.cs new file mode 100644 index 0000000..6affaeb --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SpecificEntropy.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Entropy per unit mass. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record SpecificEntropy : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SpecificEntropy Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.SpecificHeat; + + /// + /// Creates a new SpecificEntropy from a value in JoulePerKilogramKelvin. + /// + /// The value in JoulePerKilogramKelvin. + /// A new SpecificEntropy instance. + /// Thrown when the resulting magnitude would be negative. + public static SpecificEntropy FromJoulePerKilogramKelvin(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-SpecificHeat unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ISpecificHeatUnit unit) => unit.FromBase(Value); +/// Implicit conversion to SpecificHeat. + public static implicit operator SpecificHeat(SpecificEntropy value) => SpecificHeat.Create(value.Value); +/// Explicit conversion from SpecificHeat. + public static explicit operator SpecificEntropy(SpecificHeat value) => Create(value.Value); +/// Creates a SpecificEntropy from a SpecificHeat value. + public static SpecificEntropy From(SpecificHeat value) => Create(value.Value); +/// Subtracts two SpecificEntropy values, returning the absolute difference as a non-negative SpecificEntropy. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SpecificEntropy operator -(SpecificEntropy left, SpecificEntropy right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SpecificGravity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SpecificGravity.g.cs new file mode 100644 index 0000000..9702d7c --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SpecificGravity.g.cs @@ -0,0 +1,110 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Ratio of a substance's density to a reference density. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record SpecificGravity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SpecificGravity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// + /// Creates a new SpecificGravity from a value in Dimensionless. + /// + /// The value in Dimensionless. + /// A new SpecificGravity instance. + /// Thrown when the resulting magnitude would be negative. + public static SpecificGravity FromDimensionless(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new SpecificGravity from a value in Radian. + /// + /// The value in Radian. + /// A new SpecificGravity instance. + /// Thrown when the resulting magnitude would be negative. + public static SpecificGravity FromRadian(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new SpecificGravity from a value in Degree. + /// + /// The value in Degree. + /// A new SpecificGravity instance. + /// Thrown when the resulting magnitude would be negative. + public static SpecificGravity FromDegree(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DegreeToRadians)), nameof(value))); +/// + /// Creates a new SpecificGravity from a value in Gradian. + /// + /// The value in Gradian. + /// A new SpecificGravity instance. + /// Thrown when the resulting magnitude would be negative. + public static SpecificGravity FromGradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GradianToRadians)), nameof(value))); +/// + /// Creates a new SpecificGravity from a value in Revolution. + /// + /// The value in Revolution. + /// A new SpecificGravity instance. + /// Thrown when the resulting magnitude would be negative. + public static SpecificGravity FromRevolution(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.RevolutionToRadians)), nameof(value))); +/// + /// Creates a new SpecificGravity from a value in Milliradian. + /// + /// The value in Milliradian. + /// A new SpecificGravity instance. + /// Thrown when the resulting magnitude would be negative. + public static SpecificGravity FromMilliradian(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new SpecificGravity from a value in Percent. + /// + /// The value in Percent. + /// A new SpecificGravity instance. + /// Thrown when the resulting magnitude would be negative. + public static SpecificGravity FromPercent(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentToRatio)), nameof(value))); +/// + /// Creates a new SpecificGravity from a value in PartPerMillion. + /// + /// The value in PartPerMillion. + /// A new SpecificGravity instance. + /// Thrown when the resulting magnitude would be negative. + public static SpecificGravity FromPartPerMillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerMillionToRatio)), nameof(value))); +/// + /// Creates a new SpecificGravity from a value in PartPerBillion. + /// + /// The value in PartPerBillion. + /// A new SpecificGravity instance. + /// Thrown when the resulting magnitude would be negative. + public static SpecificGravity FromPartPerBillion(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PartPerBillionToRatio)), nameof(value))); +/// + /// Creates a new SpecificGravity from a value in PercentByWeight. + /// + /// The value in PercentByWeight. + /// A new SpecificGravity instance. + /// Thrown when the resulting magnitude would be negative. + public static SpecificGravity FromPercentByWeight(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PercentByWeightToRatio)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Dimensionless unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IDimensionlessUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Ratio. + public static implicit operator Ratio(SpecificGravity value) => Ratio.Create(value.Value); +/// Explicit conversion from Ratio. + public static explicit operator SpecificGravity(Ratio value) => Create(value.Value); +/// Creates a SpecificGravity from a Ratio value. + public static SpecificGravity From(Ratio value) => Create(value.Value); +/// Subtracts two SpecificGravity values, returning the absolute difference as a non-negative SpecificGravity. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SpecificGravity operator -(SpecificGravity left, SpecificGravity right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SpecificHeat.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SpecificHeat.g.cs new file mode 100644 index 0000000..80da680 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SpecificHeat.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the SpecificHeat dimension. +/// +/// The numeric storage type. +public partial record SpecificHeat : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SpecificHeat Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.SpecificHeat; + + /// + /// Creates a new from a value in JoulePerKilogramKelvin. + /// + /// The value in JoulePerKilogramKelvin. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static SpecificHeat FromJoulePerKilogramKelvin(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-SpecificHeat unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ISpecificHeatUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two SpecificHeat values, returning the absolute difference as a non-negative SpecificHeat. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SpecificHeat operator -(SpecificHeat left, SpecificHeat right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies SpecificHeat by Mass to produce Entropy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Entropy operator *(SpecificHeat left, Mass right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Speed.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Speed.g.cs new file mode 100644 index 0000000..d73bb2e --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Speed.g.cs @@ -0,0 +1,99 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Velocity dimension. +/// +/// The numeric storage type. +public partial record Speed : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Speed Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// + /// Creates a new from a value in MeterPerSecond. + /// + /// The value in MeterPerSecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Speed FromMeterPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in KilometerPerHour. + /// + /// The value in KilometerPerHour. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Speed FromKilometerPerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilometerPerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new from a value in MilePerHour. + /// + /// The value in MilePerHour. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Speed FromMilePerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MilePerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new from a value in FootPerSecond. + /// + /// The value in FootPerSecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Speed FromFootPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FootPerSecondToMeterPerSecond)), nameof(value))); +/// + /// Creates a new from a value in Knot. + /// + /// The value in Knot. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Speed FromKnot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KnotToMeterPerSecond)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Velocity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IVelocityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Speed values, returning the absolute difference as a non-negative Speed. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Speed operator -(Speed left, Speed right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Speed by Mass to produce MomentumMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MomentumMagnitude operator *(Speed left, Mass right) => Multiply>(left, right); +/// + /// Multiplies Speed by Duration to produce Length. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Length operator *(Speed left, Duration right) => Multiply>(left, right); +/// + /// Divides Speed by Duration to produce AccelerationMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AccelerationMagnitude operator /(Speed left, Duration right) => Divide>(left, right); +/// + /// Divides Speed by AccelerationMagnitude to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(Speed left, AccelerationMagnitude right) => Divide>(left, right); +/// + /// Divides Speed by Length to produce Frequency. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Frequency operator /(Speed left, Length right) => Divide>(left, right); +/// + /// Divides Speed by Frequency to produce Length. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Length operator /(Speed left, Frequency right) => Divide>(left, right); +/// + /// Multiplies Speed by ForceMagnitude to produce Power. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Power operator *(Speed left, ForceMagnitude right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Stress.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Stress.g.cs new file mode 100644 index 0000000..fc5b300 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Stress.g.cs @@ -0,0 +1,82 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Internal force per unit area in a material. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Stress : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Stress Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// + /// Creates a new Stress from a value in Pascal. + /// + /// The value in Pascal. + /// A new Stress instance. + /// Thrown when the resulting magnitude would be negative. + public static Stress FromPascal(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Stress from a value in Kilopascal. + /// + /// The value in Kilopascal. + /// A new Stress instance. + /// Thrown when the resulting magnitude would be negative. + public static Stress FromKilopascal(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Stress from a value in Bar. + /// + /// The value in Bar. + /// A new Stress instance. + /// Thrown when the resulting magnitude would be negative. + public static Stress FromBar(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BarToPascals)), nameof(value))); +/// + /// Creates a new Stress from a value in Atmosphere. + /// + /// The value in Atmosphere. + /// A new Stress instance. + /// Thrown when the resulting magnitude would be negative. + public static Stress FromAtmosphere(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AtmosphereToPascals)), nameof(value))); +/// + /// Creates a new Stress from a value in Psi. + /// + /// The value in Psi. + /// A new Stress instance. + /// Thrown when the resulting magnitude would be negative. + public static Stress FromPsi(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PsiToPascals)), nameof(value))); +/// + /// Creates a new Stress from a value in Torr. + /// + /// The value in Torr. + /// A new Stress instance. + /// Thrown when the resulting magnitude would be negative. + public static Stress FromTorr(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.TorrToPascals)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Pressure unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IPressureUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Pressure. + public static implicit operator Pressure(Stress value) => Pressure.Create(value.Value); +/// Explicit conversion from Pressure. + public static explicit operator Stress(Pressure value) => Create(value.Value); +/// Creates a Stress from a Pressure value. + public static Stress From(Pressure value) => Create(value.Value); +/// Subtracts two Stress values, returning the absolute difference as a non-negative Stress. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Stress operator -(Stress left, Stress right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SurfaceArea.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SurfaceArea.g.cs new file mode 100644 index 0000000..db92ea9 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SurfaceArea.g.cs @@ -0,0 +1,96 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Total area of the outer surface of a body. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record SurfaceArea : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SurfaceArea Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Area; + + /// + /// Creates a new SurfaceArea from a value in SquareMeter. + /// + /// The value in SquareMeter. + /// A new SurfaceArea instance. + /// Thrown when the resulting magnitude would be negative. + public static SurfaceArea FromSquareMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new SurfaceArea from a value in SquareKilometer. + /// + /// The value in SquareKilometer. + /// A new SurfaceArea instance. + /// Thrown when the resulting magnitude would be negative. + public static SurfaceArea FromSquareKilometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareKilometerToSquareMeters)), nameof(value))); +/// + /// Creates a new SurfaceArea from a value in SquareCentimeter. + /// + /// The value in SquareCentimeter. + /// A new SurfaceArea instance. + /// Thrown when the resulting magnitude would be negative. + public static SurfaceArea FromSquareCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareCentimeterToSquareMeters)), nameof(value))); +/// + /// Creates a new SurfaceArea from a value in SquareFoot. + /// + /// The value in SquareFoot. + /// A new SurfaceArea instance. + /// Thrown when the resulting magnitude would be negative. + public static SurfaceArea FromSquareFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareFootToSquareMeters)), nameof(value))); +/// + /// Creates a new SurfaceArea from a value in SquareInch. + /// + /// The value in SquareInch. + /// A new SurfaceArea instance. + /// Thrown when the resulting magnitude would be negative. + public static SurfaceArea FromSquareInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareInchToSquareMeters)), nameof(value))); +/// + /// Creates a new SurfaceArea from a value in SquareMile. + /// + /// The value in SquareMile. + /// A new SurfaceArea instance. + /// Thrown when the resulting magnitude would be negative. + public static SurfaceArea FromSquareMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.SquareMileToSquareMeters)), nameof(value))); +/// + /// Creates a new SurfaceArea from a value in Hectare. + /// + /// The value in Hectare. + /// A new SurfaceArea instance. + /// Thrown when the resulting magnitude would be negative. + public static SurfaceArea FromHectare(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.HectareToSquareMeters)), nameof(value))); +/// + /// Creates a new SurfaceArea from a value in Acre. + /// + /// The value in Acre. + /// A new SurfaceArea instance. + /// Thrown when the resulting magnitude would be negative. + public static SurfaceArea FromAcre(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AcreToSquareMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Area unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IAreaUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Area. + public static implicit operator Area(SurfaceArea value) => Area.Create(value.Value); +/// Explicit conversion from Area. + public static explicit operator SurfaceArea(Area value) => Create(value.Value); +/// Creates a SurfaceArea from a Area value. + public static SurfaceArea From(Area value) => Create(value.Value); +/// Subtracts two SurfaceArea values, returning the absolute difference as a non-negative SurfaceArea. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SurfaceArea operator -(SurfaceArea left, SurfaceArea right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SurfaceTension.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SurfaceTension.g.cs new file mode 100644 index 0000000..47b88c8 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/SurfaceTension.g.cs @@ -0,0 +1,54 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the SurfaceTension dimension. +/// +/// The numeric storage type. +public partial record SurfaceTension : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static SurfaceTension Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.SurfaceTension; + + /// + /// Creates a new from a value in NewtonPerMeter. + /// + /// The value in NewtonPerMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static SurfaceTension FromNewtonPerMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in DynePerCentimeter. + /// + /// The value in DynePerCentimeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static SurfaceTension FromDynePerCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DynePerCentimeterToNewtonPerMeter)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-SurfaceTension unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ISurfaceTensionUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two SurfaceTension values, returning the absolute difference as a non-negative SurfaceTension. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static SurfaceTension operator -(SurfaceTension left, SurfaceTension right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies SurfaceTension by Length to produce ForceMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ForceMagnitude operator *(SurfaceTension left, Length right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Temperature.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Temperature.g.cs new file mode 100644 index 0000000..6c7fb7f --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Temperature.g.cs @@ -0,0 +1,72 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Temperature dimension. +/// +/// The numeric storage type. +public partial record Temperature : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Temperature Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Temperature; + + /// + /// Creates a new from a value in Kelvin. + /// + /// The value in Kelvin. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Temperature FromKelvin(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Celsius. + /// + /// The value in Celsius. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Temperature FromCelsius(T value) => Create(Vector0Guards.EnsureNonNegative((value + T.CreateChecked(Units.ConversionConstants.CelsiusToKelvinOffset)), nameof(value))); +/// + /// Creates a new from a value in Fahrenheit. + /// + /// The value in Fahrenheit. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Temperature FromFahrenheit(T value) => Create(Vector0Guards.EnsureNonNegative(((value * T.CreateChecked(Units.ConversionConstants.FahrenheitScale)) + T.CreateChecked(Units.ConversionConstants.FahrenheitToKelvinOffset)), nameof(value))); +/// + /// Creates a new from a value in Rankine. + /// + /// The value in Rankine. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Temperature FromRankine(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FahrenheitScale)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Temperature unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ITemperatureUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Temperature values, returning the absolute difference as a non-negative Temperature. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Temperature operator -(Temperature left, Temperature right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies Temperature by Entropy to produce Energy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Energy operator *(Temperature left, Entropy right) => Multiply>(left, right); +/// + /// Multiplies Temperature by ThermalExpansionCoefficient to produce Ratio. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Ratio operator *(Temperature left, ThermalExpansionCoefficient right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TemperatureDelta.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TemperatureDelta.g.cs new file mode 100644 index 0000000..34e5c10 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TemperatureDelta.g.cs @@ -0,0 +1,60 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the Temperature dimension. +/// +/// The numeric storage type. +public partial record TemperatureDelta : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static TemperatureDelta Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Temperature; + + /// + /// Creates a new from a value in Kelvin. + /// + /// The value in Kelvin. + /// A new instance. + public static TemperatureDelta FromKelvin(T value) => Create(value); +/// + /// Creates a new from a value in Celsius. + /// + /// The value in Celsius. + /// A new instance. + public static TemperatureDelta FromCelsius(T value) => Create((value + T.CreateChecked(Units.ConversionConstants.CelsiusToKelvinOffset))); +/// + /// Creates a new from a value in Fahrenheit. + /// + /// The value in Fahrenheit. + /// A new instance. + public static TemperatureDelta FromFahrenheit(T value) => Create(((value * T.CreateChecked(Units.ConversionConstants.FahrenheitScale)) + T.CreateChecked(Units.ConversionConstants.FahrenheitToKelvinOffset))); +/// + /// Creates a new from a value in Rankine. + /// + /// The value in Rankine. + /// A new instance. + public static TemperatureDelta FromRankine(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.FahrenheitScale))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Temperature unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ITemperatureUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public Temperature Magnitude() => Temperature.Create(T.Abs(Value)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TemperatureDrop.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TemperatureDrop.g.cs new file mode 100644 index 0000000..a911309 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TemperatureDrop.g.cs @@ -0,0 +1,62 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Decrease in temperature. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record TemperatureDrop : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static TemperatureDrop Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Temperature; + + /// + /// Creates a new TemperatureDrop from a value in Kelvin. + /// + /// The value in Kelvin. + /// A new TemperatureDrop instance. + public static TemperatureDrop FromKelvin(T value) => Create(value); +/// + /// Creates a new TemperatureDrop from a value in Celsius. + /// + /// The value in Celsius. + /// A new TemperatureDrop instance. + public static TemperatureDrop FromCelsius(T value) => Create((value + T.CreateChecked(Units.ConversionConstants.CelsiusToKelvinOffset))); +/// + /// Creates a new TemperatureDrop from a value in Fahrenheit. + /// + /// The value in Fahrenheit. + /// A new TemperatureDrop instance. + public static TemperatureDrop FromFahrenheit(T value) => Create(((value * T.CreateChecked(Units.ConversionConstants.FahrenheitScale)) + T.CreateChecked(Units.ConversionConstants.FahrenheitToKelvinOffset))); +/// + /// Creates a new TemperatureDrop from a value in Rankine. + /// + /// The value in Rankine. + /// A new TemperatureDrop instance. + public static TemperatureDrop FromRankine(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.FahrenheitScale))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Temperature unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ITemperatureUnit unit) => unit.FromBase(Value); +/// Implicit conversion to TemperatureDelta. + public static implicit operator TemperatureDelta(TemperatureDrop value) => TemperatureDelta.Create(value.Value); +/// Explicit conversion from TemperatureDelta. + public static explicit operator TemperatureDrop(TemperatureDelta value) => Create(value.Value); +/// Creates a TemperatureDrop from a TemperatureDelta value. + public static TemperatureDrop From(TemperatureDelta value) => Create(value.Value); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TemperatureRise.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TemperatureRise.g.cs new file mode 100644 index 0000000..27e08cd --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TemperatureRise.g.cs @@ -0,0 +1,62 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Increase in temperature. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record TemperatureRise : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static TemperatureRise Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Temperature; + + /// + /// Creates a new TemperatureRise from a value in Kelvin. + /// + /// The value in Kelvin. + /// A new TemperatureRise instance. + public static TemperatureRise FromKelvin(T value) => Create(value); +/// + /// Creates a new TemperatureRise from a value in Celsius. + /// + /// The value in Celsius. + /// A new TemperatureRise instance. + public static TemperatureRise FromCelsius(T value) => Create((value + T.CreateChecked(Units.ConversionConstants.CelsiusToKelvinOffset))); +/// + /// Creates a new TemperatureRise from a value in Fahrenheit. + /// + /// The value in Fahrenheit. + /// A new TemperatureRise instance. + public static TemperatureRise FromFahrenheit(T value) => Create(((value * T.CreateChecked(Units.ConversionConstants.FahrenheitScale)) + T.CreateChecked(Units.ConversionConstants.FahrenheitToKelvinOffset))); +/// + /// Creates a new TemperatureRise from a value in Rankine. + /// + /// The value in Rankine. + /// A new TemperatureRise instance. + public static TemperatureRise FromRankine(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.FahrenheitScale))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Temperature unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ITemperatureUnit unit) => unit.FromBase(Value); +/// Implicit conversion to TemperatureDelta. + public static implicit operator TemperatureDelta(TemperatureRise value) => TemperatureDelta.Create(value.Value); +/// Explicit conversion from TemperatureDelta. + public static explicit operator TemperatureRise(TemperatureDelta value) => Create(value.Value); +/// Creates a TemperatureRise from a TemperatureDelta value. + public static TemperatureRise From(TemperatureDelta value) => Create(value.Value); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Tension.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Tension.g.cs new file mode 100644 index 0000000..b538f09 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Tension.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Pulling force along a rope, cable, or similar. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Tension : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Tension Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Force; + + /// + /// Creates a new Tension from a value in Newton. + /// + /// The value in Newton. + /// A new Tension instance. + /// Thrown when the resulting magnitude would be negative. + public static Tension FromNewton(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Tension from a value in Kilonewton. + /// + /// The value in Kilonewton. + /// A new Tension instance. + /// Thrown when the resulting magnitude would be negative. + public static Tension FromKilonewton(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Tension from a value in Dyne. + /// + /// The value in Dyne. + /// A new Tension instance. + /// Thrown when the resulting magnitude would be negative. + public static Tension FromDyne(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DyneToNewtons)), nameof(value))); +/// + /// Creates a new Tension from a value in PoundForce. + /// + /// The value in PoundForce. + /// A new Tension instance. + /// Thrown when the resulting magnitude would be negative. + public static Tension FromPoundForce(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PoundForceToNewtons)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Force unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IForceUnit unit) => unit.FromBase(Value); +/// Implicit conversion to ForceMagnitude. + public static implicit operator ForceMagnitude(Tension value) => ForceMagnitude.Create(value.Value); +/// Explicit conversion from ForceMagnitude. + public static explicit operator Tension(ForceMagnitude value) => Create(value.Value); +/// Creates a Tension from a ForceMagnitude value. + public static Tension From(ForceMagnitude value) => Create(value.Value); +/// Subtracts two Tension values, returning the absolute difference as a non-negative Tension. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Tension operator -(Tension left, Tension right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalConductivity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalConductivity.g.cs new file mode 100644 index 0000000..36ac20f --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalConductivity.g.cs @@ -0,0 +1,43 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ThermalConductivity dimension. +/// +/// The numeric storage type. +public partial record ThermalConductivity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ThermalConductivity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ThermalConductivity; + + /// + /// Creates a new from a value in WattPerMeterKelvin. + /// + /// The value in WattPerMeterKelvin. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalConductivity FromWattPerMeterKelvin(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ThermalConductivity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IThermalConductivityUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two ThermalConductivity values, returning the absolute difference as a non-negative ThermalConductivity. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ThermalConductivity operator -(ThermalConductivity left, ThermalConductivity right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalDiffusivity.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalDiffusivity.g.cs new file mode 100644 index 0000000..91b7823 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalDiffusivity.g.cs @@ -0,0 +1,54 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Rate of heat transfer relative to heat storage. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record ThermalDiffusivity : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ThermalDiffusivity Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.KinematicViscosity; + + /// + /// Creates a new ThermalDiffusivity from a value in SquareMeterPerSecond. + /// + /// The value in SquareMeterPerSecond. + /// A new ThermalDiffusivity instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalDiffusivity FromSquareMeterPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new ThermalDiffusivity from a value in Stokes. + /// + /// The value in Stokes. + /// A new ThermalDiffusivity instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalDiffusivity FromStokes(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.StokesToSquareMeterPerSecond)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-KinematicViscosity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IKinematicViscosityUnit unit) => unit.FromBase(Value); +/// Implicit conversion to KinematicViscosity. + public static implicit operator KinematicViscosity(ThermalDiffusivity value) => KinematicViscosity.Create(value.Value); +/// Explicit conversion from KinematicViscosity. + public static explicit operator ThermalDiffusivity(KinematicViscosity value) => Create(value.Value); +/// Creates a ThermalDiffusivity from a KinematicViscosity value. + public static ThermalDiffusivity From(KinematicViscosity value) => Create(value.Value); +/// Subtracts two ThermalDiffusivity values, returning the absolute difference as a non-negative ThermalDiffusivity. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ThermalDiffusivity operator -(ThermalDiffusivity left, ThermalDiffusivity right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalEnergy.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalEnergy.g.cs new file mode 100644 index 0000000..103bc9c --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalEnergy.g.cs @@ -0,0 +1,103 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Internal energy due to particle motion. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record ThermalEnergy : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ThermalEnergy Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// + /// Creates a new ThermalEnergy from a value in Joule. + /// + /// The value in Joule. + /// A new ThermalEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalEnergy FromJoule(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new ThermalEnergy from a value in Kilojoule. + /// + /// The value in Kilojoule. + /// A new ThermalEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalEnergy FromKilojoule(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new ThermalEnergy from a value in ElectronVolt. + /// + /// The value in ElectronVolt. + /// A new ThermalEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalEnergy FromElectronVolt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ElectronVoltToJoules)), nameof(value))); +/// + /// Creates a new ThermalEnergy from a value in Calorie. + /// + /// The value in Calorie. + /// A new ThermalEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalEnergy FromCalorie(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CalorieToJoules)), nameof(value))); +/// + /// Creates a new ThermalEnergy from a value in Kilocalorie. + /// + /// The value in Kilocalorie. + /// A new ThermalEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalEnergy FromKilocalorie(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilocalorieToJoules)), nameof(value))); +/// + /// Creates a new ThermalEnergy from a value in KilowattHour. + /// + /// The value in KilowattHour. + /// A new ThermalEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalEnergy FromKilowattHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilowattHourToJoules)), nameof(value))); +/// + /// Creates a new ThermalEnergy from a value in WattHour. + /// + /// The value in WattHour. + /// A new ThermalEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalEnergy FromWattHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.WattHourToJoules)), nameof(value))); +/// + /// Creates a new ThermalEnergy from a value in Erg. + /// + /// The value in Erg. + /// A new ThermalEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalEnergy FromErg(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ErgToJoules)), nameof(value))); +/// + /// Creates a new ThermalEnergy from a value in Btu. + /// + /// The value in Btu. + /// A new ThermalEnergy instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalEnergy FromBtu(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BtuToJoules)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Energy unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IEnergyUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Energy. + public static implicit operator Energy(ThermalEnergy value) => Energy.Create(value.Value); +/// Explicit conversion from Energy. + public static explicit operator ThermalEnergy(Energy value) => Create(value.Value); +/// Creates a ThermalEnergy from a Energy value. + public static ThermalEnergy From(Energy value) => Create(value.Value); +/// Subtracts two ThermalEnergy values, returning the absolute difference as a non-negative ThermalEnergy. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ThermalEnergy operator -(ThermalEnergy left, ThermalEnergy right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalExpansionCoefficient.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalExpansionCoefficient.g.cs new file mode 100644 index 0000000..d9ec5c9 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalExpansionCoefficient.g.cs @@ -0,0 +1,47 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ThermalExpansion dimension. +/// +/// The numeric storage type. +public partial record ThermalExpansionCoefficient : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ThermalExpansionCoefficient Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ThermalExpansion; + + /// + /// Creates a new from a value in PerKelvin. + /// + /// The value in PerKelvin. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalExpansionCoefficient FromPerKelvin(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ThermalExpansion unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IThermalExpansionUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two ThermalExpansionCoefficient values, returning the absolute difference as a non-negative ThermalExpansionCoefficient. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ThermalExpansionCoefficient operator -(ThermalExpansionCoefficient left, ThermalExpansionCoefficient right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies ThermalExpansionCoefficient by Temperature to produce Ratio. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Ratio operator *(ThermalExpansionCoefficient left, Temperature right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalResistance.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalResistance.g.cs new file mode 100644 index 0000000..52e8496 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThermalResistance.g.cs @@ -0,0 +1,43 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ThermalResistance dimension. +/// +/// The numeric storage type. +public partial record ThermalResistance : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static ThermalResistance Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ThermalResistance; + + /// + /// Creates a new from a value in KelvinPerWatt. + /// + /// The value in KelvinPerWatt. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static ThermalResistance FromKelvinPerWatt(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ThermalResistance unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IThermalResistanceUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two ThermalResistance values, returning the absolute difference as a non-negative ThermalResistance. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ThermalResistance operator -(ThermalResistance left, ThermalResistance right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Thickness.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Thickness.g.cs new file mode 100644 index 0000000..f19c6ec --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Thickness.g.cs @@ -0,0 +1,124 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Extent through the thinnest dimension. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Thickness : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Thickness Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Length; + + /// + /// Creates a new Thickness from a value in Meter. + /// + /// The value in Meter. + /// A new Thickness instance. + /// Thrown when the resulting magnitude would be negative. + public static Thickness FromMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Thickness from a value in Kilometer. + /// + /// The value in Kilometer. + /// A new Thickness instance. + /// Thrown when the resulting magnitude would be negative. + public static Thickness FromKilometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Thickness from a value in Centimeter. + /// + /// The value in Centimeter. + /// A new Thickness instance. + /// Thrown when the resulting magnitude would be negative. + public static Thickness FromCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Centi)), nameof(value))); +/// + /// Creates a new Thickness from a value in Millimeter. + /// + /// The value in Millimeter. + /// A new Thickness instance. + /// Thrown when the resulting magnitude would be negative. + public static Thickness FromMillimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Thickness from a value in Micrometer. + /// + /// The value in Micrometer. + /// A new Thickness instance. + /// Thrown when the resulting magnitude would be negative. + public static Thickness FromMicrometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new Thickness from a value in Nanometer. + /// + /// The value in Nanometer. + /// A new Thickness instance. + /// Thrown when the resulting magnitude would be negative. + public static Thickness FromNanometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Creates a new Thickness from a value in Angstrom. + /// + /// The value in Angstrom. + /// A new Thickness instance. + /// Thrown when the resulting magnitude would be negative. + public static Thickness FromAngstrom(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AngstromToMeters)), nameof(value))); +/// + /// Creates a new Thickness from a value in Foot. + /// + /// The value in Foot. + /// A new Thickness instance. + /// Thrown when the resulting magnitude would be negative. + public static Thickness FromFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FeetToMeters)), nameof(value))); +/// + /// Creates a new Thickness from a value in Inch. + /// + /// The value in Inch. + /// A new Thickness instance. + /// Thrown when the resulting magnitude would be negative. + public static Thickness FromInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.InchesToMeters)), nameof(value))); +/// + /// Creates a new Thickness from a value in Yard. + /// + /// The value in Yard. + /// A new Thickness instance. + /// Thrown when the resulting magnitude would be negative. + public static Thickness FromYard(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YardToMeters)), nameof(value))); +/// + /// Creates a new Thickness from a value in Mile. + /// + /// The value in Mile. + /// A new Thickness instance. + /// Thrown when the resulting magnitude would be negative. + public static Thickness FromMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MileToMeters)), nameof(value))); +/// + /// Creates a new Thickness from a value in NauticalMile. + /// + /// The value in NauticalMile. + /// A new Thickness instance. + /// Thrown when the resulting magnitude would be negative. + public static Thickness FromNauticalMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.NauticalMileToMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Length unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILengthUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Length. + public static implicit operator Length(Thickness value) => Length.Create(value.Value); +/// Explicit conversion from Length. + public static explicit operator Thickness(Length value) => Create(value.Value); +/// Creates a Thickness from a Length value. + public static Thickness From(Length value) => Create(value.Value); +/// Subtracts two Thickness values, returning the absolute difference as a non-negative Thickness. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Thickness operator -(Thickness left, Thickness right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Thrust.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Thrust.g.cs new file mode 100644 index 0000000..ae73d54 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Thrust.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Propulsive force. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Thrust : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Thrust Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Force; + + /// + /// Creates a new Thrust from a value in Newton. + /// + /// The value in Newton. + /// A new Thrust instance. + /// Thrown when the resulting magnitude would be negative. + public static Thrust FromNewton(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Thrust from a value in Kilonewton. + /// + /// The value in Kilonewton. + /// A new Thrust instance. + /// Thrown when the resulting magnitude would be negative. + public static Thrust FromKilonewton(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Thrust from a value in Dyne. + /// + /// The value in Dyne. + /// A new Thrust instance. + /// Thrown when the resulting magnitude would be negative. + public static Thrust FromDyne(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DyneToNewtons)), nameof(value))); +/// + /// Creates a new Thrust from a value in PoundForce. + /// + /// The value in PoundForce. + /// A new Thrust instance. + /// Thrown when the resulting magnitude would be negative. + public static Thrust FromPoundForce(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PoundForceToNewtons)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Force unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IForceUnit unit) => unit.FromBase(Value); +/// Implicit conversion to ForceMagnitude. + public static implicit operator ForceMagnitude(Thrust value) => ForceMagnitude.Create(value.Value); +/// Explicit conversion from ForceMagnitude. + public static explicit operator Thrust(ForceMagnitude value) => Create(value.Value); +/// Creates a Thrust from a ForceMagnitude value. + public static Thrust From(ForceMagnitude value) => Create(value.Value); +/// Subtracts two Thrust values, returning the absolute difference as a non-negative Thrust. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Thrust operator -(Thrust left, Thrust right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThrustVector.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThrustVector.g.cs new file mode 100644 index 0000000..f64f502 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/ThrustVector.g.cs @@ -0,0 +1,115 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// Propulsive force vector. +/// Semantic overload of . +/// +public partial record ThrustVector : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static ThrustVector Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static ThrustVector One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static ThrustVector UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static ThrustVector UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static ThrustVector UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(ThrustVector other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public ThrustVector Cross(ThrustVector other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(ThrustVector other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(ThrustVector other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public ThrustVector Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static ThrustVector operator +(ThrustVector left, ThrustVector right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static ThrustVector operator -(ThrustVector left, ThrustVector right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static ThrustVector operator *(ThrustVector vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static ThrustVector operator *(T scalar, ThrustVector vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static ThrustVector operator /(ThrustVector vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static ThrustVector operator -(ThrustVector vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Implicit conversion to Force3D. + public static implicit operator Force3D(ThrustVector value) => new() { X = value.X, Y = value.Y, Z = value.Z }; + + /// Explicit conversion from Force3D. + public static explicit operator ThrustVector(Force3D value) => new() { X = value.X, Y = value.Y, Z = value.Z }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TimeConstant.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TimeConstant.g.cs new file mode 100644 index 0000000..9da50ce --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TimeConstant.g.cs @@ -0,0 +1,103 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Characteristic response time of a system. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record TimeConstant : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static TimeConstant Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Time; + + /// + /// Creates a new TimeConstant from a value in Second. + /// + /// The value in Second. + /// A new TimeConstant instance. + /// Thrown when the resulting magnitude would be negative. + public static TimeConstant FromSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new TimeConstant from a value in Millisecond. + /// + /// The value in Millisecond. + /// A new TimeConstant instance. + /// Thrown when the resulting magnitude would be negative. + public static TimeConstant FromMillisecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new TimeConstant from a value in Microsecond. + /// + /// The value in Microsecond. + /// A new TimeConstant instance. + /// Thrown when the resulting magnitude would be negative. + public static TimeConstant FromMicrosecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new TimeConstant from a value in Minute. + /// + /// The value in Minute. + /// A new TimeConstant instance. + /// Thrown when the resulting magnitude would be negative. + public static TimeConstant FromMinute(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MinuteToSeconds)), nameof(value))); +/// + /// Creates a new TimeConstant from a value in Hour. + /// + /// The value in Hour. + /// A new TimeConstant instance. + /// Thrown when the resulting magnitude would be negative. + public static TimeConstant FromHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.HourToSeconds)), nameof(value))); +/// + /// Creates a new TimeConstant from a value in Day. + /// + /// The value in Day. + /// A new TimeConstant instance. + /// Thrown when the resulting magnitude would be negative. + public static TimeConstant FromDay(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DayToSeconds)), nameof(value))); +/// + /// Creates a new TimeConstant from a value in Year. + /// + /// The value in Year. + /// A new TimeConstant instance. + /// Thrown when the resulting magnitude would be negative. + public static TimeConstant FromYear(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YearToSeconds)), nameof(value))); +/// + /// Creates a new TimeConstant from a value in Week. + /// + /// The value in Week. + /// A new TimeConstant instance. + /// Thrown when the resulting magnitude would be negative. + public static TimeConstant FromWeek(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.WeekToSeconds)), nameof(value))); +/// + /// Creates a new TimeConstant from a value in Nanosecond. + /// + /// The value in Nanosecond. + /// A new TimeConstant instance. + /// Thrown when the resulting magnitude would be negative. + public static TimeConstant FromNanosecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Time unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ITimeUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Duration. + public static implicit operator Duration(TimeConstant value) => Duration.Create(value.Value); +/// Explicit conversion from Duration. + public static explicit operator TimeConstant(Duration value) => Create(value.Value); +/// Creates a TimeConstant from a Duration value. + public static TimeConstant From(Duration value) => Create(value.Value); +/// Subtracts two TimeConstant values, returning the absolute difference as a non-negative TimeConstant. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static TimeConstant operator -(TimeConstant left, TimeConstant right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Torque1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Torque1D.g.cs new file mode 100644 index 0000000..994914c --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Torque1D.g.cs @@ -0,0 +1,52 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the Torque dimension. +/// +/// The numeric storage type. +public partial record Torque1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Torque1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Torque; + + /// + /// Creates a new from a value in NewtonMeter. + /// + /// The value in NewtonMeter. + /// A new instance. + public static Torque1D FromNewtonMeter(T value) => Create(value); +/// + /// Creates a new from a value in PoundFoot. + /// + /// The value in PoundFoot. + /// A new instance. + public static Torque1D FromPoundFoot(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.PoundFootToNewtonMeters))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Torque unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ITorqueUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public TorqueMagnitude Magnitude() => TorqueMagnitude.Create(T.Abs(Value)); +/// + /// Multiplies Torque1D by Duration to produce AngularMomentum1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularMomentum1D operator *(Torque1D left, Duration right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Torque3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Torque3D.g.cs new file mode 100644 index 0000000..3ef1492 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Torque3D.g.cs @@ -0,0 +1,115 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of Torque. +/// +/// The numeric component type. +public partial record Torque3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static Torque3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static Torque3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static Torque3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Torque3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Torque3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public TorqueMagnitude Magnitude() => TorqueMagnitude.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(Torque3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public Torque3D Cross(Torque3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(Torque3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Torque3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public Torque3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static Torque3D operator +(Torque3D left, Torque3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static Torque3D operator -(Torque3D left, Torque3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static Torque3D operator *(Torque3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static Torque3D operator *(T scalar, Torque3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static Torque3D operator /(Torque3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static Torque3D operator -(Torque3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Torque3D * Duration = AngularMomentum3D. + public static AngularMomentum3D operator *(Torque3D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TorqueMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TorqueMagnitude.g.cs new file mode 100644 index 0000000..8775fc4 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/TorqueMagnitude.g.cs @@ -0,0 +1,66 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Torque dimension. +/// +/// The numeric storage type. +public partial record TorqueMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static TorqueMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Torque; + + /// + /// Creates a new from a value in NewtonMeter. + /// + /// The value in NewtonMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static TorqueMagnitude FromNewtonMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in PoundFoot. + /// + /// The value in PoundFoot. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static TorqueMagnitude FromPoundFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PoundFootToNewtonMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Torque unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ITorqueUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two TorqueMagnitude values, returning the absolute difference as a non-negative TorqueMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static TorqueMagnitude operator -(TorqueMagnitude left, TorqueMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies TorqueMagnitude by Angle to produce Energy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Energy operator *(TorqueMagnitude left, Angle right) => Multiply>(left, right); +/// + /// Multiplies TorqueMagnitude by Duration to produce AngularMomentumMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularMomentumMagnitude operator *(TorqueMagnitude left, Duration right) => Multiply>(left, right); +/// + /// Divides TorqueMagnitude by AngularAccelerationMagnitude to produce MomentOfInertia. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MomentOfInertia operator /(TorqueMagnitude left, AngularAccelerationMagnitude right) => Divide>(left, right); +/// + /// Divides TorqueMagnitude by MomentOfInertia to produce AngularAccelerationMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AngularAccelerationMagnitude operator /(TorqueMagnitude left, MomentOfInertia right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Translation3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Translation3D.g.cs new file mode 100644 index 0000000..2179d15 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Translation3D.g.cs @@ -0,0 +1,115 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// Movement applied to an object in 3D space. +/// Semantic overload of . +/// +public partial record Translation3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static Translation3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static Translation3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static Translation3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Translation3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Translation3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(Translation3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public Translation3D Cross(Translation3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(Translation3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Translation3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public Translation3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static Translation3D operator +(Translation3D left, Translation3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static Translation3D operator -(Translation3D left, Translation3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static Translation3D operator *(Translation3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static Translation3D operator *(T scalar, Translation3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static Translation3D operator /(Translation3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static Translation3D operator -(Translation3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Implicit conversion to Displacement3D. + public static implicit operator Displacement3D(Translation3D value) => new() { X = value.X, Y = value.Y, Z = value.Z }; + + /// Explicit conversion from Displacement3D. + public static explicit operator Translation3D(Displacement3D value) => new() { X = value.X, Y = value.Y, Z = value.Z }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Velocity1D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Velocity1D.g.cs new file mode 100644 index 0000000..81cd00f --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Velocity1D.g.cs @@ -0,0 +1,74 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the Velocity dimension. +/// +/// The numeric storage type. +public partial record Velocity1D : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Velocity1D Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// + /// Creates a new from a value in MeterPerSecond. + /// + /// The value in MeterPerSecond. + /// A new instance. + public static Velocity1D FromMeterPerSecond(T value) => Create(value); +/// + /// Creates a new from a value in KilometerPerHour. + /// + /// The value in KilometerPerHour. + /// A new instance. + public static Velocity1D FromKilometerPerHour(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.KilometerPerHourToMeterPerSecond))); +/// + /// Creates a new from a value in MilePerHour. + /// + /// The value in MilePerHour. + /// A new instance. + public static Velocity1D FromMilePerHour(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.MilePerHourToMeterPerSecond))); +/// + /// Creates a new from a value in FootPerSecond. + /// + /// The value in FootPerSecond. + /// A new instance. + public static Velocity1D FromFootPerSecond(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.FootPerSecondToMeterPerSecond))); +/// + /// Creates a new from a value in Knot. + /// + /// The value in Knot. + /// A new instance. + public static Velocity1D FromKnot(T value) => Create((value * T.CreateChecked(Units.ConversionConstants.KnotToMeterPerSecond))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Velocity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IVelocityUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public Speed Magnitude() => Speed.Create(T.Abs(Value)); +/// + /// Multiplies Velocity1D by Duration to produce Displacement1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Displacement1D operator *(Velocity1D left, Duration right) => Multiply>(left, right); +/// + /// Divides Velocity1D by Duration to produce Acceleration1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Acceleration1D operator /(Velocity1D left, Duration right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Velocity2D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Velocity2D.g.cs new file mode 100644 index 0000000..91f31be --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Velocity2D.g.cs @@ -0,0 +1,104 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 2D vector representation of Velocity. +/// +/// The numeric component type. +public partial record Velocity2D : IVector2, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets a vector with all components set to zero. + public static Velocity2D Zero => new() { X = T.Zero, Y = T.Zero }; + + /// Gets a vector with all components set to one. + public static Velocity2D One => new() { X = T.One, Y = T.One }; + + /// Gets the unit vector for the X-axis. + public static Velocity2D UnitX => new() { X = T.One, Y = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Velocity2D UnitY => new() { X = T.Zero, Y = T.One }; + + /// Gets the magnitude as a . + public Speed Magnitude() => Speed.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y); + + /// Calculates the dot product of two vectors. + public T Dot(Velocity2D other) => (X * other.X) + (Y * other.Y); + + /// Calculates the distance between two vectors. + public T Distance(Velocity2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T sum = (dX * dX) + (dY * dY); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Velocity2D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + return (dX * dX) + (dY * dY); + } + + /// Returns a normalized version of the vector. + public Velocity2D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len }; + } + + /// Adds two vectors. + public static Velocity2D operator +(Velocity2D left, Velocity2D right) => new() { X = left.X + right.X, Y = left.Y + right.Y }; + + /// Subtracts two vectors. + public static Velocity2D operator -(Velocity2D left, Velocity2D right) => new() { X = left.X - right.X, Y = left.Y - right.Y }; + + /// Multiplies a vector by a scalar. + public static Velocity2D operator *(Velocity2D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar }; + + /// Multiplies a scalar by a vector. + public static Velocity2D operator *(T scalar, Velocity2D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y }; + + /// Divides a vector by a scalar. + public static Velocity2D operator /(Velocity2D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar }; + + /// Negates a vector. + public static Velocity2D operator -(Velocity2D vector) => new() { X = -vector.X, Y = -vector.Y }; + /// Velocity2D * Duration = Displacement2D. + public static Displacement2D operator *(Velocity2D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value }; + + /// Velocity2D / Duration = Acceleration2D. + public static Acceleration2D operator /(Velocity2D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Velocity3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Velocity3D.g.cs new file mode 100644 index 0000000..a723d2f --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Velocity3D.g.cs @@ -0,0 +1,118 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 3D vector representation of Velocity. +/// +/// The numeric component type. +public partial record Velocity3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static Velocity3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static Velocity3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static Velocity3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Velocity3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Velocity3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Gets the magnitude as a . + public Speed Magnitude() => Speed.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(Velocity3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public Velocity3D Cross(Velocity3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(Velocity3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Velocity3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public Velocity3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static Velocity3D operator +(Velocity3D left, Velocity3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static Velocity3D operator -(Velocity3D left, Velocity3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static Velocity3D operator *(Velocity3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static Velocity3D operator *(T scalar, Velocity3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static Velocity3D operator /(Velocity3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static Velocity3D operator -(Velocity3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Velocity3D * Duration = Displacement3D. + public static Displacement3D operator *(Velocity3D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value }; + + /// Velocity3D / Duration = Acceleration3D. + public static Acceleration3D operator /(Velocity3D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Velocity4D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Velocity4D.g.cs new file mode 100644 index 0000000..42b2417 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Velocity4D.g.cs @@ -0,0 +1,120 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// 4D vector representation of Velocity. +/// +/// The numeric component type. +public partial record Velocity4D : IVector4, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets the W component. + public T W { get; init; } + + /// Gets a vector with all components set to zero. + public static Velocity4D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets a vector with all components set to one. + public static Velocity4D One => new() { X = T.One, Y = T.One, Z = T.One, W = T.One }; + + /// Gets the unit vector for the X-axis. + public static Velocity4D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static Velocity4D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero, W = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static Velocity4D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One, W = T.Zero }; + + /// Gets the unit vector for the W-axis. + public static Velocity4D UnitW => new() { X = T.Zero, Y = T.Zero, Z = T.Zero, W = T.One }; + + /// Gets the magnitude as a . + public Speed Magnitude() => Speed.Create(Length()); + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z) + (W * W); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z) + (W * W); + + /// Calculates the dot product of two vectors. + public T Dot(Velocity4D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z) + (W * other.W); + + /// Calculates the distance between two vectors. + public T Distance(Velocity4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(Velocity4D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T dW = W - other.W; + return (dX * dX) + (dY * dY) + (dZ * dZ) + (dW * dW); + } + + /// Returns a normalized version of the vector. + public Velocity4D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len, W = W / len }; + } + + /// Adds two vectors. + public static Velocity4D operator +(Velocity4D left, Velocity4D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z, W = left.W + right.W }; + + /// Subtracts two vectors. + public static Velocity4D operator -(Velocity4D left, Velocity4D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z, W = left.W - right.W }; + + /// Multiplies a vector by a scalar. + public static Velocity4D operator *(Velocity4D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar, W = vector.W * scalar }; + + /// Multiplies a scalar by a vector. + public static Velocity4D operator *(T scalar, Velocity4D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z, W = scalar * vector.W }; + + /// Divides a vector by a scalar. + public static Velocity4D operator /(Velocity4D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar, W = vector.W / scalar }; + + /// Negates a vector. + public static Velocity4D operator -(Velocity4D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z, W = -vector.W }; + /// Velocity4D * Duration = Displacement4D. + public static Displacement4D operator *(Velocity4D left, Duration right) => new() { X = left.X * right.Value, Y = left.Y * right.Value, Z = left.Z * right.Value, W = left.W * right.Value }; + + /// Velocity4D / Duration = Acceleration4D. + public static Acceleration4D operator /(Velocity4D left, Duration right) => new() { X = left.X / right.Value, Y = left.Y / right.Value, Z = left.Z / right.Value, W = left.W / right.Value }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Voltage.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Voltage.g.cs new file mode 100644 index 0000000..42ed31d --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Voltage.g.cs @@ -0,0 +1,56 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Signed one-dimensional (Vector1) quantity for the ElectricPotential dimension. +/// +/// The numeric storage type. +public partial record Voltage : PhysicalQuantity, T>, IVector1, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Voltage Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricPotential; + + /// + /// Creates a new from a value in Volt. + /// + /// The value in Volt. + /// A new instance. + public static Voltage FromVolt(T value) => Create(value); +/// + /// Creates a new from a value in Kilovolt. + /// + /// The value in Kilovolt. + /// A new instance. + public static Voltage FromKilovolt(T value) => Create((value * T.CreateChecked(MetricMagnitudes.Kilo))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricPotential unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricPotentialUnit unit) => unit.FromBase(Value); +/// + /// Gets the magnitude of this quantity as a . + /// + /// The non-negative magnitude. + public VoltageMagnitude Magnitude() => VoltageMagnitude.Create(T.Abs(Value)); +/// + /// Divides Voltage by Resistance to produce Current1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Current1D operator /(Voltage left, Resistance right) => Divide>(left, right); +/// + /// Divides Voltage by Length to produce ElectricField1D. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ElectricField1D operator /(Voltage left, Length right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/VoltageDrop.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/VoltageDrop.g.cs new file mode 100644 index 0000000..212517b --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/VoltageDrop.g.cs @@ -0,0 +1,54 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Decrease in voltage across a component. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record VoltageDrop : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static VoltageDrop Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricPotential; + + /// + /// Creates a new VoltageDrop from a value in Volt. + /// + /// The value in Volt. + /// A new VoltageDrop instance. + /// Thrown when the resulting magnitude would be negative. + public static VoltageDrop FromVolt(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new VoltageDrop from a value in Kilovolt. + /// + /// The value in Kilovolt. + /// A new VoltageDrop instance. + /// Thrown when the resulting magnitude would be negative. + public static VoltageDrop FromKilovolt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricPotential unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricPotentialUnit unit) => unit.FromBase(Value); +/// Implicit conversion to VoltageMagnitude. + public static implicit operator VoltageMagnitude(VoltageDrop value) => VoltageMagnitude.Create(value.Value); +/// Explicit conversion from VoltageMagnitude. + public static explicit operator VoltageDrop(VoltageMagnitude value) => Create(value.Value); +/// Creates a VoltageDrop from a VoltageMagnitude value. + public static VoltageDrop From(VoltageMagnitude value) => Create(value.Value); +/// Subtracts two VoltageDrop values, returning the absolute difference as a non-negative VoltageDrop. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VoltageDrop operator -(VoltageDrop left, VoltageDrop right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/VoltageMagnitude.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/VoltageMagnitude.g.cs new file mode 100644 index 0000000..1d41abf --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/VoltageMagnitude.g.cs @@ -0,0 +1,90 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the ElectricPotential dimension. +/// +/// The numeric storage type. +public partial record VoltageMagnitude : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static VoltageMagnitude Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.ElectricPotential; + + /// + /// Creates a new from a value in Volt. + /// + /// The value in Volt. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static VoltageMagnitude FromVolt(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Kilovolt. + /// + /// The value in Kilovolt. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static VoltageMagnitude FromKilovolt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-ElectricPotential unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IElectricPotentialUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two VoltageMagnitude values, returning the absolute difference as a non-negative VoltageMagnitude. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VoltageMagnitude operator -(VoltageMagnitude left, VoltageMagnitude right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides VoltageMagnitude by Resistance to produce CurrentMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static CurrentMagnitude operator /(VoltageMagnitude left, Resistance right) => Divide>(left, right); +/// + /// Divides VoltageMagnitude by CurrentMagnitude to produce Resistance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Resistance operator /(VoltageMagnitude left, CurrentMagnitude right) => Divide>(left, right); +/// + /// Multiplies VoltageMagnitude by CurrentMagnitude to produce Power. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Power operator *(VoltageMagnitude left, CurrentMagnitude right) => Multiply>(left, right); +/// + /// Divides VoltageMagnitude by Length to produce ElectricFieldMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ElectricFieldMagnitude operator /(VoltageMagnitude left, Length right) => Divide>(left, right); +/// + /// Divides VoltageMagnitude by ElectricFieldMagnitude to produce Length. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Length operator /(VoltageMagnitude left, ElectricFieldMagnitude right) => Divide>(left, right); +/// + /// Multiplies VoltageMagnitude by Capacitance to produce ChargeMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static ChargeMagnitude operator *(VoltageMagnitude left, Capacitance right) => Multiply>(left, right); +/// + /// Multiplies VoltageMagnitude by Conductance to produce CurrentMagnitude. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static CurrentMagnitude operator *(VoltageMagnitude left, Conductance right) => Multiply>(left, right); +/// + /// Multiplies VoltageMagnitude by Duration to produce MagneticFlux. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static MagneticFlux operator *(VoltageMagnitude left, Duration right) => Multiply>(left, right); +/// + /// Divides VoltageMagnitude by Pressure to produce Sensitivity. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Sensitivity operator /(VoltageMagnitude left, Pressure right) => Divide>(left, right); +/// + /// Divides VoltageMagnitude by Sensitivity to produce Pressure. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Pressure operator /(VoltageMagnitude left, Sensitivity right) => Divide>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Volume.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Volume.g.cs new file mode 100644 index 0000000..742b28f --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Volume.g.cs @@ -0,0 +1,145 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the Volume dimension. +/// +/// The numeric storage type. +public partial record Volume : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Volume Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Volume; + + /// + /// Creates a new from a value in CubicMeter. + /// + /// The value in CubicMeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Volume FromCubicMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in Liter. + /// + /// The value in Liter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Volume FromLiter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.LiterToCubicMeters)), nameof(value))); +/// + /// Creates a new from a value in Milliliter. + /// + /// The value in Milliliter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Volume FromMilliliter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new from a value in CubicCentimeter. + /// + /// The value in CubicCentimeter. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Volume FromCubicCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CubicCentimeterToCubicMeters)), nameof(value))); +/// + /// Creates a new from a value in CubicFoot. + /// + /// The value in CubicFoot. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Volume FromCubicFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CubicFootToCubicMeters)), nameof(value))); +/// + /// Creates a new from a value in CubicInch. + /// + /// The value in CubicInch. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Volume FromCubicInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CubicInchToCubicMeters)), nameof(value))); +/// + /// Creates a new from a value in Gallon. + /// + /// The value in Gallon. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Volume FromGallon(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.GallonToCubicMeters)), nameof(value))); +/// + /// Creates a new from a value in ImperialGallon. + /// + /// The value in ImperialGallon. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Volume FromImperialGallon(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ImperialGallonToCubicMeters)), nameof(value))); +/// + /// Creates a new from a value in USQuart. + /// + /// The value in USQuart. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Volume FromUSQuart(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.USQuartToCubicMeters)), nameof(value))); +/// + /// Creates a new from a value in USPint. + /// + /// The value in USPint. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Volume FromUSPint(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.USPintToCubicMeters)), nameof(value))); +/// + /// Creates a new from a value in USFluidOunce. + /// + /// The value in USFluidOunce. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static Volume FromUSFluidOunce(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.USFluidOunceToCubicMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Volume unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IVolumeUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two Volume values, returning the absolute difference as a non-negative Volume. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Volume operator -(Volume left, Volume right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Divides Volume by Length to produce Area. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Area operator /(Volume left, Length right) => Divide>(left, right); +/// + /// Divides Volume by Area to produce Length. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Length operator /(Volume left, Area right) => Divide>(left, right); +/// + /// Multiplies Volume by Pressure to produce Energy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Energy operator *(Volume left, Pressure right) => Multiply>(left, right); +/// + /// Multiplies Volume by Density to produce Mass. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Mass operator *(Volume left, Density right) => Multiply>(left, right); +/// + /// Multiplies Volume by Concentration to produce AmountOfSubstance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static AmountOfSubstance operator *(Volume left, Concentration right) => Multiply>(left, right); +/// + /// Divides Volume by Duration to produce VolumetricFlowRate. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VolumetricFlowRate operator /(Volume left, Duration right) => Divide>(left, right); +/// + /// Divides Volume by VolumetricFlowRate to produce Duration. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Duration operator /(Volume left, VolumetricFlowRate right) => Divide>(left, right); +/// + /// Multiplies Volume by ElectricPowerDensity to produce Power. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Power operator *(Volume left, ElectricPowerDensity right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/VolumetricFlowRate.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/VolumetricFlowRate.g.cs new file mode 100644 index 0000000..e59b4b0 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/VolumetricFlowRate.g.cs @@ -0,0 +1,54 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Magnitude (Vector0) quantity for the VolumetricFlowRate dimension. +/// +/// The numeric storage type. +public partial record VolumetricFlowRate : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static VolumetricFlowRate Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.VolumetricFlowRate; + + /// + /// Creates a new from a value in CubicMeterPerSecond. + /// + /// The value in CubicMeterPerSecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static VolumetricFlowRate FromCubicMeterPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new from a value in LiterPerSecond. + /// + /// The value in LiterPerSecond. + /// A new instance. + /// Thrown when the resulting magnitude would be negative. + public static VolumetricFlowRate FromLiterPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.LiterPerSecondToCubicMeterPerSecond)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-VolumetricFlowRate unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IVolumetricFlowRateUnit unit) => unit.FromBase(Value); +/// + /// Subtracts two VolumetricFlowRate values, returning the absolute difference as a non-negative VolumetricFlowRate. + /// Magnitude subtraction stays a magnitude (per the unified-vector model). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static VolumetricFlowRate operator -(VolumetricFlowRate left, VolumetricFlowRate right) => Create(T.Abs(left.Quantity - right.Quantity)); +/// + /// Multiplies VolumetricFlowRate by Duration to produce Volume. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Volume operator *(VolumetricFlowRate left, Duration right) => Multiply>(left, right); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Wavelength.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Wavelength.g.cs new file mode 100644 index 0000000..1d89740 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Wavelength.g.cs @@ -0,0 +1,124 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Spatial period of a periodic wave. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Wavelength : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Wavelength Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Length; + + /// + /// Creates a new Wavelength from a value in Meter. + /// + /// The value in Meter. + /// A new Wavelength instance. + /// Thrown when the resulting magnitude would be negative. + public static Wavelength FromMeter(T value) => Create(Vector0Guards.EnsurePositive(value, nameof(value))); +/// + /// Creates a new Wavelength from a value in Kilometer. + /// + /// The value in Kilometer. + /// A new Wavelength instance. + /// Thrown when the resulting magnitude would be negative. + public static Wavelength FromKilometer(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Wavelength from a value in Centimeter. + /// + /// The value in Centimeter. + /// A new Wavelength instance. + /// Thrown when the resulting magnitude would be negative. + public static Wavelength FromCentimeter(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(MetricMagnitudes.Centi)), nameof(value))); +/// + /// Creates a new Wavelength from a value in Millimeter. + /// + /// The value in Millimeter. + /// A new Wavelength instance. + /// Thrown when the resulting magnitude would be negative. + public static Wavelength FromMillimeter(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Wavelength from a value in Micrometer. + /// + /// The value in Micrometer. + /// A new Wavelength instance. + /// Thrown when the resulting magnitude would be negative. + public static Wavelength FromMicrometer(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new Wavelength from a value in Nanometer. + /// + /// The value in Nanometer. + /// A new Wavelength instance. + /// Thrown when the resulting magnitude would be negative. + public static Wavelength FromNanometer(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Creates a new Wavelength from a value in Angstrom. + /// + /// The value in Angstrom. + /// A new Wavelength instance. + /// Thrown when the resulting magnitude would be negative. + public static Wavelength FromAngstrom(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.AngstromToMeters)), nameof(value))); +/// + /// Creates a new Wavelength from a value in Foot. + /// + /// The value in Foot. + /// A new Wavelength instance. + /// Thrown when the resulting magnitude would be negative. + public static Wavelength FromFoot(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.FeetToMeters)), nameof(value))); +/// + /// Creates a new Wavelength from a value in Inch. + /// + /// The value in Inch. + /// A new Wavelength instance. + /// Thrown when the resulting magnitude would be negative. + public static Wavelength FromInch(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.InchesToMeters)), nameof(value))); +/// + /// Creates a new Wavelength from a value in Yard. + /// + /// The value in Yard. + /// A new Wavelength instance. + /// Thrown when the resulting magnitude would be negative. + public static Wavelength FromYard(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.YardToMeters)), nameof(value))); +/// + /// Creates a new Wavelength from a value in Mile. + /// + /// The value in Mile. + /// A new Wavelength instance. + /// Thrown when the resulting magnitude would be negative. + public static Wavelength FromMile(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.MileToMeters)), nameof(value))); +/// + /// Creates a new Wavelength from a value in NauticalMile. + /// + /// The value in NauticalMile. + /// A new Wavelength instance. + /// Thrown when the resulting magnitude would be negative. + public static Wavelength FromNauticalMile(T value) => Create(Vector0Guards.EnsurePositive((value * T.CreateChecked(Units.ConversionConstants.NauticalMileToMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Length unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILengthUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Length. + public static implicit operator Length(Wavelength value) => Length.Create(value.Value); +/// Explicit conversion from Length. + public static explicit operator Wavelength(Length value) => Create(value.Value); +/// Creates a Wavelength from a Length value. + public static Wavelength From(Length value) => Create(value.Value); +/// Subtracts two Wavelength values, returning the absolute difference as a non-negative Wavelength. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Wavelength operator -(Wavelength left, Wavelength right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Weight.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Weight.g.cs new file mode 100644 index 0000000..779575c --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Weight.g.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Gravitational force on an object. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Weight : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Weight Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Force; + + /// + /// Creates a new Weight from a value in Newton. + /// + /// The value in Newton. + /// A new Weight instance. + /// Thrown when the resulting magnitude would be negative. + public static Weight FromNewton(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Weight from a value in Kilonewton. + /// + /// The value in Kilonewton. + /// A new Weight instance. + /// Thrown when the resulting magnitude would be negative. + public static Weight FromKilonewton(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Weight from a value in Dyne. + /// + /// The value in Dyne. + /// A new Weight instance. + /// Thrown when the resulting magnitude would be negative. + public static Weight FromDyne(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.DyneToNewtons)), nameof(value))); +/// + /// Creates a new Weight from a value in PoundForce. + /// + /// The value in PoundForce. + /// A new Weight instance. + /// Thrown when the resulting magnitude would be negative. + public static Weight FromPoundForce(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PoundForceToNewtons)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Force unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IForceUnit unit) => unit.FromBase(Value); +/// Implicit conversion to ForceMagnitude. + public static implicit operator ForceMagnitude(Weight value) => ForceMagnitude.Create(value.Value); +/// Explicit conversion from ForceMagnitude. + public static explicit operator Weight(ForceMagnitude value) => Create(value.Value); +/// Creates a Weight from a ForceMagnitude value. + public static Weight From(ForceMagnitude value) => Create(value.Value); +/// Subtracts two Weight values, returning the absolute difference as a non-negative Weight. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Weight operator -(Weight left, Weight right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/WeightVector.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/WeightVector.g.cs new file mode 100644 index 0000000..ded7986 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/WeightVector.g.cs @@ -0,0 +1,115 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// Gravitational force vector. +/// Semantic overload of . +/// +public partial record WeightVector : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static WeightVector Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static WeightVector One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static WeightVector UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static WeightVector UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static WeightVector UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(WeightVector other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public WeightVector Cross(WeightVector other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(WeightVector other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(WeightVector other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public WeightVector Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static WeightVector operator +(WeightVector left, WeightVector right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static WeightVector operator -(WeightVector left, WeightVector right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static WeightVector operator *(WeightVector vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static WeightVector operator *(T scalar, WeightVector vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static WeightVector operator /(WeightVector vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static WeightVector operator -(WeightVector vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Implicit conversion to Force3D. + public static implicit operator Force3D(WeightVector value) => new() { X = value.X, Y = value.Y, Z = value.Z }; + + /// Explicit conversion from Force3D. + public static explicit operator WeightVector(Force3D value) => new() { X = value.X, Y = value.Y, Z = value.Z }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Width.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Width.g.cs new file mode 100644 index 0000000..5a61f5a --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Width.g.cs @@ -0,0 +1,124 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Horizontal extent of an object or space. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Width : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Width Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Length; + + /// + /// Creates a new Width from a value in Meter. + /// + /// The value in Meter. + /// A new Width instance. + /// Thrown when the resulting magnitude would be negative. + public static Width FromMeter(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Width from a value in Kilometer. + /// + /// The value in Kilometer. + /// A new Width instance. + /// Thrown when the resulting magnitude would be negative. + public static Width FromKilometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Width from a value in Centimeter. + /// + /// The value in Centimeter. + /// A new Width instance. + /// Thrown when the resulting magnitude would be negative. + public static Width FromCentimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Centi)), nameof(value))); +/// + /// Creates a new Width from a value in Millimeter. + /// + /// The value in Millimeter. + /// A new Width instance. + /// Thrown when the resulting magnitude would be negative. + public static Width FromMillimeter(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Milli)), nameof(value))); +/// + /// Creates a new Width from a value in Micrometer. + /// + /// The value in Micrometer. + /// A new Width instance. + /// Thrown when the resulting magnitude would be negative. + public static Width FromMicrometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Micro)), nameof(value))); +/// + /// Creates a new Width from a value in Nanometer. + /// + /// The value in Nanometer. + /// A new Width instance. + /// Thrown when the resulting magnitude would be negative. + public static Width FromNanometer(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Nano)), nameof(value))); +/// + /// Creates a new Width from a value in Angstrom. + /// + /// The value in Angstrom. + /// A new Width instance. + /// Thrown when the resulting magnitude would be negative. + public static Width FromAngstrom(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AngstromToMeters)), nameof(value))); +/// + /// Creates a new Width from a value in Foot. + /// + /// The value in Foot. + /// A new Width instance. + /// Thrown when the resulting magnitude would be negative. + public static Width FromFoot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FeetToMeters)), nameof(value))); +/// + /// Creates a new Width from a value in Inch. + /// + /// The value in Inch. + /// A new Width instance. + /// Thrown when the resulting magnitude would be negative. + public static Width FromInch(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.InchesToMeters)), nameof(value))); +/// + /// Creates a new Width from a value in Yard. + /// + /// The value in Yard. + /// A new Width instance. + /// Thrown when the resulting magnitude would be negative. + public static Width FromYard(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.YardToMeters)), nameof(value))); +/// + /// Creates a new Width from a value in Mile. + /// + /// The value in Mile. + /// A new Width instance. + /// Thrown when the resulting magnitude would be negative. + public static Width FromMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MileToMeters)), nameof(value))); +/// + /// Creates a new Width from a value in NauticalMile. + /// + /// The value in NauticalMile. + /// A new Width instance. + /// Thrown when the resulting magnitude would be negative. + public static Width FromNauticalMile(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.NauticalMileToMeters)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Length unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.ILengthUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Length. + public static implicit operator Length(Width value) => Length.Create(value.Value); +/// Explicit conversion from Length. + public static explicit operator Width(Length value) => Create(value.Value); +/// Creates a Width from a Length value. + public static Width From(Length value) => Create(value.Value); +/// Subtracts two Width values, returning the absolute difference as a non-negative Width. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Width operator -(Width left, Width right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/WindSpeed.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/WindSpeed.g.cs new file mode 100644 index 0000000..4aa704e --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/WindSpeed.g.cs @@ -0,0 +1,75 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Speed of wind movement. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record WindSpeed : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static WindSpeed Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// + /// Creates a new WindSpeed from a value in MeterPerSecond. + /// + /// The value in MeterPerSecond. + /// A new WindSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static WindSpeed FromMeterPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new WindSpeed from a value in KilometerPerHour. + /// + /// The value in KilometerPerHour. + /// A new WindSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static WindSpeed FromKilometerPerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilometerPerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new WindSpeed from a value in MilePerHour. + /// + /// The value in MilePerHour. + /// A new WindSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static WindSpeed FromMilePerHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.MilePerHourToMeterPerSecond)), nameof(value))); +/// + /// Creates a new WindSpeed from a value in FootPerSecond. + /// + /// The value in FootPerSecond. + /// A new WindSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static WindSpeed FromFootPerSecond(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.FootPerSecondToMeterPerSecond)), nameof(value))); +/// + /// Creates a new WindSpeed from a value in Knot. + /// + /// The value in Knot. + /// A new WindSpeed instance. + /// Thrown when the resulting magnitude would be negative. + public static WindSpeed FromKnot(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KnotToMeterPerSecond)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Velocity unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IVelocityUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Speed. + public static implicit operator Speed(WindSpeed value) => Speed.Create(value.Value); +/// Explicit conversion from Speed. + public static explicit operator WindSpeed(Speed value) => Create(value.Value); +/// Creates a WindSpeed from a Speed value. + public static WindSpeed From(Speed value) => Create(value.Value); +/// Subtracts two WindSpeed values, returning the absolute difference as a non-negative WindSpeed. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static WindSpeed operator -(WindSpeed left, WindSpeed right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/WindVelocity3D.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/WindVelocity3D.g.cs new file mode 100644 index 0000000..d82867d --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/WindVelocity3D.g.cs @@ -0,0 +1,115 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// Wind velocity vector in 3D space. +/// Semantic overload of . +/// +public partial record WindVelocity3D : IVector3, T> + where T : struct, INumber +{ + /// Gets the X component. + public T X { get; init; } + + /// Gets the Y component. + public T Y { get; init; } + + /// Gets the Z component. + public T Z { get; init; } + + /// Gets a vector with all components set to zero. + public static WindVelocity3D Zero => new() { X = T.Zero, Y = T.Zero, Z = T.Zero }; + + /// Gets a vector with all components set to one. + public static WindVelocity3D One => new() { X = T.One, Y = T.One, Z = T.One }; + + /// Gets the unit vector for the X-axis. + public static WindVelocity3D UnitX => new() { X = T.One, Y = T.Zero, Z = T.Zero }; + + /// Gets the unit vector for the Y-axis. + public static WindVelocity3D UnitY => new() { X = T.Zero, Y = T.One, Z = T.Zero }; + + /// Gets the unit vector for the Z-axis. + public static WindVelocity3D UnitZ => new() { X = T.Zero, Y = T.Zero, Z = T.One }; + + /// Calculates the length of the vector. + public T Length() + { + T sum = (X * X) + (Y * Y) + (Z * Z); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared length of the vector. + public T LengthSquared() => (X * X) + (Y * Y) + (Z * Z); + + /// Calculates the dot product of two vectors. + public T Dot(WindVelocity3D other) => (X * other.X) + (Y * other.Y) + (Z * other.Z); + + /// Calculates the cross product of two vectors. + public WindVelocity3D Cross(WindVelocity3D other) + { + return new() { X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }; + } + + /// Calculates the distance between two vectors. + public T Distance(WindVelocity3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + T sum = (dX * dX) + (dY * dY) + (dZ * dZ); + double asDouble = double.CreateChecked(sum); + return T.CreateChecked(Math.Sqrt(asDouble)); + } + + /// Calculates the squared distance between two vectors. + public T DistanceSquared(WindVelocity3D other) + { + T dX = X - other.X; + T dY = Y - other.Y; + T dZ = Z - other.Z; + return (dX * dX) + (dY * dY) + (dZ * dZ); + } + + /// Returns a normalized version of the vector. + public WindVelocity3D Normalize() + { + T len = Length(); + return new() { X = X / len, Y = Y / len, Z = Z / len }; + } + + /// Adds two vectors. + public static WindVelocity3D operator +(WindVelocity3D left, WindVelocity3D right) => new() { X = left.X + right.X, Y = left.Y + right.Y, Z = left.Z + right.Z }; + + /// Subtracts two vectors. + public static WindVelocity3D operator -(WindVelocity3D left, WindVelocity3D right) => new() { X = left.X - right.X, Y = left.Y - right.Y, Z = left.Z - right.Z }; + + /// Multiplies a vector by a scalar. + public static WindVelocity3D operator *(WindVelocity3D vector, T scalar) => new() { X = vector.X * scalar, Y = vector.Y * scalar, Z = vector.Z * scalar }; + + /// Multiplies a scalar by a vector. + public static WindVelocity3D operator *(T scalar, WindVelocity3D vector) => new() { X = scalar * vector.X, Y = scalar * vector.Y, Z = scalar * vector.Z }; + + /// Divides a vector by a scalar. + public static WindVelocity3D operator /(WindVelocity3D vector, T scalar) => new() { X = vector.X / scalar, Y = vector.Y / scalar, Z = vector.Z / scalar }; + + /// Negates a vector. + public static WindVelocity3D operator -(WindVelocity3D vector) => new() { X = -vector.X, Y = -vector.Y, Z = -vector.Z }; + /// Implicit conversion to Velocity3D. + public static implicit operator Velocity3D(WindVelocity3D value) => new() { X = value.X, Y = value.Y, Z = value.Z }; + + /// Explicit conversion from Velocity3D. + public static explicit operator WindVelocity3D(Velocity3D value) => new() { X = value.X, Y = value.Y, Z = value.Z }; + +}; diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Work.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Work.g.cs new file mode 100644 index 0000000..aa3b5a9 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/Work.g.cs @@ -0,0 +1,103 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Energy transferred by a force. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record Work : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static Work Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// + /// Creates a new Work from a value in Joule. + /// + /// The value in Joule. + /// A new Work instance. + /// Thrown when the resulting magnitude would be negative. + public static Work FromJoule(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new Work from a value in Kilojoule. + /// + /// The value in Kilojoule. + /// A new Work instance. + /// Thrown when the resulting magnitude would be negative. + public static Work FromKilojoule(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new Work from a value in ElectronVolt. + /// + /// The value in ElectronVolt. + /// A new Work instance. + /// Thrown when the resulting magnitude would be negative. + public static Work FromElectronVolt(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ElectronVoltToJoules)), nameof(value))); +/// + /// Creates a new Work from a value in Calorie. + /// + /// The value in Calorie. + /// A new Work instance. + /// Thrown when the resulting magnitude would be negative. + public static Work FromCalorie(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.CalorieToJoules)), nameof(value))); +/// + /// Creates a new Work from a value in Kilocalorie. + /// + /// The value in Kilocalorie. + /// A new Work instance. + /// Thrown when the resulting magnitude would be negative. + public static Work FromKilocalorie(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilocalorieToJoules)), nameof(value))); +/// + /// Creates a new Work from a value in KilowattHour. + /// + /// The value in KilowattHour. + /// A new Work instance. + /// Thrown when the resulting magnitude would be negative. + public static Work FromKilowattHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.KilowattHourToJoules)), nameof(value))); +/// + /// Creates a new Work from a value in WattHour. + /// + /// The value in WattHour. + /// A new Work instance. + /// Thrown when the resulting magnitude would be negative. + public static Work FromWattHour(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.WattHourToJoules)), nameof(value))); +/// + /// Creates a new Work from a value in Erg. + /// + /// The value in Erg. + /// A new Work instance. + /// Thrown when the resulting magnitude would be negative. + public static Work FromErg(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.ErgToJoules)), nameof(value))); +/// + /// Creates a new Work from a value in Btu. + /// + /// The value in Btu. + /// A new Work instance. + /// Thrown when the resulting magnitude would be negative. + public static Work FromBtu(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BtuToJoules)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Energy unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IEnergyUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Energy. + public static implicit operator Energy(Work value) => Energy.Create(value.Value); +/// Explicit conversion from Energy. + public static explicit operator Work(Energy value) => Create(value.Value); +/// Creates a Work from a Energy value. + public static Work From(Energy value) => Create(value.Value); +/// Subtracts two Work values, returning the absolute difference as a non-negative Work. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static Work operator -(Work left, Work right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/YoungsModulus.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/YoungsModulus.g.cs new file mode 100644 index 0000000..6e97ae6 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/YoungsModulus.g.cs @@ -0,0 +1,82 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Ratio of stress to strain in a material. +/// Semantic overload of . +/// +/// The numeric storage type. +public partial record YoungsModulus : PhysicalQuantity, T>, IVector0, T> + where T : struct, INumber +{ + /// Gets a quantity with value zero. + public static YoungsModulus Zero => Create(T.Zero); + + /// Gets the physical dimension this quantity belongs to. + public override DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// + /// Creates a new YoungsModulus from a value in Pascal. + /// + /// The value in Pascal. + /// A new YoungsModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static YoungsModulus FromPascal(T value) => Create(Vector0Guards.EnsureNonNegative(value, nameof(value))); +/// + /// Creates a new YoungsModulus from a value in Kilopascal. + /// + /// The value in Kilopascal. + /// A new YoungsModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static YoungsModulus FromKilopascal(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(MetricMagnitudes.Kilo)), nameof(value))); +/// + /// Creates a new YoungsModulus from a value in Bar. + /// + /// The value in Bar. + /// A new YoungsModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static YoungsModulus FromBar(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.BarToPascals)), nameof(value))); +/// + /// Creates a new YoungsModulus from a value in Atmosphere. + /// + /// The value in Atmosphere. + /// A new YoungsModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static YoungsModulus FromAtmosphere(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.AtmosphereToPascals)), nameof(value))); +/// + /// Creates a new YoungsModulus from a value in Psi. + /// + /// The value in Psi. + /// A new YoungsModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static YoungsModulus FromPsi(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.PsiToPascals)), nameof(value))); +/// + /// Creates a new YoungsModulus from a value in Torr. + /// + /// The value in Torr. + /// A new YoungsModulus instance. + /// Thrown when the resulting magnitude would be negative. + public static YoungsModulus FromTorr(T value) => Create(Vector0Guards.EnsureNonNegative((value * T.CreateChecked(Units.ConversionConstants.TorrToPascals)), nameof(value))); +/// + /// Converts this quantity's SI-base value to the value in . + /// Cross-dimension calls (e.g. passing a non-Pressure unit) fail at compile time. + /// + /// The dimensionally-compatible target unit. + /// The value expressed in . + public T In(global::ktsu.Semantics.Quantities.IPressureUnit unit) => unit.FromBase(Value); +/// Implicit conversion to Pressure. + public static implicit operator Pressure(YoungsModulus value) => Pressure.Create(value.Value); +/// Explicit conversion from Pressure. + public static explicit operator YoungsModulus(Pressure value) => Create(value.Value); +/// Creates a YoungsModulus from a Pressure value. + public static YoungsModulus From(Pressure value) => Create(value.Value); +/// Subtracts two YoungsModulus values, returning the absolute difference as a non-negative YoungsModulus. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Physics quantity operator")] public static YoungsModulus operator -(YoungsModulus left, YoungsModulus right) => Create(T.Abs(left.Quantity - right.Quantity)); +}; + diff --git a/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.UnitsGenerator/Units.g.cs b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.UnitsGenerator/Units.g.cs new file mode 100644 index 0000000..ea08567 --- /dev/null +++ b/Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.UnitsGenerator/Units.g.cs @@ -0,0 +1,5876 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. +// + +namespace ktsu.Semantics.Quantities.Units; + +using ktsu.Semantics.Quantities; +using static ktsu.Semantics.Quantities.Units.ConversionConstants; + +/// +/// Meter - SI base unit of length. +/// +public sealed record Meter : IUnit, ILengthUnit +{ + /// Gets the full name of the unit. + public string Name => "Meter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "m"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIBase; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Length; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Meter() { } + +}; + +/// +/// Kilometer - 1000 meters. +/// +public sealed record Kilometer : IUnit, ILengthUnit +{ + /// Gets the full name of the unit. + public string Name => "Kilometer"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "km"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Length; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Kilo; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Kilometer() { } + +}; + +/// +/// Centimeter - 0.01 meters. +/// +public sealed record Centimeter : IUnit, ILengthUnit +{ + /// Gets the full name of the unit. + public string Name => "Centimeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "cm"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Length; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Centi; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Centimeter() { } + +}; + +/// +/// Millimeter - 0.001 meters. +/// +public sealed record Millimeter : IUnit, ILengthUnit +{ + /// Gets the full name of the unit. + public string Name => "Millimeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "mm"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Length; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Milli; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Millimeter() { } + +}; + +/// +/// Foot - Imperial unit of length. +/// +public sealed record Foot : IUnit, ILengthUnit +{ + /// Gets the full name of the unit. + public string Name => "Foot"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "ft"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Length; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => FeetToMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Foot() { } + +}; + +/// +/// Inch - Imperial unit of length. +/// +public sealed record Inch : IUnit, ILengthUnit +{ + /// Gets the full name of the unit. + public string Name => "Inch"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "in"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Length; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => InchesToMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Inch() { } + +}; + +/// +/// Micrometer - 0.000001 meters. +/// +public sealed record Micrometer : IUnit, ILengthUnit +{ + /// Gets the full name of the unit. + public string Name => "Micrometer"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "μm"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Length; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Micro; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Micrometer() { } + +}; + +/// +/// Nanometer - 0.000000001 meters. +/// +public sealed record Nanometer : IUnit, ILengthUnit +{ + /// Gets the full name of the unit. + public string Name => "Nanometer"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "nm"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Length; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Nano; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Nanometer() { } + +}; + +/// +/// Angstrom - 10⁻¹⁰ meters, used for atomic scales. +/// +public sealed record Angstrom : IUnit, ILengthUnit +{ + /// Gets the full name of the unit. + public string Name => "Angstrom"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Å"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Length; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => AngstromToMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Angstrom() { } + +}; + +/// +/// Yard - Imperial unit of length. +/// +public sealed record Yard : IUnit, ILengthUnit +{ + /// Gets the full name of the unit. + public string Name => "Yard"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "yd"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Length; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => YardToMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Yard() { } + +}; + +/// +/// Mile - Imperial unit of length. +/// +public sealed record Mile : IUnit, ILengthUnit +{ + /// Gets the full name of the unit. + public string Name => "Mile"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "mi"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Length; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MileToMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Mile() { } + +}; + +/// +/// Kilogram - SI base unit of mass. +/// +public sealed record Kilogram : IUnit, IMassUnit +{ + /// Gets the full name of the unit. + public string Name => "Kilogram"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kg"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIBase; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Mass; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Kilogram() { } + +}; + +/// +/// Gram - 0.001 kilograms. +/// +public sealed record Gram : IUnit, IMassUnit +{ + /// Gets the full name of the unit. + public string Name => "Gram"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "g"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Mass; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Milli; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Gram() { } + +}; + +/// +/// Metric ton - 1000 kilograms. +/// +public sealed record Ton : IUnit, IMassUnit +{ + /// Gets the full name of the unit. + public string Name => "Ton"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "t"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Mass; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => TonToKilograms; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Ton() { } + +}; + +/// +/// Pound - Imperial unit of mass. +/// +public sealed record Pound : IUnit, IMassUnit +{ + /// Gets the full name of the unit. + public string Name => "Pound"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "lb"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Mass; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => PoundToKilograms; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Pound() { } + +}; + +/// +/// Ounce - Imperial unit of mass. +/// +public sealed record Ounce : IUnit, IMassUnit +{ + /// Gets the full name of the unit. + public string Name => "Ounce"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "oz"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Mass; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => OunceToKilograms; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Ounce() { } + +}; + +/// +/// Second - SI base unit of time. +/// +public sealed record Second : IUnit, ITimeUnit +{ + /// Gets the full name of the unit. + public string Name => "Second"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "s"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIBase; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Time; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Second() { } + +}; + +/// +/// Minute - 60 seconds. +/// +public sealed record Minute : IUnit, ITimeUnit +{ + /// Gets the full name of the unit. + public string Name => "Minute"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "min"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Time; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MinuteToSeconds; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Minute() { } + +}; + +/// +/// Hour - 3600 seconds. +/// +public sealed record Hour : IUnit, ITimeUnit +{ + /// Gets the full name of the unit. + public string Name => "Hour"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "h"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Time; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => HourToSeconds; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Hour() { } + +}; + +/// +/// Day - 86400 seconds. +/// +public sealed record Day : IUnit, ITimeUnit +{ + /// Gets the full name of the unit. + public string Name => "Day"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "d"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Time; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => DayToSeconds; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Day() { } + +}; + +/// +/// Year - 365.25 days (31557600 seconds). +/// +public sealed record Year : IUnit, ITimeUnit +{ + /// Gets the full name of the unit. + public string Name => "Year"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "yr"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Time; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => YearToSeconds; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Year() { } + +}; + +/// +/// Millisecond - 0.001 seconds. +/// +public sealed record Millisecond : IUnit, ITimeUnit +{ + /// Gets the full name of the unit. + public string Name => "Millisecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "ms"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Time; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Milli; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Millisecond() { } + +}; + +/// +/// Microsecond - 0.000001 seconds. +/// +public sealed record Microsecond : IUnit, ITimeUnit +{ + /// Gets the full name of the unit. + public string Name => "Microsecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "μs"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Time; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Micro; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Microsecond() { } + +}; + +/// +/// Square meter - SI derived unit of area. +/// +public sealed record SquareMeter : IUnit, IAreaUnit, INuclearCrossSectionUnit +{ + /// Gets the full name of the unit. + public string Name => "SquareMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "m²"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Area; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public SquareMeter() { } + +}; + +/// +/// Square foot - Imperial unit of area. +/// +public sealed record SquareFoot : IUnit, IAreaUnit +{ + /// Gets the full name of the unit. + public string Name => "SquareFoot"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "ft²"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Area; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => SquareFootToSquareMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public SquareFoot() { } + +}; + +/// +/// Square inch - Imperial unit of area. +/// +public sealed record SquareInch : IUnit, IAreaUnit +{ + /// Gets the full name of the unit. + public string Name => "SquareInch"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "in²"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Area; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => SquareInchToSquareMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public SquareInch() { } + +}; + +/// +/// Cubic meter - SI derived unit of volume. +/// +public sealed record CubicMeter : IUnit, IVolumeUnit +{ + /// Gets the full name of the unit. + public string Name => "CubicMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "m³"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Volume; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public CubicMeter() { } + +}; + +/// +/// Liter - 0.001 cubic meters. +/// +public sealed record Liter : IUnit, IVolumeUnit +{ + /// Gets the full name of the unit. + public string Name => "Liter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "L"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Volume; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => LiterToCubicMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Liter() { } + +}; + +/// +/// Milliliter - 0.001 liters. +/// +public sealed record Milliliter : IUnit, IVolumeUnit +{ + /// Gets the full name of the unit. + public string Name => "Milliliter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "mL"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Volume; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Milli; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Milliliter() { } + +}; + +/// +/// US gallon - Imperial unit of volume. +/// +public sealed record Gallon : IUnit, IVolumeUnit +{ + /// Gets the full name of the unit. + public string Name => "Gallon"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "gal"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Volume; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => GallonToCubicMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Gallon() { } + +}; + +/// +/// Dimensionless - Pure number or ratio with no physical units. +/// +public sealed record Dimensionless : IUnit, IDimensionlessUnit +{ + /// Gets the full name of the unit. + public string Name => "Dimensionless"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "1"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIBase; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Dimensionless() { } + +}; + +/// +/// Radian - SI derived unit of plane angle. +/// +public sealed record Radian : IUnit, IDimensionlessUnit, IAngularDisplacementUnit +{ + /// Gets the full name of the unit. + public string Name => "Radian"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "rad"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Radian() { } + +}; + +/// +/// Degree - Common unit of plane angle. +/// +public sealed record Degree : IUnit, IDimensionlessUnit, IAngularDisplacementUnit +{ + /// Gets the full name of the unit. + public string Name => "Degree"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "°"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => DegreeToRadians; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Degree() { } + +}; + +/// +/// Nautical mile - 1852 meters, used in navigation. +/// +public sealed record NauticalMile : IUnit, ILengthUnit +{ + /// Gets the full name of the unit. + public string Name => "NauticalMile"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "nmi"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Length; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => NauticalMileToMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public NauticalMile() { } + +}; + +/// +/// Stone - Imperial unit of mass (14 pounds). +/// +public sealed record Stone : IUnit, IMassUnit +{ + /// Gets the full name of the unit. + public string Name => "Stone"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "st"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Mass; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => StoneToKilograms; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Stone() { } + +}; + +/// +/// Short ton - US customary unit of mass (2000 pounds). +/// +public sealed record ShortTon : IUnit, IMassUnit +{ + /// Gets the full name of the unit. + public string Name => "ShortTon"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "ton"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.USCustomary; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Mass; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => ShortTonToKilograms; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public ShortTon() { } + +}; + +/// +/// Atomic mass unit - 1/12 the mass of a carbon-12 atom. +/// +public sealed record AtomicMassUnit : IUnit, IMassUnit +{ + /// Gets the full name of the unit. + public string Name => "AtomicMassUnit"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "u"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Atomic; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Mass; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => AtomicMassUnitToKilograms; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public AtomicMassUnit() { } + +}; + +/// +/// Week - 7 days. +/// +public sealed record Week : IUnit, ITimeUnit +{ + /// Gets the full name of the unit. + public string Name => "Week"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "wk"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Time; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => WeekToSeconds; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Week() { } + +}; + +/// +/// Nanosecond - 1e-9 seconds. +/// +public sealed record Nanosecond : IUnit, ITimeUnit +{ + /// Gets the full name of the unit. + public string Name => "Nanosecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "ns"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Time; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Nano; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Nanosecond() { } + +}; + +/// +/// Square kilometer - 1e6 square meters. +/// +public sealed record SquareKilometer : IUnit, IAreaUnit +{ + /// Gets the full name of the unit. + public string Name => "SquareKilometer"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "km²"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Area; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => SquareKilometerToSquareMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public SquareKilometer() { } + +}; + +/// +/// Square centimeter - 1e-4 square meters. +/// +public sealed record SquareCentimeter : IUnit, IAreaUnit +{ + /// Gets the full name of the unit. + public string Name => "SquareCentimeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "cm²"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Area; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => SquareCentimeterToSquareMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public SquareCentimeter() { } + +}; + +/// +/// Square mile - Imperial unit of area. +/// +public sealed record SquareMile : IUnit, IAreaUnit +{ + /// Gets the full name of the unit. + public string Name => "SquareMile"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "mi²"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Area; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => SquareMileToSquareMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public SquareMile() { } + +}; + +/// +/// Hectare - metric unit of area (10000 m²). +/// +public sealed record Hectare : IUnit, IAreaUnit +{ + /// Gets the full name of the unit. + public string Name => "Hectare"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "ha"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Metric; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Area; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => HectareToSquareMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Hectare() { } + +}; + +/// +/// Acre - Imperial unit of area. +/// +public sealed record Acre : IUnit, IAreaUnit +{ + /// Gets the full name of the unit. + public string Name => "Acre"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "ac"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Area; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => AcreToSquareMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Acre() { } + +}; + +/// +/// Cubic centimeter - 1e-6 cubic meters. +/// +public sealed record CubicCentimeter : IUnit, IVolumeUnit +{ + /// Gets the full name of the unit. + public string Name => "CubicCentimeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "cm³"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Volume; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => CubicCentimeterToCubicMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public CubicCentimeter() { } + +}; + +/// +/// Cubic foot - Imperial unit of volume. +/// +public sealed record CubicFoot : IUnit, IVolumeUnit +{ + /// Gets the full name of the unit. + public string Name => "CubicFoot"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "ft³"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Volume; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => CubicFootToCubicMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public CubicFoot() { } + +}; + +/// +/// Cubic inch - Imperial unit of volume. +/// +public sealed record CubicInch : IUnit, IVolumeUnit +{ + /// Gets the full name of the unit. + public string Name => "CubicInch"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "in³"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Volume; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => CubicInchToCubicMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public CubicInch() { } + +}; + +/// +/// Imperial gallon - British unit of volume. +/// +public sealed record ImperialGallon : IUnit, IVolumeUnit +{ + /// Gets the full name of the unit. + public string Name => "ImperialGallon"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "imp gal"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Volume; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => ImperialGallonToCubicMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public ImperialGallon() { } + +}; + +/// +/// US liquid quart - US customary unit of volume. +/// +public sealed record USQuart : IUnit, IVolumeUnit +{ + /// Gets the full name of the unit. + public string Name => "USQuart"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "qt"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.USCustomary; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Volume; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => USQuartToCubicMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public USQuart() { } + +}; + +/// +/// US liquid pint - US customary unit of volume. +/// +public sealed record USPint : IUnit, IVolumeUnit +{ + /// Gets the full name of the unit. + public string Name => "USPint"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "pt"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.USCustomary; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Volume; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => USPintToCubicMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public USPint() { } + +}; + +/// +/// US fluid ounce - US customary unit of volume. +/// +public sealed record USFluidOunce : IUnit, IVolumeUnit +{ + /// Gets the full name of the unit. + public string Name => "USFluidOunce"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "fl oz"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.USCustomary; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Volume; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => USFluidOunceToCubicMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public USFluidOunce() { } + +}; + +/// +/// Gradian - 1/400 of a full circle (π/200 rad). +/// +public sealed record Gradian : IUnit, IDimensionlessUnit, IAngularDisplacementUnit +{ + /// Gets the full name of the unit. + public string Name => "Gradian"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "grad"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => GradianToRadians; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Gradian() { } + +}; + +/// +/// Revolution - one full circle (2π rad). +/// +public sealed record Revolution : IUnit, IDimensionlessUnit, IAngularDisplacementUnit +{ + /// Gets the full name of the unit. + public string Name => "Revolution"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "rev"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => RevolutionToRadians; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Revolution() { } + +}; + +/// +/// Milliradian - 0.001 radians. +/// +public sealed record Milliradian : IUnit, IDimensionlessUnit, IAngularDisplacementUnit +{ + /// Gets the full name of the unit. + public string Name => "Milliradian"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "mrad"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Milli; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Milliradian() { } + +}; + +/// +/// Percent - one hundredth of unity. +/// +public sealed record Percent : IUnit, IDimensionlessUnit +{ + /// Gets the full name of the unit. + public string Name => "Percent"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "%"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => PercentToRatio; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Percent() { } + +}; + +/// +/// Parts per million - 1e-6 dimensionless ratio. +/// +public sealed record PartPerMillion : IUnit, IDimensionlessUnit +{ + /// Gets the full name of the unit. + public string Name => "PartPerMillion"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "ppm"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => PartPerMillionToRatio; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public PartPerMillion() { } + +}; + +/// +/// Parts per billion - 1e-9 dimensionless ratio. +/// +public sealed record PartPerBillion : IUnit, IDimensionlessUnit +{ + /// Gets the full name of the unit. + public string Name => "PartPerBillion"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "ppb"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => PartPerBillionToRatio; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public PartPerBillion() { } + +}; + +/// +/// Percent by weight - mass fraction expressed as a percentage. +/// +public sealed record PercentByWeight : IUnit, IDimensionlessUnit +{ + /// Gets the full name of the unit. + public string Name => "PercentByWeight"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "% w/w"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Dimensionless; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => PercentByWeightToRatio; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public PercentByWeight() { } + +}; + +/// +/// Newton - SI derived unit of force. +/// +public sealed record Newton : IUnit, IForceUnit +{ + /// Gets the full name of the unit. + public string Name => "Newton"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "N"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Force; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Newton() { } + +}; + +/// +/// Pascal - SI derived unit of pressure. +/// +public sealed record Pascal : IUnit, IPressureUnit +{ + /// Gets the full name of the unit. + public string Name => "Pascal"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Pa"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Pascal() { } + +}; + +/// +/// Meters per second - SI derived unit of velocity. +/// +public sealed record MeterPerSecond : IUnit, IVelocityUnit +{ + /// Gets the full name of the unit. + public string Name => "MeterPerSecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "m/s"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public MeterPerSecond() { } + +}; + +/// +/// Meters per second squared - SI derived unit of acceleration. +/// +public sealed record MeterPerSecondSquared : IUnit, IAccelerationUnit +{ + /// Gets the full name of the unit. + public string Name => "MeterPerSecondSquared"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "m/s²"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Acceleration; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public MeterPerSecondSquared() { } + +}; + +/// +/// Bar - Metric unit of pressure. +/// +public sealed record Bar : IUnit, IPressureUnit +{ + /// Gets the full name of the unit. + public string Name => "Bar"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "bar"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => BarToPascals; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Bar() { } + +}; + +/// +/// Standard atmosphere - Unit of pressure. +/// +public sealed record Atmosphere : IUnit, IPressureUnit +{ + /// Gets the full name of the unit. + public string Name => "Atmosphere"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "atm"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => AtmosphereToPascals; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Atmosphere() { } + +}; + +/// +/// Pounds per square inch - Imperial unit of pressure. +/// +public sealed record Psi : IUnit, IPressureUnit +{ + /// Gets the full name of the unit. + public string Name => "Psi"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "psi"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => PsiToPascals; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Psi() { } + +}; + +/// +/// Kilometers per hour - Common unit of velocity. +/// +public sealed record KilometerPerHour : IUnit, IVelocityUnit +{ + /// Gets the full name of the unit. + public string Name => "KilometerPerHour"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "km/h"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => KilometerPerHourToMeterPerSecond; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public KilometerPerHour() { } + +}; + +/// +/// Miles per hour - Imperial unit of velocity. +/// +public sealed record MilePerHour : IUnit, IVelocityUnit +{ + /// Gets the full name of the unit. + public string Name => "MilePerHour"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "mph"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MilePerHourToMeterPerSecond; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public MilePerHour() { } + +}; + +/// +/// Joule - SI derived unit of energy. +/// +public sealed record Joule : IUnit, IEnergyUnit +{ + /// Gets the full name of the unit. + public string Name => "Joule"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "J"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Joule() { } + +}; + +/// +/// Watt - SI derived unit of power. +/// +public sealed record Watt : IUnit, IPowerUnit +{ + /// Gets the full name of the unit. + public string Name => "Watt"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "W"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Power; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Watt() { } + +}; + +/// +/// Calorie - Thermochemical calorie, energy unit. +/// +public sealed record Calorie : IUnit, IEnergyUnit +{ + /// Gets the full name of the unit. + public string Name => "Calorie"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "cal"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => CalorieToJoules; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Calorie() { } + +}; + +/// +/// Kilowatt-hour - Common unit of electrical energy. +/// +public sealed record KilowattHour : IUnit, IEnergyUnit +{ + /// Gets the full name of the unit. + public string Name => "KilowattHour"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kWh"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => KilowattHourToJoules; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public KilowattHour() { } + +}; + +/// +/// Mechanical horsepower - Imperial unit of power. +/// +public sealed record Horsepower : IUnit, IPowerUnit +{ + /// Gets the full name of the unit. + public string Name => "Horsepower"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "hp"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Power; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => HorsepowerToWatts; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Horsepower() { } + +}; + +/// +/// Newton-second - SI derived unit of momentum. +/// +public sealed record NewtonSecond : IUnit, IMomentumUnit +{ + /// Gets the full name of the unit. + public string Name => "NewtonSecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "N⋅s"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Momentum; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public NewtonSecond() { } + +}; + +/// +/// Electron volt - Energy unit equal to electron charge times one volt. +/// +public sealed record ElectronVolt : IUnit, IEnergyUnit +{ + /// Gets the full name of the unit. + public string Name => "ElectronVolt"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "eV"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => ElectronVoltToJoules; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public ElectronVolt() { } + +}; + +/// +/// Newton-meter - SI derived unit of torque. +/// +public sealed record NewtonMeter : IUnit, ITorqueUnit +{ + /// Gets the full name of the unit. + public string Name => "NewtonMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "N⋅m"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Torque; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public NewtonMeter() { } + +}; + +/// +/// Pound-foot - Imperial unit of torque. +/// +public sealed record PoundFoot : IUnit, ITorqueUnit +{ + /// Gets the full name of the unit. + public string Name => "PoundFoot"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "lb⋅ft"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Torque; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => PoundFootToNewtonMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public PoundFoot() { } + +}; + +/// +/// Kilogram-meter squared - SI derived unit of moment of inertia. +/// +public sealed record KilogramMeterSquared : IUnit, IMomentOfInertiaUnit +{ + /// Gets the full name of the unit. + public string Name => "KilogramMeterSquared"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kg⋅m²"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.MomentOfInertia; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public KilogramMeterSquared() { } + +}; + +/// +/// Kilogram-meter squared per second - SI derived unit of angular momentum. +/// +public sealed record KilogramMeterSquaredPerSecond : IUnit, IAngularMomentumUnit +{ + /// Gets the full name of the unit. + public string Name => "KilogramMeterSquaredPerSecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kg⋅m²/s"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.AngularMomentum; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public KilogramMeterSquaredPerSecond() { } + +}; + +/// +/// Meters per second cubed - SI derived unit of jerk. +/// +public sealed record MeterPerSecondCubed : IUnit, IJerkUnit +{ + /// Gets the full name of the unit. + public string Name => "MeterPerSecondCubed"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "m/s³"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Jerk; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public MeterPerSecondCubed() { } + +}; + +/// +/// Meters per second to the fourth - SI derived unit of snap. +/// +public sealed record MeterPerSecondQuartic : IUnit, ISnapUnit +{ + /// Gets the full name of the unit. + public string Name => "MeterPerSecondQuartic"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "m/s⁴"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Snap; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public MeterPerSecondQuartic() { } + +}; + +/// +/// Feet per second - Imperial unit of velocity. +/// +public sealed record FootPerSecond : IUnit, IVelocityUnit +{ + /// Gets the full name of the unit. + public string Name => "FootPerSecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "ft/s"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => FootPerSecondToMeterPerSecond; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public FootPerSecond() { } + +}; + +/// +/// Knot - one nautical mile per hour. +/// +public sealed record Knot : IUnit, IVelocityUnit +{ + /// Gets the full name of the unit. + public string Name => "Knot"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kn"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Velocity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => KnotToMeterPerSecond; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Knot() { } + +}; + +/// +/// Standard gravity - acceleration of free fall at Earth's surface. +/// +public sealed record StandardGravity : IUnit, IAccelerationUnit +{ + /// Gets the full name of the unit. + public string Name => "StandardGravity"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "g"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Acceleration; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => StandardGravityToMeterPerSecondSquared; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public StandardGravity() { } + +}; + +/// +/// Kilonewton - 1000 newtons. +/// +public sealed record Kilonewton : IUnit, IForceUnit +{ + /// Gets the full name of the unit. + public string Name => "Kilonewton"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kN"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Force; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Kilo; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Kilonewton() { } + +}; + +/// +/// Dyne - CGS unit of force. +/// +public sealed record Dyne : IUnit, IForceUnit +{ + /// Gets the full name of the unit. + public string Name => "Dyne"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "dyn"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.CGS; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Force; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => DyneToNewtons; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Dyne() { } + +}; + +/// +/// Pound-force - Imperial unit of force. +/// +public sealed record PoundForce : IUnit, IForceUnit +{ + /// Gets the full name of the unit. + public string Name => "PoundForce"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "lbf"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Force; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => PoundForceToNewtons; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public PoundForce() { } + +}; + +/// +/// Kilopascal - 1000 pascals. +/// +public sealed record Kilopascal : IUnit, IPressureUnit +{ + /// Gets the full name of the unit. + public string Name => "Kilopascal"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kPa"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Kilo; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Kilopascal() { } + +}; + +/// +/// Torr - 1/760 of a standard atmosphere. +/// +public sealed record Torr : IUnit, IPressureUnit +{ + /// Gets the full name of the unit. + public string Name => "Torr"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Torr"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Pressure; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => TorrToPascals; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Torr() { } + +}; + +/// +/// Kilojoule - 1000 joules. +/// +public sealed record Kilojoule : IUnit, IEnergyUnit +{ + /// Gets the full name of the unit. + public string Name => "Kilojoule"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kJ"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Kilo; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Kilojoule() { } + +}; + +/// +/// Kilocalorie - 1000 thermochemical calories. +/// +public sealed record Kilocalorie : IUnit, IEnergyUnit +{ + /// Gets the full name of the unit. + public string Name => "Kilocalorie"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kcal"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => KilocalorieToJoules; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Kilocalorie() { } + +}; + +/// +/// Watt-hour - 3600 joules. +/// +public sealed record WattHour : IUnit, IEnergyUnit +{ + /// Gets the full name of the unit. + public string Name => "WattHour"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Wh"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => WattHourToJoules; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public WattHour() { } + +}; + +/// +/// Erg - CGS unit of energy. +/// +public sealed record Erg : IUnit, IEnergyUnit +{ + /// Gets the full name of the unit. + public string Name => "Erg"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "erg"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.CGS; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => ErgToJoules; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Erg() { } + +}; + +/// +/// British thermal unit (IT) - Imperial unit of energy. +/// +public sealed record Btu : IUnit, IEnergyUnit +{ + /// Gets the full name of the unit. + public string Name => "Btu"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "BTU"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Energy; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => BtuToJoules; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Btu() { } + +}; + +/// +/// Kilowatt - 1000 watts. +/// +public sealed record Kilowatt : IUnit, IPowerUnit +{ + /// Gets the full name of the unit. + public string Name => "Kilowatt"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kW"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Power; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Kilo; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Kilowatt() { } + +}; + +/// +/// Megawatt - 1e6 watts. +/// +public sealed record Megawatt : IUnit, IPowerUnit +{ + /// Gets the full name of the unit. + public string Name => "Megawatt"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "MW"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Power; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Mega; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Megawatt() { } + +}; + +/// +/// Watt per cubic meter - SI unit of volumetric power density. +/// +public sealed record WattPerCubicMeter : IUnit, IElectricPowerDensityUnit +{ + /// Gets the full name of the unit. + public string Name => "WattPerCubicMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "W/m³"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricPowerDensity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public WattPerCubicMeter() { } + +}; + +/// +/// Kelvin - SI base unit of thermodynamic temperature. +/// +public sealed record Kelvin : IUnit, ITemperatureUnit +{ + /// Gets the full name of the unit. + public string Name => "Kelvin"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "K"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIBase; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Temperature; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Kelvin() { } + +}; + +/// +/// Celsius - Common temperature scale. +/// +public sealed record Celsius : IUnit, ITemperatureUnit +{ + /// Gets the full name of the unit. + public string Name => "Celsius"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "°C"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Temperature; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => CelsiusToKelvinOffset; + + /// Initializes a new instance of the unit. + public Celsius() { } + +}; + +/// +/// Fahrenheit - Imperial temperature scale. +/// +public sealed record Fahrenheit : IUnit, ITemperatureUnit +{ + /// Gets the full name of the unit. + public string Name => "Fahrenheit"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "°F"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Temperature; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => FahrenheitScale; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => FahrenheitToKelvinOffset; + + /// Initializes a new instance of the unit. + public Fahrenheit() { } + +}; + +/// +/// Joule per kelvin - SI derived unit of entropy and heat capacity. +/// +public sealed record JoulePerKelvin : IUnit, IEntropyUnit +{ + /// Gets the full name of the unit. + public string Name => "JoulePerKelvin"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "J/K"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Entropy; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public JoulePerKelvin() { } + +}; + +/// +/// Joule per kilogram kelvin - SI derived unit of specific heat capacity. +/// +public sealed record JoulePerKilogramKelvin : IUnit, ISpecificHeatUnit +{ + /// Gets the full name of the unit. + public string Name => "JoulePerKilogramKelvin"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "J/(kg·K)"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.SpecificHeat; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public JoulePerKilogramKelvin() { } + +}; + +/// +/// Watt per meter kelvin - SI derived unit of thermal conductivity. +/// +public sealed record WattPerMeterKelvin : IUnit, IThermalConductivityUnit +{ + /// Gets the full name of the unit. + public string Name => "WattPerMeterKelvin"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "W/(m·K)"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ThermalConductivity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public WattPerMeterKelvin() { } + +}; + +/// +/// Watt per square meter kelvin - SI derived unit of heat transfer coefficient. +/// +public sealed record WattPerSquareMeterKelvin : IUnit, IHeatTransferCoefficientUnit +{ + /// Gets the full name of the unit. + public string Name => "WattPerSquareMeterKelvin"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "W/(m²·K)"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.HeatTransferCoefficient; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public WattPerSquareMeterKelvin() { } + +}; + +/// +/// Per kelvin - SI derived unit of thermal expansion coefficient. +/// +public sealed record PerKelvin : IUnit, IThermalExpansionUnit +{ + /// Gets the full name of the unit. + public string Name => "PerKelvin"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "K⁻¹"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ThermalExpansion; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public PerKelvin() { } + +}; + +/// +/// Rankine - absolute temperature scale with Fahrenheit-sized degrees. +/// +public sealed record Rankine : IUnit, ITemperatureUnit +{ + /// Gets the full name of the unit. + public string Name => "Rankine"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "°R"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Temperature; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => FahrenheitScale; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Rankine() { } + +}; + +/// +/// Kelvin per watt - SI unit of absolute thermal resistance. +/// +public sealed record KelvinPerWatt : IUnit, IThermalResistanceUnit +{ + /// Gets the full name of the unit. + public string Name => "KelvinPerWatt"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "K/W"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ThermalResistance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public KelvinPerWatt() { } + +}; + +/// +/// Ampere - SI base unit of electric current. +/// +public sealed record Ampere : IUnit, IElectricCurrentUnit +{ + /// Gets the full name of the unit. + public string Name => "Ampere"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "A"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIBase; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricCurrent; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Ampere() { } + +}; + +/// +/// Volt - SI derived unit of electric potential. +/// +public sealed record Volt : IUnit, IElectricPotentialUnit +{ + /// Gets the full name of the unit. + public string Name => "Volt"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "V"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricPotential; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Volt() { } + +}; + +/// +/// Volt per meter - SI derived unit of electric field strength. +/// +public sealed record VoltPerMeter : IUnit, IElectricFieldUnit +{ + /// Gets the full name of the unit. + public string Name => "VoltPerMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "V/m"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricField; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public VoltPerMeter() { } + +}; + +/// +/// Ohm - SI derived unit of electric resistance. +/// +public sealed record Ohm : IUnit, IElectricResistanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Ohm"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Ω"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricResistance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Ohm() { } + +}; + +/// +/// Coulomb - SI derived unit of electric charge. +/// +public sealed record Coulomb : IUnit, IElectricChargeUnit +{ + /// Gets the full name of the unit. + public string Name => "Coulomb"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "C"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricCharge; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Coulomb() { } + +}; + +/// +/// Farad - SI derived unit of electric capacitance. +/// +public sealed record Farad : IUnit, IElectricCapacitanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Farad"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "F"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricCapacitance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Farad() { } + +}; + +/// +/// Siemens - SI derived unit of electric conductance. +/// +public sealed record Siemens : IUnit, IElectricConductanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Siemens"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "S"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricConductance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Siemens() { } + +}; + +/// +/// Tesla - SI derived unit of magnetic flux density. +/// +public sealed record Tesla : IUnit, IMagneticFluxDensityUnit +{ + /// Gets the full name of the unit. + public string Name => "Tesla"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "T"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.MagneticFluxDensity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Tesla() { } + +}; + +/// +/// Gauss - CGS unit of magnetic flux density. +/// +public sealed record Gauss : IUnit, IMagneticFluxDensityUnit +{ + /// Gets the full name of the unit. + public string Name => "Gauss"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "G"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.CGS; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.MagneticFluxDensity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => GaussToTesla; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Gauss() { } + +}; + +/// +/// Weber - SI derived unit of magnetic flux. +/// +public sealed record Weber : IUnit, IMagneticFluxUnit +{ + /// Gets the full name of the unit. + public string Name => "Weber"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Wb"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.MagneticFlux; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Weber() { } + +}; + +/// +/// Henry - SI derived unit of inductance. +/// +public sealed record Henry : IUnit, IInductanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Henry"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "H"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Inductance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Henry() { } + +}; + +/// +/// Milliampere - 0.001 amperes. +/// +public sealed record Milliampere : IUnit, IElectricCurrentUnit +{ + /// Gets the full name of the unit. + public string Name => "Milliampere"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "mA"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricCurrent; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Milli; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Milliampere() { } + +}; + +/// +/// Kiloampere - 1000 amperes. +/// +public sealed record Kiloampere : IUnit, IElectricCurrentUnit +{ + /// Gets the full name of the unit. + public string Name => "Kiloampere"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kA"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricCurrent; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Kilo; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Kiloampere() { } + +}; + +/// +/// Kilovolt - 1000 volts. +/// +public sealed record Kilovolt : IUnit, IElectricPotentialUnit +{ + /// Gets the full name of the unit. + public string Name => "Kilovolt"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kV"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricPotential; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Kilo; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Kilovolt() { } + +}; + +/// +/// Kilohm - 1000 ohms. +/// +public sealed record Kilohm : IUnit, IElectricResistanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Kilohm"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kΩ"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricResistance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Kilo; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Kilohm() { } + +}; + +/// +/// Megohm - 1e6 ohms. +/// +public sealed record Megohm : IUnit, IElectricResistanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Megohm"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "MΩ"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricResistance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Mega; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Megohm() { } + +}; + +/// +/// Microfarad - 1e-6 farads. +/// +public sealed record Microfarad : IUnit, IElectricCapacitanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Microfarad"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "μF"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricCapacitance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Micro; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Microfarad() { } + +}; + +/// +/// Nanofarad - 1e-9 farads. +/// +public sealed record Nanofarad : IUnit, IElectricCapacitanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Nanofarad"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "nF"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricCapacitance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Nano; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Nanofarad() { } + +}; + +/// +/// Picofarad - 1e-12 farads. +/// +public sealed record Picofarad : IUnit, IElectricCapacitanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Picofarad"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "pF"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricCapacitance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Pico; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Picofarad() { } + +}; + +/// +/// Ampere-hour - 3600 coulombs of electric charge. +/// +public sealed record AmpereHour : IUnit, IElectricChargeUnit +{ + /// Gets the full name of the unit. + public string Name => "AmpereHour"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Ah"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricCharge; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => AmpereHourToCoulombs; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public AmpereHour() { } + +}; + +/// +/// Volt meter - SI unit of electric flux. +/// +public sealed record VoltMeter : IUnit, IElectricFluxUnit +{ + /// Gets the full name of the unit. + public string Name => "VoltMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "V·m"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricFlux; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public VoltMeter() { } + +}; + +/// +/// Farad per meter - SI unit of permittivity. +/// +public sealed record FaradPerMeter : IUnit, IPermittivityUnit +{ + /// Gets the full name of the unit. + public string Name => "FaradPerMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "F/m"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Permittivity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public FaradPerMeter() { } + +}; + +/// +/// Siemens per meter - SI unit of electrical conductivity. +/// +public sealed record SiemensPerMeter : IUnit, IElectricConductivityUnit +{ + /// Gets the full name of the unit. + public string Name => "SiemensPerMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "S/m"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ElectricConductivity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public SiemensPerMeter() { } + +}; + +/// +/// Volt per pascal - SI unit of transducer sensitivity. +/// +public sealed record VoltPerPascal : IUnit, ISensitivityUnit +{ + /// Gets the full name of the unit. + public string Name => "VoltPerPascal"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "V/Pa"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Sensitivity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public VoltPerPascal() { } + +}; + +/// +/// Radians per second - SI derived unit of angular velocity. +/// +public sealed record RadianPerSecond : IUnit, IAngularVelocityUnit +{ + /// Gets the full name of the unit. + public string Name => "RadianPerSecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "rad/s"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.AngularVelocity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public RadianPerSecond() { } + +}; + +/// +/// Revolutions per minute - Common unit of angular velocity. +/// +public sealed record RevolutionPerMinute : IUnit, IAngularVelocityUnit +{ + /// Gets the full name of the unit. + public string Name => "RevolutionPerMinute"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "rpm"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.AngularVelocity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => RevolutionPerMinuteToRadianPerSecond; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public RevolutionPerMinute() { } + +}; + +/// +/// Radians per second squared - SI derived unit of angular acceleration. +/// +public sealed record RadianPerSecondSquared : IUnit, IAngularAccelerationUnit +{ + /// Gets the full name of the unit. + public string Name => "RadianPerSecondSquared"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "rad/s²"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.AngularAcceleration; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public RadianPerSecondSquared() { } + +}; + +/// +/// Radians per second cubed - SI derived unit of angular jerk. +/// +public sealed record RadianPerSecondCubed : IUnit, IAngularJerkUnit +{ + /// Gets the full name of the unit. + public string Name => "RadianPerSecondCubed"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "rad/s³"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.AngularJerk; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public RadianPerSecondCubed() { } + +}; + +/// +/// Hertz - SI derived unit of frequency. +/// +public sealed record Hertz : IUnit, IFrequencyUnit +{ + /// Gets the full name of the unit. + public string Name => "Hertz"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Hz"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Frequency; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Hertz() { } + +}; + +/// +/// Kilohertz - 1000 hertz. +/// +public sealed record Kilohertz : IUnit, IFrequencyUnit +{ + /// Gets the full name of the unit. + public string Name => "Kilohertz"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kHz"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Frequency; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Kilo; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Kilohertz() { } + +}; + +/// +/// Megahertz - 1e6 hertz. +/// +public sealed record Megahertz : IUnit, IFrequencyUnit +{ + /// Gets the full name of the unit. + public string Name => "Megahertz"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "MHz"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Frequency; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Mega; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Megahertz() { } + +}; + +/// +/// Sone - psychoacoustic unit of perceived loudness. +/// +public sealed record Sone : IUnit, ILoudnessUnit +{ + /// Gets the full name of the unit. + public string Name => "Sone"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "sone"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Loudness; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Sone() { } + +}; + +/// +/// Acum - psychoacoustic unit of perceived sharpness. +/// +public sealed record Acum : IUnit, ISharpnessUnit +{ + /// Gets the full name of the unit. + public string Name => "Acum"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "acum"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Sharpness; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Acum() { } + +}; + +/// +/// Candela - SI base unit of luminous intensity. +/// +public sealed record Candela : IUnit, ILuminousIntensityUnit +{ + /// Gets the full name of the unit. + public string Name => "Candela"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "cd"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIBase; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.LuminousIntensity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Candela() { } + +}; + +/// +/// Lumen - SI derived unit of luminous flux. +/// +public sealed record Lumen : IUnit, ILuminousFluxUnit +{ + /// Gets the full name of the unit. + public string Name => "Lumen"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "lm"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.LuminousFlux; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Lumen() { } + +}; + +/// +/// Lux - SI derived unit of illuminance. +/// +public sealed record Lux : IUnit, IIlluminanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Lux"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "lx"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Illuminance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Lux() { } + +}; + +/// +/// Diopter - SI unit of optical power. +/// +public sealed record Diopter : IUnit, IOpticalPowerUnit +{ + /// Gets the full name of the unit. + public string Name => "Diopter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "D"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.OpticalPower; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Diopter() { } + +}; + +/// +/// Millicandela - 0.001 candelas. +/// +public sealed record Millicandela : IUnit, ILuminousIntensityUnit +{ + /// Gets the full name of the unit. + public string Name => "Millicandela"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "mcd"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.LuminousIntensity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Milli; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Millicandela() { } + +}; + +/// +/// Foot-candle - Imperial unit of illuminance (lumen per square foot). +/// +public sealed record FootCandle : IUnit, IIlluminanceUnit +{ + /// Gets the full name of the unit. + public string Name => "FootCandle"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "fc"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Illuminance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => FootCandleToLux; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public FootCandle() { } + +}; + +/// +/// Candela per square meter - SI unit of luminance. +/// +public sealed record CandelaPerSquareMeter : IUnit, ILuminanceUnit +{ + /// Gets the full name of the unit. + public string Name => "CandelaPerSquareMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "cd/m²"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Luminance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public CandelaPerSquareMeter() { } + +}; + +/// +/// Nit - common name for one candela per square meter. +/// +public sealed record Nit : IUnit, ILuminanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Nit"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "nt"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Luminance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Nit() { } + +}; + +/// +/// Foot-lambert - Imperial unit of luminance (1/π candela per square foot). +/// +public sealed record FootLambert : IUnit, ILuminanceUnit +{ + /// Gets the full name of the unit. + public string Name => "FootLambert"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "fL"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Imperial; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Luminance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => FootLambertToCandelaPerSquareMeter; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public FootLambert() { } + +}; + +/// +/// Becquerel - SI derived unit of radioactive activity. +/// +public sealed record Becquerel : IUnit, IRadioactiveActivityUnit +{ + /// Gets the full name of the unit. + public string Name => "Becquerel"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Bq"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.RadioactiveActivity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Becquerel() { } + +}; + +/// +/// Gray - SI derived unit of absorbed dose. +/// +public sealed record Gray : IUnit, IAbsorbedDoseUnit +{ + /// Gets the full name of the unit. + public string Name => "Gray"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Gy"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.AbsorbedDose; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Gray() { } + +}; + +/// +/// Sievert - SI derived unit of equivalent dose. +/// +public sealed record Sievert : IUnit, IEquivalentDoseUnit +{ + /// Gets the full name of the unit. + public string Name => "Sievert"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Sv"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.EquivalentDose; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Sievert() { } + +}; + +/// +/// Barn - Unit of nuclear cross section. +/// +public sealed record Barn : IUnit, INuclearCrossSectionUnit +{ + /// Gets the full name of the unit. + public string Name => "Barn"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "b"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.NuclearCrossSection; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => BarnToSquareMeters; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Barn() { } + +}; + +/// +/// Coulomb per kilogram - SI derived unit of radiation exposure. +/// +public sealed record CoulombPerKilogram : IUnit, IExposureUnit +{ + /// Gets the full name of the unit. + public string Name => "CoulombPerKilogram"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "C/kg"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Exposure; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public CoulombPerKilogram() { } + +}; + +/// +/// Curie - traditional unit of radioactive activity. +/// +public sealed record Curie : IUnit, IRadioactiveActivityUnit +{ + /// Gets the full name of the unit. + public string Name => "Curie"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Ci"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.RadioactiveActivity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => CurieToBecquerels; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Curie() { } + +}; + +/// +/// Rad - traditional unit of absorbed dose (0.01 Gy). +/// +public sealed record Rad : IUnit, IAbsorbedDoseUnit +{ + /// Gets the full name of the unit. + public string Name => "Rad"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "rad"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.AbsorbedDose; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => RadToGrays; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Rad() { } + +}; + +/// +/// Rem - traditional unit of equivalent dose (0.01 Sv). +/// +public sealed record Rem : IUnit, IEquivalentDoseUnit +{ + /// Gets the full name of the unit. + public string Name => "Rem"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "rem"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.EquivalentDose; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => RemToSieverts; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Rem() { } + +}; + +/// +/// Roentgen - traditional unit of ionizing radiation exposure. +/// +public sealed record Roentgen : IUnit, IExposureUnit +{ + /// Gets the full name of the unit. + public string Name => "Roentgen"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "R"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Exposure; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => RoentgenToCoulombsPerKilogram; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Roentgen() { } + +}; + +/// +/// Kilogram per cubic meter - SI derived unit of density. +/// +public sealed record KilogramPerCubicMeter : IUnit, IDensityUnit +{ + /// Gets the full name of the unit. + public string Name => "KilogramPerCubicMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kg/m³"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Density; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public KilogramPerCubicMeter() { } + +}; + +/// +/// Gram per cubic centimeter - 1000 kg/m³. +/// +public sealed record GramPerCubicCentimeter : IUnit, IDensityUnit +{ + /// Gets the full name of the unit. + public string Name => "GramPerCubicCentimeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "g/cm³"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Density; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => GramPerCubicCentimeterToKilogramPerCubicMeter; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public GramPerCubicCentimeter() { } + +}; + +/// +/// Gram per liter - equal to one kilogram per cubic meter. +/// +public sealed record GramPerLiter : IUnit, IDensityUnit +{ + /// Gets the full name of the unit. + public string Name => "GramPerLiter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "g/L"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Density; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => GramPerLiterToKilogramPerCubicMeter; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public GramPerLiter() { } + +}; + +/// +/// Mole - SI base unit of amount of substance. +/// +public sealed record Mole : IUnit, IAmountOfSubstanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Mole"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "mol"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIBase; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.AmountOfSubstance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Mole() { } + +}; + +/// +/// Mole per cubic meter - SI base unit of concentration. +/// +public sealed record MolePerCubicMeter : IUnit, IConcentrationUnit +{ + /// Gets the full name of the unit. + public string Name => "MolePerCubicMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "mol/m³"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Concentration; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public MolePerCubicMeter() { } + +}; + +/// +/// Molar - Moles per liter concentration. +/// +public sealed record Molar : IUnit, IConcentrationUnit +{ + /// Gets the full name of the unit. + public string Name => "Molar"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "M"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Concentration; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MolarToCubicMeter; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Molar() { } + +}; + +/// +/// Kilomole - 1000 moles. +/// +public sealed record Kilomole : IUnit, IAmountOfSubstanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Kilomole"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kmol"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.AmountOfSubstance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Kilo; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Kilomole() { } + +}; + +/// +/// Millimole - 0.001 moles. +/// +public sealed record Millimole : IUnit, IAmountOfSubstanceUnit +{ + /// Gets the full name of the unit. + public string Name => "Millimole"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "mmol"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.AmountOfSubstance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MetricMagnitudes.Milli; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Millimole() { } + +}; + +/// +/// Millimolar - 0.001 moles per liter (1 mol/m³). +/// +public sealed record Millimolar : IUnit, IConcentrationUnit +{ + /// Gets the full name of the unit. + public string Name => "Millimolar"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "mM"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Concentration; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MillimolarToMolePerCubicMeter; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Millimolar() { } + +}; + +/// +/// Micromolar - 1e-6 moles per liter (0.001 mol/m³). +/// +public sealed record Micromolar : IUnit, IConcentrationUnit +{ + /// Gets the full name of the unit. + public string Name => "Micromolar"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "μM"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Concentration; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => MicromolarToMolePerCubicMeter; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Micromolar() { } + +}; + +/// +/// Square meter per second - SI derived unit of kinematic viscosity. +/// +public sealed record SquareMeterPerSecond : IUnit, IKinematicViscosityUnit +{ + /// Gets the full name of the unit. + public string Name => "SquareMeterPerSecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "m²/s"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.KinematicViscosity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public SquareMeterPerSecond() { } + +}; + +/// +/// Stokes - CGS unit of kinematic viscosity. +/// +public sealed record Stokes : IUnit, IKinematicViscosityUnit +{ + /// Gets the full name of the unit. + public string Name => "Stokes"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "St"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.CGS; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.KinematicViscosity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => StokesToSquareMeterPerSecond; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Stokes() { } + +}; + +/// +/// Pascal second - SI derived unit of dynamic viscosity. +/// +public sealed record PascalSecond : IUnit, IDynamicViscosityUnit +{ + /// Gets the full name of the unit. + public string Name => "PascalSecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Pa·s"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.DynamicViscosity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public PascalSecond() { } + +}; + +/// +/// Poise - CGS unit of dynamic viscosity. +/// +public sealed record Poise : IUnit, IDynamicViscosityUnit +{ + /// Gets the full name of the unit. + public string Name => "Poise"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "P"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.CGS; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.DynamicViscosity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => PoiseToPascalSecond; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Poise() { } + +}; + +/// +/// Cubic meter per second - SI derived unit of volumetric flow rate. +/// +public sealed record CubicMeterPerSecond : IUnit, IVolumetricFlowRateUnit +{ + /// Gets the full name of the unit. + public string Name => "CubicMeterPerSecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "m³/s"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.VolumetricFlowRate; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public CubicMeterPerSecond() { } + +}; + +/// +/// Liter per second - Common unit of volumetric flow rate. +/// +public sealed record LiterPerSecond : IUnit, IVolumetricFlowRateUnit +{ + /// Gets the full name of the unit. + public string Name => "LiterPerSecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "L/s"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.VolumetricFlowRate; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => LiterPerSecondToCubicMeterPerSecond; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public LiterPerSecond() { } + +}; + +/// +/// Kilogram per second - SI derived unit of mass flow rate. +/// +public sealed record KilogramPerSecond : IUnit, IMassFlowRateUnit +{ + /// Gets the full name of the unit. + public string Name => "KilogramPerSecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kg/s"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.MassFlowRate; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public KilogramPerSecond() { } + +}; + +/// +/// Newton per meter - SI derived unit of surface tension. +/// +public sealed record NewtonPerMeter : IUnit, ISurfaceTensionUnit +{ + /// Gets the full name of the unit. + public string Name => "NewtonPerMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "N/m"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.SurfaceTension; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public NewtonPerMeter() { } + +}; + +/// +/// Centipoise - 0.001 pascal seconds (viscosity of water at 20°C ≈ 1 cP). +/// +public sealed record Centipoise : IUnit, IDynamicViscosityUnit +{ + /// Gets the full name of the unit. + public string Name => "Centipoise"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "cP"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.CGS; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.DynamicViscosity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => CentipoiseToPascalSecond; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Centipoise() { } + +}; + +/// +/// Dyne per centimeter - CGS unit of surface tension. +/// +public sealed record DynePerCentimeter : IUnit, ISurfaceTensionUnit +{ + /// Gets the full name of the unit. + public string Name => "DynePerCentimeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "dyn/cm"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.CGS; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.SurfaceTension; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => DynePerCentimeterToNewtonPerMeter; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public DynePerCentimeter() { } + +}; + +/// +/// Kilogram per mole - SI derived unit of molar mass. +/// +public sealed record KilogramPerMole : IUnit, IMolarMassUnit +{ + /// Gets the full name of the unit. + public string Name => "KilogramPerMole"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kg/mol"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.MolarMass; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public KilogramPerMole() { } + +}; + +/// +/// Gram per mole - Common unit of molar mass. +/// +public sealed record GramPerMole : IUnit, IMolarMassUnit +{ + /// Gets the full name of the unit. + public string Name => "GramPerMole"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "g/mol"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.MolarMass; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => GramPerMoleToKilogramPerMole; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public GramPerMole() { } + +}; + +/// +/// Katal - SI derived unit of catalytic activity. +/// +public sealed record Katal : IUnit, ICatalyticActivityUnit +{ + /// Gets the full name of the unit. + public string Name => "Katal"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kat"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.CatalyticActivity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Katal() { } + +}; + +/// +/// Mole per cubic meter second - SI derived unit of reaction rate. +/// +public sealed record MolePerCubicMeterSecond : IUnit, IReactionRateUnit +{ + /// Gets the full name of the unit. + public string Name => "MolePerCubicMeterSecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "mol/(m³·s)"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.ReactionRate; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public MolePerCubicMeterSecond() { } + +}; + +/// +/// Joule per mole - SI derived unit of molar energy. +/// +public sealed record JoulePerMole : IUnit, IMolarEnergyUnit +{ + /// Gets the full name of the unit. + public string Name => "JoulePerMole"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "J/mol"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.MolarEnergy; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public JoulePerMole() { } + +}; + +/// +/// Kilojoule per mole - Common unit of molar energy. +/// +public sealed record KilojoulePerMole : IUnit, IMolarEnergyUnit +{ + /// Gets the full name of the unit. + public string Name => "KilojoulePerMole"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "kJ/mol"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.MolarEnergy; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => KilojoulePerMoleToJoulePerMole; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public KilojoulePerMole() { } + +}; + +/// +/// Calorie per mole - thermochemical calorie per mole. +/// +public sealed record CaloriePerMole : IUnit, IMolarEnergyUnit +{ + /// Gets the full name of the unit. + public string Name => "CaloriePerMole"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "cal/mol"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.MolarEnergy; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => CaloriePerMoleToJoulePerMole; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public CaloriePerMole() { } + +}; + +/// +/// Enzyme unit - one micromole of substrate per minute. +/// +public sealed record EnzymeUnit : IUnit, ICatalyticActivityUnit +{ + /// Gets the full name of the unit. + public string Name => "EnzymeUnit"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "U"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Other; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.CatalyticActivity; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => EnzymeUnitToKatals; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public EnzymeUnit() { } + +}; + +/// +/// Dalton - molar mass numerically equal to one gram per mole. +/// +public sealed record Dalton : IUnit, IMolarMassUnit +{ + /// Gets the full name of the unit. + public string Name => "Dalton"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Da"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.Atomic; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.MolarMass; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => GramPerMoleToKilogramPerMole; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public Dalton() { } + +}; + +/// +/// Per second - SI unit of a first-order rate constant. +/// +public sealed record PerSecond : IUnit, IRateConstantUnit +{ + /// Gets the full name of the unit. + public string Name => "PerSecond"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "s⁻¹"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.RateConstant; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public PerSecond() { } + +}; + +/// +/// Watt per square meter - SI derived unit of irradiance and sound intensity. +/// +public sealed record WattPerSquareMeter : IUnit, IIrradianceUnit +{ + /// Gets the full name of the unit. + public string Name => "WattPerSquareMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "W/m²"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.Irradiance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public WattPerSquareMeter() { } + +}; + +/// +/// Pascal second per meter - SI derived unit of acoustic impedance. +/// +public sealed record PascalSecondPerMeter : IUnit, IAcousticImpedanceUnit +{ + /// Gets the full name of the unit. + public string Name => "PascalSecondPerMeter"; + + /// Gets the symbol/abbreviation of the unit. + public string Symbol => "Pa·s/m"; + + /// Gets the unit system this unit belongs to. + public UnitSystem System => UnitSystem.SIDerived; + + /// Gets the physical dimension this unit measures. + public DimensionInfo Dimension => PhysicalDimensions.AcousticImpedance; + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor => 1d; + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset => 0d; + + /// Initializes a new instance of the unit. + public PascalSecondPerMeter() { } + +}; + +/// +/// Static catalogue exposing one singleton per declared unit. Generated quantity +/// types accept these on their typed In(...) methods. +/// +public static class Units{ + /// Singleton Acre instance. + public static readonly Acre Acre = new Acre(); + + /// Singleton Acum instance. + public static readonly Acum Acum = new Acum(); + + /// Singleton Ampere instance. + public static readonly Ampere Ampere = new Ampere(); + + /// Singleton AmpereHour instance. + public static readonly AmpereHour AmpereHour = new AmpereHour(); + + /// Singleton Angstrom instance. + public static readonly Angstrom Angstrom = new Angstrom(); + + /// Singleton Atmosphere instance. + public static readonly Atmosphere Atmosphere = new Atmosphere(); + + /// Singleton AtomicMassUnit instance. + public static readonly AtomicMassUnit AtomicMassUnit = new AtomicMassUnit(); + + /// Singleton Bar instance. + public static readonly Bar Bar = new Bar(); + + /// Singleton Barn instance. + public static readonly Barn Barn = new Barn(); + + /// Singleton Becquerel instance. + public static readonly Becquerel Becquerel = new Becquerel(); + + /// Singleton Btu instance. + public static readonly Btu Btu = new Btu(); + + /// Singleton Calorie instance. + public static readonly Calorie Calorie = new Calorie(); + + /// Singleton CaloriePerMole instance. + public static readonly CaloriePerMole CaloriePerMole = new CaloriePerMole(); + + /// Singleton Candela instance. + public static readonly Candela Candela = new Candela(); + + /// Singleton CandelaPerSquareMeter instance. + public static readonly CandelaPerSquareMeter CandelaPerSquareMeter = new CandelaPerSquareMeter(); + + /// Singleton Celsius instance. + public static readonly Celsius Celsius = new Celsius(); + + /// Singleton Centimeter instance. + public static readonly Centimeter Centimeter = new Centimeter(); + + /// Singleton Centipoise instance. + public static readonly Centipoise Centipoise = new Centipoise(); + + /// Singleton Coulomb instance. + public static readonly Coulomb Coulomb = new Coulomb(); + + /// Singleton CoulombPerKilogram instance. + public static readonly CoulombPerKilogram CoulombPerKilogram = new CoulombPerKilogram(); + + /// Singleton CubicCentimeter instance. + public static readonly CubicCentimeter CubicCentimeter = new CubicCentimeter(); + + /// Singleton CubicFoot instance. + public static readonly CubicFoot CubicFoot = new CubicFoot(); + + /// Singleton CubicInch instance. + public static readonly CubicInch CubicInch = new CubicInch(); + + /// Singleton CubicMeter instance. + public static readonly CubicMeter CubicMeter = new CubicMeter(); + + /// Singleton CubicMeterPerSecond instance. + public static readonly CubicMeterPerSecond CubicMeterPerSecond = new CubicMeterPerSecond(); + + /// Singleton Curie instance. + public static readonly Curie Curie = new Curie(); + + /// Singleton Dalton instance. + public static readonly Dalton Dalton = new Dalton(); + + /// Singleton Day instance. + public static readonly Day Day = new Day(); + + /// Singleton Degree instance. + public static readonly Degree Degree = new Degree(); + + /// Singleton Dimensionless instance. + public static readonly Dimensionless Dimensionless = new Dimensionless(); + + /// Singleton Diopter instance. + public static readonly Diopter Diopter = new Diopter(); + + /// Singleton Dyne instance. + public static readonly Dyne Dyne = new Dyne(); + + /// Singleton DynePerCentimeter instance. + public static readonly DynePerCentimeter DynePerCentimeter = new DynePerCentimeter(); + + /// Singleton ElectronVolt instance. + public static readonly ElectronVolt ElectronVolt = new ElectronVolt(); + + /// Singleton EnzymeUnit instance. + public static readonly EnzymeUnit EnzymeUnit = new EnzymeUnit(); + + /// Singleton Erg instance. + public static readonly Erg Erg = new Erg(); + + /// Singleton Fahrenheit instance. + public static readonly Fahrenheit Fahrenheit = new Fahrenheit(); + + /// Singleton Farad instance. + public static readonly Farad Farad = new Farad(); + + /// Singleton FaradPerMeter instance. + public static readonly FaradPerMeter FaradPerMeter = new FaradPerMeter(); + + /// Singleton Foot instance. + public static readonly Foot Foot = new Foot(); + + /// Singleton FootCandle instance. + public static readonly FootCandle FootCandle = new FootCandle(); + + /// Singleton FootLambert instance. + public static readonly FootLambert FootLambert = new FootLambert(); + + /// Singleton FootPerSecond instance. + public static readonly FootPerSecond FootPerSecond = new FootPerSecond(); + + /// Singleton Gallon instance. + public static readonly Gallon Gallon = new Gallon(); + + /// Singleton Gauss instance. + public static readonly Gauss Gauss = new Gauss(); + + /// Singleton Gradian instance. + public static readonly Gradian Gradian = new Gradian(); + + /// Singleton Gram instance. + public static readonly Gram Gram = new Gram(); + + /// Singleton GramPerCubicCentimeter instance. + public static readonly GramPerCubicCentimeter GramPerCubicCentimeter = new GramPerCubicCentimeter(); + + /// Singleton GramPerLiter instance. + public static readonly GramPerLiter GramPerLiter = new GramPerLiter(); + + /// Singleton GramPerMole instance. + public static readonly GramPerMole GramPerMole = new GramPerMole(); + + /// Singleton Gray instance. + public static readonly Gray Gray = new Gray(); + + /// Singleton Hectare instance. + public static readonly Hectare Hectare = new Hectare(); + + /// Singleton Henry instance. + public static readonly Henry Henry = new Henry(); + + /// Singleton Hertz instance. + public static readonly Hertz Hertz = new Hertz(); + + /// Singleton Horsepower instance. + public static readonly Horsepower Horsepower = new Horsepower(); + + /// Singleton Hour instance. + public static readonly Hour Hour = new Hour(); + + /// Singleton ImperialGallon instance. + public static readonly ImperialGallon ImperialGallon = new ImperialGallon(); + + /// Singleton Inch instance. + public static readonly Inch Inch = new Inch(); + + /// Singleton Joule instance. + public static readonly Joule Joule = new Joule(); + + /// Singleton JoulePerKelvin instance. + public static readonly JoulePerKelvin JoulePerKelvin = new JoulePerKelvin(); + + /// Singleton JoulePerKilogramKelvin instance. + public static readonly JoulePerKilogramKelvin JoulePerKilogramKelvin = new JoulePerKilogramKelvin(); + + /// Singleton JoulePerMole instance. + public static readonly JoulePerMole JoulePerMole = new JoulePerMole(); + + /// Singleton Katal instance. + public static readonly Katal Katal = new Katal(); + + /// Singleton Kelvin instance. + public static readonly Kelvin Kelvin = new Kelvin(); + + /// Singleton KelvinPerWatt instance. + public static readonly KelvinPerWatt KelvinPerWatt = new KelvinPerWatt(); + + /// Singleton Kiloampere instance. + public static readonly Kiloampere Kiloampere = new Kiloampere(); + + /// Singleton Kilocalorie instance. + public static readonly Kilocalorie Kilocalorie = new Kilocalorie(); + + /// Singleton Kilogram instance. + public static readonly Kilogram Kilogram = new Kilogram(); + + /// Singleton KilogramMeterSquared instance. + public static readonly KilogramMeterSquared KilogramMeterSquared = new KilogramMeterSquared(); + + /// Singleton KilogramMeterSquaredPerSecond instance. + public static readonly KilogramMeterSquaredPerSecond KilogramMeterSquaredPerSecond = new KilogramMeterSquaredPerSecond(); + + /// Singleton KilogramPerCubicMeter instance. + public static readonly KilogramPerCubicMeter KilogramPerCubicMeter = new KilogramPerCubicMeter(); + + /// Singleton KilogramPerMole instance. + public static readonly KilogramPerMole KilogramPerMole = new KilogramPerMole(); + + /// Singleton KilogramPerSecond instance. + public static readonly KilogramPerSecond KilogramPerSecond = new KilogramPerSecond(); + + /// Singleton Kilohertz instance. + public static readonly Kilohertz Kilohertz = new Kilohertz(); + + /// Singleton Kilohm instance. + public static readonly Kilohm Kilohm = new Kilohm(); + + /// Singleton Kilojoule instance. + public static readonly Kilojoule Kilojoule = new Kilojoule(); + + /// Singleton KilojoulePerMole instance. + public static readonly KilojoulePerMole KilojoulePerMole = new KilojoulePerMole(); + + /// Singleton Kilometer instance. + public static readonly Kilometer Kilometer = new Kilometer(); + + /// Singleton KilometerPerHour instance. + public static readonly KilometerPerHour KilometerPerHour = new KilometerPerHour(); + + /// Singleton Kilomole instance. + public static readonly Kilomole Kilomole = new Kilomole(); + + /// Singleton Kilonewton instance. + public static readonly Kilonewton Kilonewton = new Kilonewton(); + + /// Singleton Kilopascal instance. + public static readonly Kilopascal Kilopascal = new Kilopascal(); + + /// Singleton Kilovolt instance. + public static readonly Kilovolt Kilovolt = new Kilovolt(); + + /// Singleton Kilowatt instance. + public static readonly Kilowatt Kilowatt = new Kilowatt(); + + /// Singleton KilowattHour instance. + public static readonly KilowattHour KilowattHour = new KilowattHour(); + + /// Singleton Knot instance. + public static readonly Knot Knot = new Knot(); + + /// Singleton Liter instance. + public static readonly Liter Liter = new Liter(); + + /// Singleton LiterPerSecond instance. + public static readonly LiterPerSecond LiterPerSecond = new LiterPerSecond(); + + /// Singleton Lumen instance. + public static readonly Lumen Lumen = new Lumen(); + + /// Singleton Lux instance. + public static readonly Lux Lux = new Lux(); + + /// Singleton Megahertz instance. + public static readonly Megahertz Megahertz = new Megahertz(); + + /// Singleton Megawatt instance. + public static readonly Megawatt Megawatt = new Megawatt(); + + /// Singleton Megohm instance. + public static readonly Megohm Megohm = new Megohm(); + + /// Singleton Meter instance. + public static readonly Meter Meter = new Meter(); + + /// Singleton MeterPerSecond instance. + public static readonly MeterPerSecond MeterPerSecond = new MeterPerSecond(); + + /// Singleton MeterPerSecondCubed instance. + public static readonly MeterPerSecondCubed MeterPerSecondCubed = new MeterPerSecondCubed(); + + /// Singleton MeterPerSecondQuartic instance. + public static readonly MeterPerSecondQuartic MeterPerSecondQuartic = new MeterPerSecondQuartic(); + + /// Singleton MeterPerSecondSquared instance. + public static readonly MeterPerSecondSquared MeterPerSecondSquared = new MeterPerSecondSquared(); + + /// Singleton Microfarad instance. + public static readonly Microfarad Microfarad = new Microfarad(); + + /// Singleton Micrometer instance. + public static readonly Micrometer Micrometer = new Micrometer(); + + /// Singleton Micromolar instance. + public static readonly Micromolar Micromolar = new Micromolar(); + + /// Singleton Microsecond instance. + public static readonly Microsecond Microsecond = new Microsecond(); + + /// Singleton Mile instance. + public static readonly Mile Mile = new Mile(); + + /// Singleton MilePerHour instance. + public static readonly MilePerHour MilePerHour = new MilePerHour(); + + /// Singleton Milliampere instance. + public static readonly Milliampere Milliampere = new Milliampere(); + + /// Singleton Millicandela instance. + public static readonly Millicandela Millicandela = new Millicandela(); + + /// Singleton Milliliter instance. + public static readonly Milliliter Milliliter = new Milliliter(); + + /// Singleton Millimeter instance. + public static readonly Millimeter Millimeter = new Millimeter(); + + /// Singleton Millimolar instance. + public static readonly Millimolar Millimolar = new Millimolar(); + + /// Singleton Millimole instance. + public static readonly Millimole Millimole = new Millimole(); + + /// Singleton Milliradian instance. + public static readonly Milliradian Milliradian = new Milliradian(); + + /// Singleton Millisecond instance. + public static readonly Millisecond Millisecond = new Millisecond(); + + /// Singleton Minute instance. + public static readonly Minute Minute = new Minute(); + + /// Singleton Molar instance. + public static readonly Molar Molar = new Molar(); + + /// Singleton Mole instance. + public static readonly Mole Mole = new Mole(); + + /// Singleton MolePerCubicMeter instance. + public static readonly MolePerCubicMeter MolePerCubicMeter = new MolePerCubicMeter(); + + /// Singleton MolePerCubicMeterSecond instance. + public static readonly MolePerCubicMeterSecond MolePerCubicMeterSecond = new MolePerCubicMeterSecond(); + + /// Singleton Nanofarad instance. + public static readonly Nanofarad Nanofarad = new Nanofarad(); + + /// Singleton Nanometer instance. + public static readonly Nanometer Nanometer = new Nanometer(); + + /// Singleton Nanosecond instance. + public static readonly Nanosecond Nanosecond = new Nanosecond(); + + /// Singleton NauticalMile instance. + public static readonly NauticalMile NauticalMile = new NauticalMile(); + + /// Singleton Newton instance. + public static readonly Newton Newton = new Newton(); + + /// Singleton NewtonMeter instance. + public static readonly NewtonMeter NewtonMeter = new NewtonMeter(); + + /// Singleton NewtonPerMeter instance. + public static readonly NewtonPerMeter NewtonPerMeter = new NewtonPerMeter(); + + /// Singleton NewtonSecond instance. + public static readonly NewtonSecond NewtonSecond = new NewtonSecond(); + + /// Singleton Nit instance. + public static readonly Nit Nit = new Nit(); + + /// Singleton Ohm instance. + public static readonly Ohm Ohm = new Ohm(); + + /// Singleton Ounce instance. + public static readonly Ounce Ounce = new Ounce(); + + /// Singleton PartPerBillion instance. + public static readonly PartPerBillion PartPerBillion = new PartPerBillion(); + + /// Singleton PartPerMillion instance. + public static readonly PartPerMillion PartPerMillion = new PartPerMillion(); + + /// Singleton Pascal instance. + public static readonly Pascal Pascal = new Pascal(); + + /// Singleton PascalSecond instance. + public static readonly PascalSecond PascalSecond = new PascalSecond(); + + /// Singleton PascalSecondPerMeter instance. + public static readonly PascalSecondPerMeter PascalSecondPerMeter = new PascalSecondPerMeter(); + + /// Singleton PerKelvin instance. + public static readonly PerKelvin PerKelvin = new PerKelvin(); + + /// Singleton PerSecond instance. + public static readonly PerSecond PerSecond = new PerSecond(); + + /// Singleton Percent instance. + public static readonly Percent Percent = new Percent(); + + /// Singleton PercentByWeight instance. + public static readonly PercentByWeight PercentByWeight = new PercentByWeight(); + + /// Singleton Picofarad instance. + public static readonly Picofarad Picofarad = new Picofarad(); + + /// Singleton Poise instance. + public static readonly Poise Poise = new Poise(); + + /// Singleton Pound instance. + public static readonly Pound Pound = new Pound(); + + /// Singleton PoundFoot instance. + public static readonly PoundFoot PoundFoot = new PoundFoot(); + + /// Singleton PoundForce instance. + public static readonly PoundForce PoundForce = new PoundForce(); + + /// Singleton Psi instance. + public static readonly Psi Psi = new Psi(); + + /// Singleton Rad instance. + public static readonly Rad Rad = new Rad(); + + /// Singleton Radian instance. + public static readonly Radian Radian = new Radian(); + + /// Singleton RadianPerSecond instance. + public static readonly RadianPerSecond RadianPerSecond = new RadianPerSecond(); + + /// Singleton RadianPerSecondCubed instance. + public static readonly RadianPerSecondCubed RadianPerSecondCubed = new RadianPerSecondCubed(); + + /// Singleton RadianPerSecondSquared instance. + public static readonly RadianPerSecondSquared RadianPerSecondSquared = new RadianPerSecondSquared(); + + /// Singleton Rankine instance. + public static readonly Rankine Rankine = new Rankine(); + + /// Singleton Rem instance. + public static readonly Rem Rem = new Rem(); + + /// Singleton Revolution instance. + public static readonly Revolution Revolution = new Revolution(); + + /// Singleton RevolutionPerMinute instance. + public static readonly RevolutionPerMinute RevolutionPerMinute = new RevolutionPerMinute(); + + /// Singleton Roentgen instance. + public static readonly Roentgen Roentgen = new Roentgen(); + + /// Singleton Second instance. + public static readonly Second Second = new Second(); + + /// Singleton ShortTon instance. + public static readonly ShortTon ShortTon = new ShortTon(); + + /// Singleton Siemens instance. + public static readonly Siemens Siemens = new Siemens(); + + /// Singleton SiemensPerMeter instance. + public static readonly SiemensPerMeter SiemensPerMeter = new SiemensPerMeter(); + + /// Singleton Sievert instance. + public static readonly Sievert Sievert = new Sievert(); + + /// Singleton Sone instance. + public static readonly Sone Sone = new Sone(); + + /// Singleton SquareCentimeter instance. + public static readonly SquareCentimeter SquareCentimeter = new SquareCentimeter(); + + /// Singleton SquareFoot instance. + public static readonly SquareFoot SquareFoot = new SquareFoot(); + + /// Singleton SquareInch instance. + public static readonly SquareInch SquareInch = new SquareInch(); + + /// Singleton SquareKilometer instance. + public static readonly SquareKilometer SquareKilometer = new SquareKilometer(); + + /// Singleton SquareMeter instance. + public static readonly SquareMeter SquareMeter = new SquareMeter(); + + /// Singleton SquareMeterPerSecond instance. + public static readonly SquareMeterPerSecond SquareMeterPerSecond = new SquareMeterPerSecond(); + + /// Singleton SquareMile instance. + public static readonly SquareMile SquareMile = new SquareMile(); + + /// Singleton StandardGravity instance. + public static readonly StandardGravity StandardGravity = new StandardGravity(); + + /// Singleton Stokes instance. + public static readonly Stokes Stokes = new Stokes(); + + /// Singleton Stone instance. + public static readonly Stone Stone = new Stone(); + + /// Singleton Tesla instance. + public static readonly Tesla Tesla = new Tesla(); + + /// Singleton Ton instance. + public static readonly Ton Ton = new Ton(); + + /// Singleton Torr instance. + public static readonly Torr Torr = new Torr(); + + /// Singleton USFluidOunce instance. + public static readonly USFluidOunce USFluidOunce = new USFluidOunce(); + + /// Singleton USPint instance. + public static readonly USPint USPint = new USPint(); + + /// Singleton USQuart instance. + public static readonly USQuart USQuart = new USQuart(); + + /// Singleton Volt instance. + public static readonly Volt Volt = new Volt(); + + /// Singleton VoltMeter instance. + public static readonly VoltMeter VoltMeter = new VoltMeter(); + + /// Singleton VoltPerMeter instance. + public static readonly VoltPerMeter VoltPerMeter = new VoltPerMeter(); + + /// Singleton VoltPerPascal instance. + public static readonly VoltPerPascal VoltPerPascal = new VoltPerPascal(); + + /// Singleton Watt instance. + public static readonly Watt Watt = new Watt(); + + /// Singleton WattHour instance. + public static readonly WattHour WattHour = new WattHour(); + + /// Singleton WattPerCubicMeter instance. + public static readonly WattPerCubicMeter WattPerCubicMeter = new WattPerCubicMeter(); + + /// Singleton WattPerMeterKelvin instance. + public static readonly WattPerMeterKelvin WattPerMeterKelvin = new WattPerMeterKelvin(); + + /// Singleton WattPerSquareMeter instance. + public static readonly WattPerSquareMeter WattPerSquareMeter = new WattPerSquareMeter(); + + /// Singleton WattPerSquareMeterKelvin instance. + public static readonly WattPerSquareMeterKelvin WattPerSquareMeterKelvin = new WattPerSquareMeterKelvin(); + + /// Singleton Weber instance. + public static readonly Weber Weber = new Weber(); + + /// Singleton Week instance. + public static readonly Week Week = new Week(); + + /// Singleton Yard instance. + public static readonly Yard Yard = new Yard(); + + /// Singleton Year instance. + public static readonly Year Year = new Year(); + +}; + diff --git a/Semantics.Quantities/IPhysicalQuantity.cs b/Semantics.Quantities/IPhysicalQuantity.cs new file mode 100644 index 0000000..986fbba --- /dev/null +++ b/Semantics.Quantities/IPhysicalQuantity.cs @@ -0,0 +1,35 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// Common surface for every physical quantity. Exposes the underlying +/// , a structural validity check, the +/// the quantity belongs to, and same-dimension comparison. +/// +/// +/// Concrete generated quantity types also expose a dimensionally-typed +/// In(I<Dim>Unit) method — kept off this interface so cross-dimension +/// comparison via the slim contract stays compile-time clean. +/// +/// The storage type for the quantity value. +public interface IPhysicalQuantity + : ISemanticQuantity + , IComparable> + , IEquatable> + where T : struct, INumber +{ + /// Gets the value stored in this quantity (in the dimension's SI base unit). + public T Value { get; } + + /// Gets whether this quantity satisfies structural physical constraints (finite, non-NaN). + public bool IsPhysicallyValid { get; } + + /// Gets the physical dimension this quantity belongs to. + public DimensionInfo Dimension { get; } +} diff --git a/Semantics.Quantities/ISemanticQuantity.cs b/Semantics.Quantities/ISemanticQuantity.cs index 6eedb5e..b505668 100644 --- a/Semantics.Quantities/ISemanticQuantity.cs +++ b/Semantics.Quantities/ISemanticQuantity.cs @@ -2,7 +2,7 @@ // All rights reserved. // Licensed under the MIT license. -namespace ktsu.Semantics; +namespace ktsu.Semantics.Quantities; using System.Numerics; diff --git a/Semantics.Quantities/IUnit.cs b/Semantics.Quantities/IUnit.cs new file mode 100644 index 0000000..0a0c367 --- /dev/null +++ b/Semantics.Quantities/IUnit.cs @@ -0,0 +1,55 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Common surface for every physical unit. Carries the unit's name, symbol, +/// the it belongs to, and the affine conversion +/// (base = value × ToBaseFactor + ToBaseOffset) that maps values in +/// this unit to the SI base unit of the dimension. +/// +/// +/// +/// For dimensional compile-time safety, generated quantity types do not accept +/// the raw on In(...). Each dimension has its own +/// marker interface (e.g. ILengthUnit : IUnit) which only its units +/// implement, and the generated In(I<Dim>Unit) overload accepts +/// only that family. So length.In(Units.Kilogram) fails to compile. +/// +/// +/// ToBase / FromBase are default-implemented; concrete units only +/// have to provide and . +/// +/// +public interface IUnit +{ + /// Gets the full name of the unit (e.g. "Kilometer"). + public string Name { get; } + + /// Gets the unit's symbol/abbreviation (e.g. "km"). + public string Symbol { get; } + + /// Gets the unit system this unit belongs to. + public UnitSystem System { get; } + + /// Gets the dimension this unit measures. + public DimensionInfo Dimension { get; } + + /// Gets the multiplication factor used in the to-base affine conversion. + public double ToBaseFactor { get; } + + /// Gets the additive offset used in the to-base affine conversion. + public double ToBaseOffset { get; } + + /// Converts a value expressed in this unit to the dimension's SI base unit. + public T ToBase(T value) where T : struct, INumber + => (value * T.CreateChecked(ToBaseFactor)) + T.CreateChecked(ToBaseOffset); + + /// Converts a value expressed in the dimension's SI base unit to this unit. + public T FromBase(T baseValue) where T : struct, INumber + => (baseValue - T.CreateChecked(ToBaseOffset)) / T.CreateChecked(ToBaseFactor); +} diff --git a/Semantics.Quantities/IVector0.cs b/Semantics.Quantities/IVector0.cs new file mode 100644 index 0000000..584c777 --- /dev/null +++ b/Semantics.Quantities/IVector0.cs @@ -0,0 +1,26 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Marker interface for Vector0 (magnitude-only) quantity types. +/// Vector0 quantities represent non-negative magnitudes with no directional component. +/// Arithmetic operators are inherited from +/// rather than declared here to avoid ambiguity. +/// +/// The implementing quantity type. +/// The numeric storage type. +public interface IVector0 + where TSelf : IVector0 + where T : struct, INumber +{ + /// Gets the magnitude value. + public T Value { get; } + + /// Gets a quantity with value zero. + public static abstract TSelf Zero { get; } +} diff --git a/Semantics.Quantities/IVector1.cs b/Semantics.Quantities/IVector1.cs new file mode 100644 index 0000000..f1b72d4 --- /dev/null +++ b/Semantics.Quantities/IVector1.cs @@ -0,0 +1,26 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Marker interface for Vector1 (signed one-dimensional) quantity types. +/// Vector1 quantities represent signed values along a single axis. +/// Arithmetic operators are inherited from +/// rather than declared here to avoid ambiguity. +/// +/// The implementing quantity type. +/// The numeric storage type. +public interface IVector1 + where TSelf : IVector1 + where T : struct, INumber +{ + /// Gets the signed value along the single axis. + public T Value { get; } + + /// Gets a quantity with value zero. + public static abstract TSelf Zero { get; } +} diff --git a/Semantics.Quantities/IVector2.cs b/Semantics.Quantities/IVector2.cs new file mode 100644 index 0000000..3bc809b --- /dev/null +++ b/Semantics.Quantities/IVector2.cs @@ -0,0 +1,96 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Interface for 2D vector types with generic numeric component support. +/// +/// The implementing vector type. +/// The numeric component type. +public interface IVector2 + where TVector : IVector2 + where T : struct, INumber +{ + /// Gets the X component. + T X { get; } + + /// Gets the Y component. + T Y { get; } + + /// Gets a vector with all components set to zero. + public static abstract TVector Zero { get; } + + /// Gets a vector with all components set to one. + public static abstract TVector One { get; } + + /// Gets the unit vector for the X-axis. + public static abstract TVector UnitX { get; } + + /// Gets the unit vector for the Y-axis. + public static abstract TVector UnitY { get; } + + /// + /// Calculates the length of the vector. + /// + /// The length of the vector. + public T Length(); + + /// + /// Calculates the squared length of the vector. + /// + /// The squared length of the vector. + public T LengthSquared(); + + /// + /// Calculates the dot product of two vectors. + /// + /// The other vector. + /// The dot product. + public T Dot(TVector other); + + /// + /// Calculates the distance between two vectors. + /// + /// The other vector. + /// The distance between the vectors. + public T Distance(TVector other); + + /// + /// Calculates the squared distance between two vectors. + /// + /// The other vector. + /// The squared distance between the vectors. + public T DistanceSquared(TVector other); + + /// + /// Returns a normalized version of the vector. + /// + /// The normalized vector. + public TVector Normalize(); + + // Arithmetic operators + /// Adds two vectors. + public static abstract TVector operator +(TVector left, TVector right); + + /// Subtracts two vectors. + public static abstract TVector operator -(TVector left, TVector right); + + /// Multiplies a vector by a scalar. + public static abstract TVector operator *(TVector vector, T scalar); + + /// Multiplies a scalar by a vector. + public static abstract TVector operator *(T scalar, TVector vector); + + /// Divides a vector by a scalar. + public static abstract TVector operator /(TVector vector, T scalar); + + /// Negates a vector. + public static abstract TVector operator -(TVector vector); +} diff --git a/Semantics.Quantities/IVector3.cs b/Semantics.Quantities/IVector3.cs new file mode 100644 index 0000000..3d060fa --- /dev/null +++ b/Semantics.Quantities/IVector3.cs @@ -0,0 +1,109 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Interface for 3D vector types with generic numeric component support. +/// +/// The implementing vector type. +/// The numeric component type. +public interface IVector3 + where TVector : IVector3 + where T : struct, INumber +{ + /// Gets the X component. + T X { get; } + + /// Gets the Y component. + T Y { get; } + + /// Gets the Z component. + T Z { get; } + + /// Gets a vector with all components set to zero. + public static abstract TVector Zero { get; } + + /// Gets a vector with all components set to one. + public static abstract TVector One { get; } + + /// Gets the unit vector for the X-axis. + public static abstract TVector UnitX { get; } + + /// Gets the unit vector for the Y-axis. + public static abstract TVector UnitY { get; } + + /// Gets the unit vector for the Z-axis. + public static abstract TVector UnitZ { get; } + + /// + /// Calculates the length of the vector. + /// + /// The length of the vector. + public T Length(); + + /// + /// Calculates the squared length of the vector. + /// + /// The squared length of the vector. + public T LengthSquared(); + + /// + /// Calculates the dot product of two vectors. + /// + /// The other vector. + /// The dot product. + public T Dot(TVector other); + + /// + /// Calculates the cross product of two vectors. + /// + /// The other vector. + /// The cross product. + public TVector Cross(TVector other); + + /// + /// Calculates the distance between two vectors. + /// + /// The other vector. + /// The distance between the vectors. + public T Distance(TVector other); + + /// + /// Calculates the squared distance between two vectors. + /// + /// The other vector. + /// The squared distance between the vectors. + public T DistanceSquared(TVector other); + + /// + /// Returns a normalized version of the vector. + /// + /// The normalized vector. + public TVector Normalize(); + + // Arithmetic operators + /// Adds two vectors. + public static abstract TVector operator +(TVector left, TVector right); + + /// Subtracts two vectors. + public static abstract TVector operator -(TVector left, TVector right); + + /// Multiplies a vector by a scalar. + public static abstract TVector operator *(TVector vector, T scalar); + + /// Multiplies a scalar by a vector. + public static abstract TVector operator *(T scalar, TVector vector); + + /// Divides a vector by a scalar. + public static abstract TVector operator /(TVector vector, T scalar); + + /// Negates a vector. + public static abstract TVector operator -(TVector vector); +} diff --git a/Semantics.Quantities/IVector4.cs b/Semantics.Quantities/IVector4.cs new file mode 100644 index 0000000..89559c7 --- /dev/null +++ b/Semantics.Quantities/IVector4.cs @@ -0,0 +1,108 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +#pragma warning disable IDE0040 // Accessibility modifiers required +#pragma warning disable CA2225 // Operator overloads have named alternates + +namespace ktsu.Semantics.Quantities; + +using System.Numerics; + +/// +/// Interface for 4D vector types with generic numeric component support. +/// +/// The implementing vector type. +/// The numeric component type. +public interface IVector4 + where TVector : IVector4 + where T : struct, INumber +{ + /// Gets the X component. + T X { get; } + + /// Gets the Y component. + T Y { get; } + + /// Gets the Z component. + T Z { get; } + + /// Gets the W component. + T W { get; } + + /// Gets a vector with all components set to zero. + public static abstract TVector Zero { get; } + + /// Gets a vector with all components set to one. + public static abstract TVector One { get; } + + /// Gets the unit vector for the X-axis. + public static abstract TVector UnitX { get; } + + /// Gets the unit vector for the Y-axis. + public static abstract TVector UnitY { get; } + + /// Gets the unit vector for the Z-axis. + public static abstract TVector UnitZ { get; } + + /// Gets the unit vector for the W-axis. + public static abstract TVector UnitW { get; } + + /// + /// Calculates the length of the vector. + /// + /// The length of the vector. + public T Length(); + + /// + /// Calculates the squared length of the vector. + /// + /// The squared length of the vector. + public T LengthSquared(); + + /// + /// Calculates the dot product of two vectors. + /// + /// The other vector. + /// The dot product. + public T Dot(TVector other); + + /// + /// Calculates the distance between two vectors. + /// + /// The other vector. + /// The distance between the vectors. + public T Distance(TVector other); + + /// + /// Calculates the squared distance between two vectors. + /// + /// The other vector. + /// The squared distance between the vectors. + public T DistanceSquared(TVector other); + + /// + /// Returns a normalized version of the vector. + /// + /// The normalized vector. + public TVector Normalize(); + + // Arithmetic operators + /// Adds two vectors. + public static abstract TVector operator +(TVector left, TVector right); + + /// Subtracts two vectors. + public static abstract TVector operator -(TVector left, TVector right); + + /// Multiplies a vector by a scalar. + public static abstract TVector operator *(TVector vector, T scalar); + + /// Multiplies a scalar by a vector. + public static abstract TVector operator *(T scalar, TVector vector); + + /// Divides a vector by a scalar. + public static abstract TVector operator /(TVector vector, T scalar); + + /// Negates a vector. + public static abstract TVector operator -(TVector vector); +} diff --git a/Semantics.Quantities/Mechanics/Acceleration.cs b/Semantics.Quantities/Mechanics/Acceleration.cs deleted file mode 100644 index 21e418d..0000000 --- a/Semantics.Quantities/Mechanics/Acceleration.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an acceleration quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Acceleration : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of acceleration [L T⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.Acceleration; - - /// - /// Initializes a new instance of the class. - /// - public Acceleration() : base() { } - - /// - /// Creates a new Acceleration from a value in meters per second squared. - /// - /// The value in meters per second squared. - /// A new Acceleration instance. - public static Acceleration FromMetersPerSecondSquared(T metersPerSecondSquared) => Create(metersPerSecondSquared); - - /// - /// Calculates velocity from acceleration and time (v = a·t). - /// - /// The acceleration. - /// The time duration. - /// The resulting velocity. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Velocity operator *(Acceleration acceleration, Time time) - { - Ensure.NotNull(acceleration); - Ensure.NotNull(time); - - T velocityValue = acceleration.Value * time.Value; - - return Velocity.Create(velocityValue); - } - - /// - /// Calculates velocity from time and acceleration (v = a·t). - /// - /// The time duration. - /// The acceleration. - /// The resulting velocity. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Velocity operator *(Time time, Acceleration acceleration) => acceleration * time; - - /// - /// Calculates time from acceleration and velocity (t = v/a). - /// - /// The velocity. - /// The acceleration. - /// The resulting time duration. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Time operator /(Velocity velocity, Acceleration acceleration) - { - Ensure.NotNull(velocity); - Ensure.NotNull(acceleration); - - T timeValue = velocity.Value / acceleration.Value; - - return Time.Create(timeValue); - } -} diff --git a/Semantics.Quantities/Mechanics/AngularAcceleration.cs b/Semantics.Quantities/Mechanics/AngularAcceleration.cs deleted file mode 100644 index c0a7154..0000000 --- a/Semantics.Quantities/Mechanics/AngularAcceleration.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an angular acceleration quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record AngularAcceleration : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of angularacceleration [T⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.AngularAcceleration; - - /// - /// Initializes a new instance of the class. - /// - public AngularAcceleration() : base() { } - - /// - /// Creates a new AngularAcceleration from a value in radians per second squared. - /// - /// The value in radians per second squared. - /// A new AngularAcceleration instance. - public static AngularAcceleration FromRadiansPerSecondSquared(T radiansPerSecondSquared) => Create(radiansPerSecondSquared); -} diff --git a/Semantics.Quantities/Mechanics/AngularVelocity.cs b/Semantics.Quantities/Mechanics/AngularVelocity.cs deleted file mode 100644 index c34bfd8..0000000 --- a/Semantics.Quantities/Mechanics/AngularVelocity.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an angular velocity quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record AngularVelocity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of angularvelocity [T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.AngularVelocity; - - /// - /// Initializes a new instance of the class. - /// - public AngularVelocity() : base() { } - - /// - /// Creates a new AngularVelocity from a value in radians per second. - /// - /// The value in radians per second. - /// A new AngularVelocity instance. - public static AngularVelocity FromRadiansPerSecond(T radiansPerSecond) => Create(radiansPerSecond); - - /// - /// Divides angular velocity by time to create angular acceleration. - /// - /// The angular velocity. - /// The time. - /// The resulting angular acceleration. - public static AngularAcceleration Divide(AngularVelocity angularVelocity, Time time) - { - Ensure.NotNull(angularVelocity); - Ensure.NotNull(time); - return AngularAcceleration.Create(angularVelocity.Value / time.Value); - } -} diff --git a/Semantics.Quantities/Mechanics/Area.cs b/Semantics.Quantities/Mechanics/Area.cs deleted file mode 100644 index 24e3d73..0000000 --- a/Semantics.Quantities/Mechanics/Area.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an area quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Area : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of area [L²]. - public override PhysicalDimension Dimension => PhysicalDimensions.Area; - - /// - /// Initializes a new instance of the class. - /// - public Area() : base() { } - - /// - /// Creates a new Area from a value in square meters. - /// - /// The value in square meters. - /// A new Area instance. - public static Area FromSquareMeters(T squareMeters) => Create(squareMeters); - - /// - /// Multiplies this area by a length to create a volume. - /// - /// The area. - /// The length. - /// The resulting volume. - public static Volume operator *(Area left, Length right) - { - Ensure.NotNull(left); - Ensure.NotNull(right); - return Volume.Create(left.Value * right.Value); - } - - /// - /// Multiplies this area by a length to create a volume. - /// - /// The area. - /// The length. - /// The resulting volume. - public static Volume Multiply(Area left, Length right) => left * right; -} diff --git a/Semantics.Quantities/Mechanics/Density.cs b/Semantics.Quantities/Mechanics/Density.cs deleted file mode 100644 index 20a03f6..0000000 --- a/Semantics.Quantities/Mechanics/Density.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a density quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Density : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of density [M L⁻³]. - public override PhysicalDimension Dimension => PhysicalDimensions.Density; - - /// - /// Initializes a new instance of the class. - /// - public Density() : base() { } - - /// - /// Creates a new Density from a value in kilograms per cubic meter. - /// - /// The value in kilograms per cubic meter. - /// A new Density instance. - public static Density FromKilogramsPerCubicMeter(T kilogramsPerCubicMeter) => Create(kilogramsPerCubicMeter); - - /// - /// Divides mass by volume to create density. - /// - /// The mass. - /// The volume. - /// The resulting density. - public static Density Divide(Mass mass, Volume volume) - { - Ensure.NotNull(mass); - Ensure.NotNull(volume); - return Create(mass.Value / volume.Value); - } - - /// - /// Multiplies density by volume to get mass. - /// - /// The density. - /// The volume. - /// The resulting mass. - public static Mass Multiply(Density density, Volume volume) - { - Ensure.NotNull(density); - Ensure.NotNull(volume); - return Mass.Create(density.Value * volume.Value); - } - - /// - /// Divides mass by density to get volume. - /// - /// The mass. - /// The density. - /// The resulting volume. - public static Volume Divide(Mass mass, Density density) - { - Ensure.NotNull(mass); - Ensure.NotNull(density); - return Volume.Create(mass.Value / density.Value); - } -} diff --git a/Semantics.Quantities/Mechanics/Energy.cs b/Semantics.Quantities/Mechanics/Energy.cs deleted file mode 100644 index 11f1777..0000000 --- a/Semantics.Quantities/Mechanics/Energy.cs +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an energy quantity with compile-time dimensional safety. -/// Energy can be kinetic (½mv²), potential (mgh), or work done (F·d). -/// -/// The storage type for the quantity value. -public sealed record Energy : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of energy [M L² T⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.Energy; - - /// - /// Initializes a new instance of the class. - /// - public Energy() : base() { } - - /// - /// Creates a new Energy from a value in joules. - /// - /// The value in joules. - /// A new Energy instance. - public static Energy FromJoules(T joules) => Create(joules); - - /// - /// Calculates kinetic energy from mass and velocity (KE = ½mv²). - /// - /// The mass of the object. - /// The velocity of the object. - /// The kinetic energy. - public static Energy FromKineticEnergy(Mass mass, Velocity velocity) - { - Ensure.NotNull(mass); - Ensure.NotNull(velocity); - - T halfValue = PhysicalConstants.Generic.OneHalf(); - T kineticEnergyValue = halfValue * mass.Value * velocity.Value * velocity.Value; - - return Create(kineticEnergyValue); - } - - /// - /// Calculates gravitational potential energy from mass, height, and gravity (PE = mgh). - /// - /// The mass of the object. - /// The height above reference level. - /// The gravitational acceleration (optional, defaults to standard gravity). - /// The gravitational potential energy. - public static Energy FromPotentialEnergy(Mass mass, Length height, Acceleration? gravity = null) - { - Ensure.NotNull(mass); - Ensure.NotNull(height); - - T massValue = mass.In(Units.Kilogram); - T heightValue = height.In(Units.Meter); - T gravityValue = gravity?.In(Units.MetersPerSecondSquared) ?? PhysicalConstants.Generic.StandardGravity(); - T energyValue = massValue * gravityValue * heightValue; - - return Create(energyValue); - } - - /// - /// Calculates work done by a force over a distance (W = F·d). - /// - /// The applied force. - /// The distance over which the force is applied. - /// The work done (energy transferred). - public static Energy FromWork(Force force, Length distance) - { - Ensure.NotNull(force); - Ensure.NotNull(distance); - - T forceValue = force.In(Units.Newton); - T distanceValue = distance.In(Units.Meter); - T energyValue = forceValue * distanceValue; - - return Create(energyValue); - } - - /// - /// Calculates the velocity an object would have if all this energy were kinetic energy. - /// Note: This method requires floating-point arithmetic and works best with double or float types. - /// - /// The mass of the object. - /// The velocity (v = √(2E/m)). - public Velocity GetVelocityFromKineticEnergy(Mass mass) - { - Ensure.NotNull(mass); - - T energyValue = In(Units.Joule); - T massValue = mass.In(Units.Kilogram); - T two = T.CreateChecked(2.0); - T velocitySquared = two * energyValue / massValue; - - // Convert to double for square root calculation, then back to T - double velocitySquaredDouble = double.CreateChecked(velocitySquared); - double velocityDouble = Math.Sqrt(velocitySquaredDouble); - T velocityValue = T.CreateChecked(velocityDouble); - - return Velocity.Create(velocityValue); - } - - /// - /// Calculates power from energy and time (P = E/t). - /// - /// The energy. - /// The time duration. - /// The resulting power. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Power operator /(Energy energy, Time time) - { - Ensure.NotNull(energy); - Ensure.NotNull(time); - - T powerValue = energy.Value / time.Value; - - return Power.Create(powerValue); - } - - /// - /// Calculates time from energy and power (t = E/P). - /// - /// The energy. - /// The power. - /// The resulting time duration. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Time operator /(Energy energy, Power power) - { - Ensure.NotNull(energy); - Ensure.NotNull(power); - - T timeValue = energy.Value / power.Value; - - return Time.Create(timeValue); - } - - /// - /// Calculates force from energy and distance (F = E/d). - /// - /// The energy. - /// The distance. - /// The resulting force. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Force operator /(Energy energy, Length distance) - { - Ensure.NotNull(energy); - Ensure.NotNull(distance); - - T forceValue = energy.Value / distance.Value; - - return Force.Create(forceValue); - } - - /// - /// Calculates distance from energy and force (d = E/F). - /// - /// The energy. - /// The force. - /// The resulting distance. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Length operator /(Energy energy, Force force) - { - Ensure.NotNull(energy); - Ensure.NotNull(force); - - T distanceValue = energy.Value / force.Value; - - return Length.Create(distanceValue); - } - - /// - /// Calculates gravitational potential energy (PE = mgh). - /// - /// The mass. - /// The height. - /// The resulting gravitational potential energy. - public static Energy FromGravitationalPotential(Mass mass, Length height) - { - Ensure.NotNull(mass); - Ensure.NotNull(height); - - T gravityValue = PhysicalConstants.Generic.StandardGravity(); - T potentialEnergyValue = mass.Value * gravityValue * height.Value; - - return Create(potentialEnergyValue); - } - - /// - /// Calculates velocity from kinetic energy and mass (v = √(2KE/m)). - /// - /// The kinetic energy. - /// The mass. - /// The resulting velocity. - public static Velocity GetVelocityFromKineticEnergy(Energy kineticEnergy, Mass mass) - { - Ensure.NotNull(kineticEnergy); - Ensure.NotNull(mass); - - T twoValue = T.CreateChecked(2.0); - T velocitySquared = twoValue * kineticEnergy.Value / mass.Value; - T velocityValue = T.CreateChecked(Math.Sqrt(double.CreateChecked(velocitySquared))); - - return Velocity.Create(velocityValue); - } -} diff --git a/Semantics.Quantities/Mechanics/Force.cs b/Semantics.Quantities/Mechanics/Force.cs deleted file mode 100644 index a761797..0000000 --- a/Semantics.Quantities/Mechanics/Force.cs +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a force quantity with compile-time dimensional safety. -/// Force is defined by Newton's second law: F = ma -/// -/// The storage type for the quantity value. -public sealed record Force : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of force [M L T⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.Force; - - /// - /// Initializes a new instance of the class. - /// - public Force() : base() { } - - /// - /// Creates a new Force from a value in newtons. - /// - /// The value in newtons. - /// A new Force instance. - public static Force FromNewtons(T newtons) => Create(newtons); - - /// - /// Calculates force from mass and acceleration using Newton's second law (F = ma). - /// - /// The mass of the object. - /// The acceleration of the object. - /// The resulting force. - public static Force FromMassAndAcceleration(Mass mass, Acceleration acceleration) - { - Ensure.NotNull(mass); - Ensure.NotNull(acceleration); - - T massValue = mass.In(Units.Kilogram); - T accelerationValue = acceleration.In(Units.MetersPerSecondSquared); - T forceValue = massValue * accelerationValue; - - return Create(forceValue); - } - - /// - /// Calculates the weight force of an object under standard gravity. - /// - /// The mass of the object. - /// The weight force under standard gravity (9.80665 m/s²). - public static Force FromWeight(Mass mass) - { - Ensure.NotNull(mass); - - T massValue = mass.In(Units.Kilogram); - T gravity = PhysicalConstants.Generic.StandardGravity(); - T forceValue = massValue * gravity; - - return Create(forceValue); - } - - /// - /// Calculates the acceleration that this force would produce on a given mass. - /// - /// The mass to accelerate. - /// The resulting acceleration (a = F/m). - public Acceleration GetAcceleration(Mass mass) - { - Ensure.NotNull(mass); - - T forceValue = In(Units.Newton); - T massValue = mass.In(Units.Kilogram); - T accelerationValue = forceValue / massValue; - - return Acceleration.Create(accelerationValue); - } - - /// - /// Calculates power from force and velocity (P = F·v). - /// - /// The force. - /// The velocity. - /// The resulting power. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Power operator *(Force force, Velocity velocity) - { - Ensure.NotNull(force); - Ensure.NotNull(velocity); - - T powerValue = force.Value * velocity.Value; - - return Power.Create(powerValue); - } - - /// - /// Calculates power from velocity and force (P = F·v). - /// - /// The velocity. - /// The force. - /// The resulting power. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Power operator *(Velocity velocity, Force force) => force * velocity; - - /// - /// Calculates work/energy from force and displacement distance (W = F·d). - /// Use this when force is applied in the direction of motion. - /// For torque calculations, use CalculateTorque() method instead. - /// - /// The applied force. - /// The displacement distance in the direction of force. - /// The resulting work/energy. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Energy operator *(Force force, Length distance) - { - Ensure.NotNull(force); - Ensure.NotNull(distance); - - T energyValue = force.Value * distance.Value; - - return Energy.Create(energyValue); - } - - /// - /// Calculates work/energy from displacement distance and force (W = F·d). - /// Use this when force is applied in the direction of motion. - /// For torque calculations, use CalculateTorque() method instead. - /// - /// The displacement distance in the direction of force. - /// The applied force. - /// The resulting work/energy. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Energy operator *(Length distance, Force force) => force * distance; - - /// - /// Calculates acceleration from force and mass (a = F/m). - /// - /// The force. - /// The mass. - /// The resulting acceleration. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Acceleration operator /(Force force, Mass mass) - { - Ensure.NotNull(force); - Ensure.NotNull(mass); - - T accelerationValue = force.Value / mass.Value; - - return Acceleration.Create(accelerationValue); - } - - /// - /// Calculates mass from force and acceleration (m = F/a). - /// - /// The force. - /// The acceleration. - /// The resulting mass. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Mass operator /(Force force, Acceleration acceleration) - { - Ensure.NotNull(force); - Ensure.NotNull(acceleration); - - T massValue = force.Value / acceleration.Value; - - return Mass.Create(massValue); - } - - /// - /// Calculates torque from force and moment arm distance (τ = F×r). - /// Use this method when the force is applied perpendicular to the moment arm. - /// For work/energy calculations, use the * operator instead. - /// - /// The perpendicular distance from the axis of rotation. - /// The resulting torque. - public Torque CalculateTorque(Length momentArm) - { - Ensure.NotNull(momentArm); - - T torqueValue = Value * momentArm.Value; - - return Torque.Create(torqueValue); - } - - /// - /// Calculates torque from force and moment arm distance (τ = F×r). - /// Use this method when the force is applied perpendicular to the moment arm. - /// For work/energy calculations, use the * operator instead. - /// - /// The applied force. - /// The perpendicular distance from the axis of rotation. - /// The resulting torque. - public static Torque CalculateTorque(Force force, Length momentArm) - { - Ensure.NotNull(force); - Ensure.NotNull(momentArm); - - T torqueValue = force.Value * momentArm.Value; - - return Torque.Create(torqueValue); - } - - /// - /// Calculates pressure from force and area (P = F/A). - /// - /// The applied force. - /// The area over which force is applied. - /// The resulting pressure. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Pressure operator /(Force force, Area area) - { - Ensure.NotNull(force); - Ensure.NotNull(area); - - T pressureValue = force.Value / area.Value; - - return Pressure.Create(pressureValue); - } -} diff --git a/Semantics.Quantities/Mechanics/Length.cs b/Semantics.Quantities/Mechanics/Length.cs deleted file mode 100644 index a52f984..0000000 --- a/Semantics.Quantities/Mechanics/Length.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a length quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Length : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of length [L]. - public override PhysicalDimension Dimension => PhysicalDimensions.Length; - - /// - /// Initializes a new instance of the class. - /// - public Length() : base() { } - - /// - /// Creates a new Length from a value in meters. - /// - /// The value in meters. - /// A new Length instance. - public static Length FromMeters(T meters) => Create(meters); - - /// - /// Multiplies this length by another length to create an area. - /// - /// The first length. - /// The second length. - /// The resulting area. - public static Area operator *(Length left, Length right) - { - Ensure.NotNull(left); - Ensure.NotNull(right); - return Area.Create(left.Value * right.Value); - } - - /// - /// Multiplies this length by another length to create an area. - /// - /// The first length. - /// The second length. - /// The resulting area. - public static Area Multiply(Length left, Length right) => left * right; - - /// - /// Divides this length by time to create a velocity. - /// - /// The length. - /// The time. - /// The resulting velocity. - public static Velocity operator /(Length left, Time right) - { - Ensure.NotNull(left); - Ensure.NotNull(right); - return Velocity.Create(left.Value / right.Value); - } - - /// - /// Divides this length by time to create a velocity. - /// - /// The length. - /// The time. - /// The resulting velocity. - public static Velocity Divide(Length left, Time right) => left / right; -} - -// Note: Operator interfaces removed due to C# constraints on binary operators. -// Dimensional operations are defined directly on each quantity type. diff --git a/Semantics.Quantities/Mechanics/Mass.cs b/Semantics.Quantities/Mechanics/Mass.cs deleted file mode 100644 index 99a6bfe..0000000 --- a/Semantics.Quantities/Mechanics/Mass.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a mass quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Mass : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of mass [M]. - public override PhysicalDimension Dimension => PhysicalDimensions.Mass; - - /// - /// Initializes a new instance of the class. - /// - public Mass() : base() { } - - /// - /// Creates a new Mass from a value in kilograms. - /// - /// The value in kilograms. - /// A new Mass instance. - public static Mass FromKilograms(T kilograms) => Create(kilograms); - - /// - /// Calculates force from mass and acceleration using Newton's second law (F = ma). - /// - /// The mass. - /// The acceleration. - /// The resulting force. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Force operator *(Mass mass, Acceleration acceleration) - { - Ensure.NotNull(mass); - Ensure.NotNull(acceleration); - - T forceValue = mass.Value * acceleration.Value; - - return Force.Create(forceValue); - } - - /// - /// Calculates force from acceleration and mass using Newton's second law (F = ma). - /// - /// The acceleration. - /// The mass. - /// The resulting force. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Force operator *(Acceleration acceleration, Mass mass) => mass * acceleration; - - /// - /// Calculates momentum from mass and velocity (p = mv). - /// - /// The mass. - /// The velocity. - /// The resulting momentum. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Momentum operator *(Mass mass, Velocity velocity) - { - Ensure.NotNull(mass); - Ensure.NotNull(velocity); - - T momentumValue = mass.Value * velocity.Value; - - return Momentum.Create(momentumValue); - } - - /// - /// Calculates momentum from velocity and mass (p = mv). - /// - /// The velocity. - /// The mass. - /// The resulting momentum. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Momentum operator *(Velocity velocity, Mass mass) => mass * velocity; - - /// - /// Calculates density from mass and volume (ρ = m/V). - /// - /// The mass. - /// The volume. - /// The resulting density. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Density operator /(Mass mass, Volume volume) - { - Ensure.NotNull(mass); - Ensure.NotNull(volume); - - T densityValue = mass.Value / volume.Value; - - return Density.Create(densityValue); - } -} diff --git a/Semantics.Quantities/Mechanics/MomentOfInertia.cs b/Semantics.Quantities/Mechanics/MomentOfInertia.cs deleted file mode 100644 index caa9928..0000000 --- a/Semantics.Quantities/Mechanics/MomentOfInertia.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a moment of inertia quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record MomentOfInertia : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of momentofinertia [M L²]. - public override PhysicalDimension Dimension => PhysicalDimensions.MomentOfInertia; - - /// - /// Initializes a new instance of the class. - /// - public MomentOfInertia() : base() { } - - /// - /// Creates a new MomentOfInertia from a value in kilogram-square meters. - /// - /// The value in kilogram-square meters. - /// A new MomentOfInertia instance. - public static MomentOfInertia FromKilogramSquareMeters(T kilogramSquareMeters) => Create(kilogramSquareMeters); - - /// - /// Multiplies mass by area to create moment of inertia. - /// - /// The mass. - /// The area. - /// The resulting moment of inertia. - public static MomentOfInertia Multiply(Mass mass, Area area) - { - Ensure.NotNull(mass); - Ensure.NotNull(area); - return Create(mass.Value * area.Value); - } - - /// - /// Divides moment of inertia by mass to get area. - /// - /// The moment of inertia. - /// The mass. - /// The resulting area. - public static Area Divide(MomentOfInertia momentOfInertia, Mass mass) - { - Ensure.NotNull(momentOfInertia); - Ensure.NotNull(mass); - return Area.Create(momentOfInertia.Value / mass.Value); - } - - /// - /// Divides moment of inertia by area to get mass. - /// - /// The moment of inertia. - /// The area. - /// The resulting mass. - public static Mass Divide(MomentOfInertia momentOfInertia, Area area) - { - Ensure.NotNull(momentOfInertia); - Ensure.NotNull(area); - return Mass.Create(momentOfInertia.Value / area.Value); - } -} diff --git a/Semantics.Quantities/Mechanics/Momentum.cs b/Semantics.Quantities/Mechanics/Momentum.cs deleted file mode 100644 index 4843e4c..0000000 --- a/Semantics.Quantities/Mechanics/Momentum.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a momentum quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Momentum : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of momentum [M L T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.Momentum; - - /// - /// Initializes a new instance of the class. - /// - public Momentum() : base() { } - - /// - /// Creates a new Momentum from a value in kilogram-meters per second. - /// - /// The value in kilogram-meters per second. - /// A new Momentum instance. - public static Momentum FromKilogramMetersPerSecond(T kilogramMetersPerSecond) => Create(kilogramMetersPerSecond); - - /// - /// Multiplies mass by velocity to create momentum. - /// - /// The mass. - /// The velocity. - /// The resulting momentum. - public static Momentum Multiply(Mass mass, Velocity velocity) - { - Ensure.NotNull(mass); - Ensure.NotNull(velocity); - return Create(mass.Value * velocity.Value); - } - - /// - /// Divides momentum by mass to get velocity. - /// - /// The momentum. - /// The mass. - /// The resulting velocity. - public static Velocity Divide(Momentum momentum, Mass mass) - { - Ensure.NotNull(momentum); - Ensure.NotNull(mass); - return Velocity.Create(momentum.Value / mass.Value); - } - - /// - /// Divides momentum by velocity to get mass. - /// - /// The momentum. - /// The velocity. - /// The resulting mass. - public static Mass Divide(Momentum momentum, Velocity velocity) - { - Ensure.NotNull(momentum); - Ensure.NotNull(velocity); - return Mass.Create(momentum.Value / velocity.Value); - } -} diff --git a/Semantics.Quantities/Mechanics/Power.cs b/Semantics.Quantities/Mechanics/Power.cs deleted file mode 100644 index 309f023..0000000 --- a/Semantics.Quantities/Mechanics/Power.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a power quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Power : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of power [M L² T⁻³]. - public override PhysicalDimension Dimension => PhysicalDimensions.Power; - - /// - /// Initializes a new instance of the class. - /// - public Power() : base() { } - - /// - /// Creates a new Power from a value in watts. - /// - /// The value in watts. - /// A new Power instance. - public static Power FromWatts(T watts) => Create(watts); - - /// - /// Calculates energy from power and time (E = P·t). - /// - /// The power. - /// The time duration. - /// The resulting energy. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Energy operator *(Power power, Time time) - { - Ensure.NotNull(power); - Ensure.NotNull(time); - - T energyValue = power.Value * time.Value; - - return Energy.Create(energyValue); - } - - /// - /// Calculates energy from time and power (E = P·t). - /// - /// The time duration. - /// The power. - /// The resulting energy. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Energy operator *(Time time, Power power) => power * time; -} diff --git a/Semantics.Quantities/Mechanics/Pressure.cs b/Semantics.Quantities/Mechanics/Pressure.cs deleted file mode 100644 index 17bd196..0000000 --- a/Semantics.Quantities/Mechanics/Pressure.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a pressure quantity with compile-time dimensional safety. -/// Pressure is defined as force per unit area (P = F/A). -/// -/// The storage type for the quantity value. -public sealed record Pressure : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of pressure [M L⁻¹ T⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.Pressure; - - /// - /// Initializes a new instance of the class. - /// - public Pressure() : base() { } - - /// - /// Creates a new Pressure from a value in pascals. - /// - /// The value in pascals. - /// A new Pressure instance. - public static Pressure FromPascals(T pascals) => Create(pascals); - - /// - /// Calculates force from pressure and area (F = P×A). - /// - /// The pressure. - /// The area. - /// The resulting force. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Force operator *(Pressure pressure, Area area) - { - Ensure.NotNull(pressure); - Ensure.NotNull(area); - - T forceValue = pressure.Value * area.Value; - - return Force.Create(forceValue); - } - - /// - /// Creates a pressure instance representing standard atmospheric pressure. - /// - /// Standard atmospheric pressure (101,325 Pa). - public static Pressure StandardAtmospheric() => - Create(PhysicalConstants.Generic.StandardAtmosphericPressure()); - - /// - /// Calculates the force that this pressure would exert on a given area. - /// - /// The area to calculate force for. - /// The resulting force (F = P·A). - public Force GetForce(Area area) - { - Ensure.NotNull(area); - - T pressureValue = In(Units.Pascal); - T areaValue = area.In(Units.SquareMeter); - T forceValue = pressureValue * areaValue; - - return Force.Create(forceValue); - } - - /// - /// Calculates the hydrostatic pressure at a given depth in a fluid. - /// - /// The density of the fluid. - /// The depth below the surface. - /// The gravitational acceleration (optional, defaults to standard gravity). - /// The hydrostatic pressure (P = ρgh). - public static Pressure FromHydrostaticPressure(Density density, Length depth, Acceleration? gravity = null) - { - Ensure.NotNull(density); - Ensure.NotNull(depth); - - T densityValue = density.In(Units.KilogramPerCubicMeter); - T depthValue = depth.In(Units.Meter); - T gravityValue = gravity?.In(Units.MetersPerSecondSquared) ?? PhysicalConstants.Generic.StandardGravity(); - T pressureValue = densityValue * gravityValue * depthValue; - - return Create(pressureValue); - } -} diff --git a/Semantics.Quantities/Mechanics/SpecificGravity.cs b/Semantics.Quantities/Mechanics/SpecificGravity.cs deleted file mode 100644 index 8ee4c84..0000000 --- a/Semantics.Quantities/Mechanics/SpecificGravity.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a specific gravity (relative density) quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record SpecificGravity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of specificgravity [dimensionless]. - public override PhysicalDimension Dimension => PhysicalDimensions.Dimensionless; - - /// - /// Initializes a new instance of the class. - /// - public SpecificGravity() : base() { } - - /// - /// Creates a new SpecificGravity from a dimensionless ratio value. - /// - /// The dimensionless ratio relative to reference density (typically water). - /// A new SpecificGravity instance. - public static SpecificGravity FromRatio(T ratio) => Create(ratio); - - /// - /// Divides density by reference density to create specific gravity. - /// - /// The density. - /// The reference density (typically water at standard conditions). - /// The resulting specific gravity. - public static SpecificGravity Divide(Density density, Density referenceDensity) - { - Ensure.NotNull(density); - Ensure.NotNull(referenceDensity); - return Create(density.Value / referenceDensity.Value); - } - - /// - /// Multiplies specific gravity by reference density to get density. - /// - /// The specific gravity. - /// The reference density. - /// The resulting density. - public static Density Multiply(SpecificGravity specificGravity, Density referenceDensity) - { - Ensure.NotNull(specificGravity); - Ensure.NotNull(referenceDensity); - return Density.Create(specificGravity.Value * referenceDensity.Value); - } -} diff --git a/Semantics.Quantities/Mechanics/Time.cs b/Semantics.Quantities/Mechanics/Time.cs deleted file mode 100644 index 0547957..0000000 --- a/Semantics.Quantities/Mechanics/Time.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a time quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Time : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of time [T]. - public override PhysicalDimension Dimension => PhysicalDimensions.Time; - - /// - /// Initializes a new instance of the class. - /// - public Time() : base() { } - - /// - /// Creates a new Time from a value in seconds. - /// - /// The value in seconds. - /// A new Time instance. - public static Time FromSeconds(T seconds) => Create(seconds); -} diff --git a/Semantics.Quantities/Mechanics/Torque.cs b/Semantics.Quantities/Mechanics/Torque.cs deleted file mode 100644 index fb13abd..0000000 --- a/Semantics.Quantities/Mechanics/Torque.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a torque quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Torque : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of torque [M L² T⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.Torque; - - /// - /// Initializes a new instance of the class. - /// - public Torque() : base() { } - - /// - /// Creates a new Torque from a value in newton-meters. - /// - /// The value in newton-meters. - /// A new Torque instance. - public static Torque FromNewtonMeters(T newtonMeters) => Create(newtonMeters); - - /// - /// Multiplies force by length to create torque. - /// - /// The force. - /// The length (lever arm). - /// The resulting torque. - public static Torque Multiply(Force force, Length length) - { - Ensure.NotNull(force); - Ensure.NotNull(length); - return Create(force.Value * length.Value); - } - - /// - /// Divides torque by force to get length. - /// - /// The torque. - /// The force. - /// The resulting length. - public static Length Divide(Torque torque, Force force) - { - Ensure.NotNull(torque); - Ensure.NotNull(force); - return Length.Create(torque.Value / force.Value); - } - - /// - /// Divides torque by length to get force. - /// - /// The torque. - /// The length. - /// The resulting force. - public static Force Divide(Torque torque, Length length) - { - Ensure.NotNull(torque); - Ensure.NotNull(length); - return Force.Create(torque.Value / length.Value); - } - - /// - /// Returns the tangential force from the torque and radius. - /// - /// The radius or moment arm. - /// The tangential force. - public Force GetTangentialForce(Length length) - { - Ensure.NotNull(length); - - return Force.Create(Value / length.Value); - } - - /// - /// Calculates force from torque and distance (F = τ/r). - /// - /// The torque. - /// The moment arm distance. - /// The resulting force. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Force operator /(Torque torque, Length distance) - { - Ensure.NotNull(torque); - Ensure.NotNull(distance); - - T forceValue = torque.Value / distance.Value; - - return Force.Create(forceValue); - } - - /// - /// Calculates distance from torque and force (r = τ/F). - /// - /// The torque. - /// The force. - /// The resulting moment arm distance. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Length operator /(Torque torque, Force force) - { - Ensure.NotNull(torque); - Ensure.NotNull(force); - - T distanceValue = torque.Value / force.Value; - - return Length.Create(distanceValue); - } -} diff --git a/Semantics.Quantities/Mechanics/Velocity.cs b/Semantics.Quantities/Mechanics/Velocity.cs deleted file mode 100644 index 980bea3..0000000 --- a/Semantics.Quantities/Mechanics/Velocity.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a velocity quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Velocity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of velocity [L T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.Velocity; - - /// - /// Initializes a new instance of the class. - /// - public Velocity() : base() { } - - /// - /// Creates a new Velocity from a value in meters per second. - /// - /// The value in meters per second. - /// A new Velocity instance. - public static Velocity FromMetersPerSecond(T metersPerSecond) => Create(metersPerSecond); - - /// - /// Divides this velocity by time to create an acceleration. - /// - /// The velocity. - /// The time. - /// The resulting acceleration. - public static Acceleration operator /(Velocity left, Time right) - { - Ensure.NotNull(left); - Ensure.NotNull(right); - - T velocityValue = left.In(Units.MetersPerSecond); - T timeValue = right.In(Units.Second); - T accelerationValue = velocityValue / timeValue; - - return Acceleration.Create(accelerationValue); - } - - /// - /// Divides this velocity by time to create an acceleration. - /// - /// The velocity. - /// The time. - /// The resulting acceleration. - public static Acceleration Divide(Velocity left, Time right) => left / right; - - /// - /// Calculates distance from velocity and time (d = v·t). - /// - /// The velocity. - /// The time duration. - /// The resulting distance. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Length operator *(Velocity velocity, Time time) - { - Ensure.NotNull(velocity); - Ensure.NotNull(time); - - T distanceValue = velocity.Value * time.Value; - - return Length.Create(distanceValue); - } - - /// - /// Calculates distance from time and velocity (d = v·t). - /// - /// The time duration. - /// The velocity. - /// The resulting distance. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Length operator *(Time time, Velocity velocity) => velocity * time; - - /// - /// Calculates time from velocity and distance (t = d/v). - /// - /// The distance. - /// The velocity. - /// The resulting time duration. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Time operator /(Length distance, Velocity velocity) - { - Ensure.NotNull(distance); - Ensure.NotNull(velocity); - - T timeValue = distance.Value / velocity.Value; - - return Time.Create(timeValue); - } - - /// - /// Calculates time from velocity and acceleration (t = v/a). - /// - /// The velocity. - /// The acceleration. - /// The resulting time duration. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Time operator /(Velocity velocity, Acceleration acceleration) - { - Ensure.NotNull(velocity); - Ensure.NotNull(acceleration); - - T timeValue = velocity.Value / acceleration.Value; - - return Time.Create(timeValue); - } -} diff --git a/Semantics.Quantities/Mechanics/Volume.cs b/Semantics.Quantities/Mechanics/Volume.cs deleted file mode 100644 index 2698c34..0000000 --- a/Semantics.Quantities/Mechanics/Volume.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a volume quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Volume : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of volume [L³]. - public override PhysicalDimension Dimension => PhysicalDimensions.Volume; - - /// - /// Initializes a new instance of the class. - /// - public Volume() : base() { } - - /// - /// Creates a new Volume from a value in cubic meters. - /// - /// The value in cubic meters. - /// A new Volume instance. - public static Volume FromCubicMeters(T cubicMeters) => Create(cubicMeters); -} diff --git a/Semantics.Quantities/Nuclear/AbsorbedDose.cs b/Semantics.Quantities/Nuclear/AbsorbedDose.cs deleted file mode 100644 index 231831d..0000000 --- a/Semantics.Quantities/Nuclear/AbsorbedDose.cs +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents absorbed dose with a specific unit of measurement. -/// Absorbed dose is the amount of energy deposited per unit mass by ionizing radiation. -/// It is measured in grays (Gy) in the SI system. -/// -/// The numeric type for the absorbed dose value. -public sealed record AbsorbedDose : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of absorbed dose [L² T⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.AbsorbedDose; - - /// Initializes a new instance of the class. - public AbsorbedDose() : base() { } - - /// Creates a new absorbed dose from a value in grays. - /// The absorbed dose in grays. - /// A new AbsorbedDose instance. - public static AbsorbedDose FromGrays(T grays) => Create(grays); - - /// Creates a new absorbed dose from a value in rads. - /// The absorbed dose in rads. - /// A new AbsorbedDose instance. - public static AbsorbedDose FromRads(T rads) - { - T grays = rads * PhysicalConstants.Generic.RadsToGrays(); - return Create(grays); - } - - /// Creates a new absorbed dose from a value in milligrays. - /// The absorbed dose in milligrays. - /// A new AbsorbedDose instance. - public static AbsorbedDose FromMilligrays(T milligrays) - { - T grays = milligrays / T.CreateChecked(1000); - return Create(grays); - } - - /// Creates a new absorbed dose from a value in micrograys. - /// The absorbed dose in micrograys. - /// A new AbsorbedDose instance. - public static AbsorbedDose FromMicrograys(T micrograys) - { - T grays = micrograys / T.CreateChecked(1e6); - return Create(grays); - } - - /// Creates a new absorbed dose from energy and mass. - /// The energy absorbed. - /// The mass of the absorbing material. - /// A new AbsorbedDose instance. - /// - /// Uses the relationship: D = E / m - /// where D is absorbed dose, E is energy, and m is mass. - /// - public static AbsorbedDose FromEnergyAndMass(Energy energy, Mass mass) - { - Ensure.NotNull(energy); - Ensure.NotNull(mass); - - T energyJoules = energy.In(Units.Joule); - T massKg = mass.In(Units.Kilogram); - - if (massKg == T.Zero) - { - throw new ArgumentException("Mass cannot be zero.", nameof(mass)); - } - - T dose = energyJoules / massKg; - return Create(dose); - } - - /// Calculates the energy absorbed from dose and mass. - /// The mass of the absorbing material. - /// The energy absorbed. - /// - /// Uses the relationship: E = D × m - /// where E is energy, D is absorbed dose, and m is mass. - /// - public Energy CalculateEnergyAbsorbed(Mass mass) - { - Ensure.NotNull(mass); - - T dose = In(Units.Gray); - T massKg = mass.In(Units.Kilogram); - T energy = dose * massKg; - - return Energy.Create(energy); - } - - /// Calculates equivalent dose using radiation weighting factor. - /// The radiation weighting factor (dimensionless). - /// The equivalent dose. - /// - /// Uses the relationship: H = D × wR - /// where H is equivalent dose, D is absorbed dose, and wR is radiation weighting factor. - /// - public EquivalentDose CalculateEquivalentDose(T radiationWeightingFactor) - { - T dose = In(Units.Gray); - T equivalentDose = dose * radiationWeightingFactor; - return EquivalentDose.Create(equivalentDose); - } - - /// Calculates dose rate from absorbed dose and time. - /// The time period over which the dose was absorbed. - /// The dose rate in Gy/s. - /// - /// Uses the relationship: Dose Rate = D / t - /// where D is absorbed dose and t is time. - /// - public T CalculateDoseRate(Time time) - { - Ensure.NotNull(time); - - T dose = In(Units.Gray); - T timeSeconds = time.In(Units.Second); - - return timeSeconds == T.Zero ? throw new ArgumentException("Time cannot be zero.", nameof(time)) : dose / timeSeconds; - } - - /// Calculates the mass of material from absorbed dose and energy. - /// The energy absorbed. - /// The mass of the absorbing material. - /// - /// Uses the relationship: m = E / D - /// where m is mass, E is energy, and D is absorbed dose. - /// - public Mass CalculateMass(Energy energy) - { - Ensure.NotNull(energy); - - T dose = In(Units.Gray); - T energyJoules = energy.In(Units.Joule); - - return dose == T.Zero - ? throw new InvalidOperationException("Absorbed dose cannot be zero for mass calculation.") - : Mass.Create(energyJoules / dose); - } - - /// Determines if the dose is potentially lethal. - /// True if the dose exceeds 4 Gy (approximate LD50 for humans). - /// - /// Reference doses for humans: - /// - 0.25 Gy: Mild radiation sickness - /// - 1 Gy: Severe radiation sickness - /// - 4 Gy: LD50 (50% lethality in 30 days without treatment) - /// - 10+ Gy: Lethal within days - /// - public bool IsPotentiallyLethal() - { - T dose = In(Units.Gray); - T lethalThreshold = T.CreateChecked(4.0); // 4 Gy LD50 - return dose > lethalThreshold; - } - - /// Determines if the dose causes radiation sickness symptoms. - /// True if the dose exceeds 0.25 Gy. - public bool CausesRadiationSickness() - { - T dose = In(Units.Gray); - T threshold = T.CreateChecked(0.25); // 0.25 Gy threshold for symptoms - return dose > threshold; - } -} diff --git a/Semantics.Quantities/Nuclear/EquivalentDose.cs b/Semantics.Quantities/Nuclear/EquivalentDose.cs deleted file mode 100644 index 4e210dd..0000000 --- a/Semantics.Quantities/Nuclear/EquivalentDose.cs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents equivalent dose with a specific unit of measurement. -/// Equivalent dose is the absorbed dose weighted by the biological effectiveness of the radiation. -/// It is measured in sieverts (Sv) in the SI system. -/// -/// The numeric type for the equivalent dose value. -public sealed record EquivalentDose : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of equivalent dose [L² T⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.EquivalentDose; - - /// Initializes a new instance of the class. - public EquivalentDose() : base() { } - - /// Creates a new equivalent dose from a value in sieverts. - /// The equivalent dose in sieverts. - /// A new EquivalentDose instance. - public static EquivalentDose FromSieverts(T sieverts) => Create(sieverts); - - /// Creates a new equivalent dose from a value in rems. - /// The equivalent dose in rems. - /// A new EquivalentDose instance. - public static EquivalentDose FromRems(T rems) - { - T sieverts = rems * PhysicalConstants.Generic.RemsToSieverts(); - return Create(sieverts); - } - - /// Creates a new equivalent dose from a value in millisieverts. - /// The equivalent dose in millisieverts. - /// A new EquivalentDose instance. - public static EquivalentDose FromMillisieverts(T millisieverts) - { - T sieverts = millisieverts / T.CreateChecked(1000); - return Create(sieverts); - } - - /// Creates a new equivalent dose from a value in microsieverts. - /// The equivalent dose in microsieverts. - /// A new EquivalentDose instance. - public static EquivalentDose FromMicrosieverts(T microsieverts) - { - T sieverts = microsieverts / T.CreateChecked(1e6); - return Create(sieverts); - } - - /// Creates a new equivalent dose from absorbed dose and radiation weighting factor. - /// The absorbed dose. - /// The radiation weighting factor (dimensionless). - /// A new EquivalentDose instance. - /// - /// Uses the relationship: H = D × wR - /// where H is equivalent dose, D is absorbed dose, and wR is radiation weighting factor. - /// - public static EquivalentDose FromAbsorbedDoseAndWeightingFactor(AbsorbedDose absorbedDose, T radiationWeightingFactor) - { - Ensure.NotNull(absorbedDose); - - T dose = absorbedDose.In(Units.Gray); - T equivalentDose = dose * radiationWeightingFactor; - return Create(equivalentDose); - } - - /// Calculates effective dose using tissue weighting factors. - /// The tissue weighting factor (dimensionless). - /// The effective dose contribution from this tissue. - /// - /// Uses the relationship: E = Σ(HT × wT) - /// where E is effective dose, HT is equivalent dose to tissue T, and wT is tissue weighting factor. - /// This method calculates the contribution from one tissue type. - /// - public EquivalentDose CalculateEffectiveDoseContribution(T tissueWeightingFactor) - { - T equivalentDose = In(Units.Sievert); - T effectiveDoseContribution = equivalentDose * tissueWeightingFactor; - return Create(effectiveDoseContribution); - } - - /// Calculates the absorbed dose from equivalent dose and radiation weighting factor. - /// The radiation weighting factor (dimensionless). - /// The absorbed dose. - /// - /// Uses the relationship: D = H / wR - /// where D is absorbed dose, H is equivalent dose, and wR is radiation weighting factor. - /// - public AbsorbedDose CalculateAbsorbedDose(T radiationWeightingFactor) - { - if (radiationWeightingFactor == T.Zero) - { - throw new ArgumentException("Radiation weighting factor cannot be zero.", nameof(radiationWeightingFactor)); - } - - T equivalentDose = In(Units.Sievert); - return AbsorbedDose.Create(equivalentDose / radiationWeightingFactor); - } - - /// Calculates dose rate from equivalent dose and time. - /// The time period over which the dose was received. - /// The dose rate in Sv/s. - /// - /// Uses the relationship: Dose Rate = H / t - /// where H is equivalent dose and t is time. - /// - public T CalculateDoseRate(Time time) - { - Ensure.NotNull(time); - - T dose = In(Units.Sievert); - T timeSeconds = time.In(Units.Second); - - return timeSeconds == T.Zero ? throw new ArgumentException("Time cannot be zero.", nameof(time)) : dose / timeSeconds; - } - - /// Determines if the dose exceeds common safety limits. - /// True if the dose exceeds 20 mSv (annual limit for radiation workers). - /// - /// Common limits: - /// - General public: 1 mSv/year - /// - Radiation workers: 20 mSv/year - /// - Emergency workers: 100 mSv (acute) - /// - public bool ExceedsAnnualWorkerLimit() - { - T dose = In(Units.Sievert); - T limit = T.CreateChecked(0.02); // 20 mSv in Sv - return dose > limit; - } - - /// Determines if the dose exceeds the annual limit for the general public. - /// True if the dose exceeds 1 mSv. - public bool ExceedsPublicLimit() - { - T dose = In(Units.Sievert); - T limit = T.CreateChecked(0.001); // 1 mSv in Sv - return dose > limit; - } -} diff --git a/Semantics.Quantities/Nuclear/Exposure.cs b/Semantics.Quantities/Nuclear/Exposure.cs deleted file mode 100644 index e1148f3..0000000 --- a/Semantics.Quantities/Nuclear/Exposure.cs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents ionizing radiation exposure with a specific unit of measurement. -/// Exposure is the measure of ionization produced in air by X-rays or gamma rays. -/// It is measured in coulombs per kilogram (C/kg) in the SI system. -/// -/// The numeric type for the exposure value. -public sealed record Exposure : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of exposure [M⁻¹ T I]. - public override PhysicalDimension Dimension => PhysicalDimensions.Exposure; - - /// Initializes a new instance of the class. - public Exposure() : base() { } - - /// Creates a new exposure from a value in coulombs per kilogram. - /// The exposure in coulombs per kilogram. - /// A new Exposure instance. - public static Exposure FromCoulombsPerKilogram(T coulombsPerKilogram) => Create(coulombsPerKilogram); - - /// Creates a new exposure from a value in roentgens. - /// The exposure in roentgens. - /// A new Exposure instance. - public static Exposure FromRoentgens(T roentgens) - { - T coulombsPerKg = roentgens * T.CreateChecked(2.58e-4); - return Create(coulombsPerKg); - } - - /// Creates a new exposure from a value in milliroentgens. - /// The exposure in milliroentgens. - /// A new Exposure instance. - public static Exposure FromMilliroentgens(T milliroentgens) - { - T coulombsPerKg = milliroentgens * T.CreateChecked(2.58e-7); - return Create(coulombsPerKg); - } - - /// Creates a new exposure from charge and mass. - /// The electric charge produced. - /// The mass of air. - /// A new Exposure instance. - /// - /// Uses the relationship: X = Q / m - /// where X is exposure, Q is charge, and m is mass. - /// - public static Exposure FromChargeAndMass(ElectricCharge charge, Mass mass) - { - Ensure.NotNull(charge); - Ensure.NotNull(mass); - - T chargeCoulombs = charge.In(Units.Coulomb); - T massKg = mass.In(Units.Kilogram); - - if (massKg == T.Zero) - { - throw new ArgumentException("Mass cannot be zero.", nameof(mass)); - } - - T exposure = chargeCoulombs / massKg; - return Create(exposure); - } - - /// Calculates the charge produced from exposure and mass. - /// The mass of air. - /// The electric charge produced. - /// - /// Uses the relationship: Q = X × m - /// where Q is charge, X is exposure, and m is mass. - /// - public ElectricCharge CalculateCharge(Mass mass) - { - Ensure.NotNull(mass); - - T exposure = In(Units.CoulombPerKilogram); - T massKg = mass.In(Units.Kilogram); - T charge = exposure * massKg; - - return ElectricCharge.Create(charge); - } - - /// Calculates the mass of air from exposure and charge. - /// The electric charge produced. - /// The mass of air. - /// - /// Uses the relationship: m = Q / X - /// where m is mass, Q is charge, and X is exposure. - /// - public Mass CalculateMass(ElectricCharge charge) - { - Ensure.NotNull(charge); - - T exposure = In(Units.CoulombPerKilogram); - T chargeCoulombs = charge.In(Units.Coulomb); - - if (exposure == T.Zero) - { - throw new InvalidOperationException("Exposure cannot be zero for mass calculation."); - } - - T mass = chargeCoulombs / exposure; - return Mass.Create(mass); - } - - /// Calculates exposure rate from exposure and time. - /// The time period over which the exposure occurred. - /// The exposure rate in C/(kg·s). - /// - /// Uses the relationship: Exposure Rate = X / t - /// where X is exposure and t is time. - /// - public T CalculateExposureRate(Time time) - { - Ensure.NotNull(time); - - T exposure = In(Units.CoulombPerKilogram); - T timeSeconds = time.In(Units.Second); - - return timeSeconds == T.Zero ? throw new ArgumentException("Time cannot be zero.", nameof(time)) : exposure / timeSeconds; - } - - /// Converts exposure to approximate absorbed dose in air. - /// The approximate absorbed dose in air. - /// - /// Uses the conversion factor: 1 R ≈ 0.00876 Gy in air - /// This is an approximation and may vary with photon energy. - /// - public AbsorbedDose ToAbsorbedDoseInAir() - { - T exposureCperKg = In(Units.CoulombPerKilogram); - T exposureRoentgens = exposureCperKg / T.CreateChecked(2.58e-4); - T doseGray = exposureRoentgens * PhysicalConstants.Generic.RoentgensToGraysInAir(); - return AbsorbedDose.Create(doseGray); - } - - /// Determines if the exposure exceeds occupational safety limits. - /// True if the exposure exceeds 50 mR (typical annual limit for radiation workers). - /// - /// Common exposure limits: - /// - General public: ~2 mR/year background - /// - Radiation workers: ~50 mR/year (varies by jurisdiction) - /// - Medical X-ray: ~10 mR typical chest X-ray - /// - public bool ExceedsOccupationalLimit() - { - T exposure = In(Units.Coulomb); - T limitCperKg = T.CreateChecked(50) * T.CreateChecked(2.58e-7); // 50 mR in C/kg - return exposure > limitCperKg; - } - - /// Determines if the exposure is within natural background levels. - /// True if the exposure is less than 2 mR (typical annual background). - public bool IsBackgroundLevel() - { - T exposure = In(Units.Coulomb); - T backgroundCperKg = T.CreateChecked(2) * T.CreateChecked(2.58e-7); // 2 mR in C/kg - return exposure <= backgroundCperKg; - } -} diff --git a/Semantics.Quantities/Nuclear/NuclearCrossSection.cs b/Semantics.Quantities/Nuclear/NuclearCrossSection.cs deleted file mode 100644 index 8035d94..0000000 --- a/Semantics.Quantities/Nuclear/NuclearCrossSection.cs +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents nuclear cross section with a specific unit of measurement. -/// Nuclear cross section is a measure of the probability of interaction between a particle and a nucleus. -/// It is measured in barns (b) where 1 barn = 10⁻²⁴ cm² = 10⁻²⁸ m². -/// -/// The numeric type for the nuclear cross section value. -public sealed record NuclearCrossSection : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of nuclear cross section [L²]. - public override PhysicalDimension Dimension => PhysicalDimensions.NuclearCrossSection; - - /// Initializes a new instance of the class. - public NuclearCrossSection() : base() { } - - /// Creates a new nuclear cross section from a value in barns. - /// The nuclear cross section in barns. - /// A new NuclearCrossSection instance. - public static NuclearCrossSection FromBarns(T barns) => Create(barns); - - /// Creates a new nuclear cross section from a value in millibarns. - /// The nuclear cross section in millibarns. - /// A new NuclearCrossSection instance. - public static NuclearCrossSection FromMillibarns(T millibarns) - { - T barns = millibarns / T.CreateChecked(1000); - return Create(barns); - } - - /// Creates a new nuclear cross section from a value in microbarns. - /// The nuclear cross section in microbarns. - /// A new NuclearCrossSection instance. - public static NuclearCrossSection FromMicrobarns(T microbarns) - { - T barns = microbarns / T.CreateChecked(1e6); - return Create(barns); - } - - /// Creates a new nuclear cross section from a value in femtobarns. - /// The nuclear cross section in femtobarns. - /// A new NuclearCrossSection instance. - public static NuclearCrossSection FromFemtobarns(T femtobarns) - { - T barns = femtobarns / T.CreateChecked(1e15); - return Create(barns); - } - - /// Creates a new nuclear cross section from a value in square meters. - /// The nuclear cross section in square meters. - /// A new NuclearCrossSection instance. - public static NuclearCrossSection FromSquareMeters(T squareMeters) - { - T barns = squareMeters * T.CreateChecked(1e28); - return Create(barns); - } - - /// Creates a new nuclear cross section from a value in square centimeters. - /// The nuclear cross section in square centimeters. - /// A new NuclearCrossSection instance. - public static NuclearCrossSection FromSquareCentimeters(T squareCentimeters) - { - T barns = squareCentimeters * T.CreateChecked(1e24); - return Create(barns); - } - - /// Calculates the reaction rate from cross section, flux, and number density. - /// The neutron flux (particles per cm² per second). - /// The number density of target nuclei (nuclei per cm³). - /// The reaction rate (reactions per cm³ per second). - /// - /// Uses the relationship: R = σ × Φ × N - /// where R is reaction rate, σ is cross section, Φ is flux, and N is number density. - /// - public T CalculateReactionRate(T neutronFlux, T numberDensity) - { - T crossSection = In(Units.Barn); - T crossSectionCm2 = crossSection * T.CreateChecked(1e-24); // Convert barns to cm² - return crossSectionCm2 * neutronFlux * numberDensity; - } - - /// Calculates the macroscopic cross section from microscopic cross section and number density. - /// The number density of target nuclei (nuclei per cm³). - /// The macroscopic cross section (cm⁻¹). - /// - /// Uses the relationship: Σ = σ × N - /// where Σ is macroscopic cross section, σ is microscopic cross section, and N is number density. - /// - public T CalculateMacroscopicCrossSection(T numberDensity) - { - T crossSection = In(Units.Barn); - T crossSectionCm2 = crossSection * T.CreateChecked(1e-24); // Convert barns to cm² - return crossSectionCm2 * numberDensity; - } - - /// Calculates the mean free path from macroscopic cross section. - /// The number density of target nuclei (nuclei per cm³). - /// The mean free path in cm. - /// - /// Uses the relationship: λ = 1 / Σ = 1 / (σ × N) - /// where λ is mean free path, Σ is macroscopic cross section, σ is microscopic cross section, and N is number density. - /// - public T CalculateMeanFreePath(T numberDensity) - { - T macroscopicCrossSection = CalculateMacroscopicCrossSection(numberDensity); - - return macroscopicCrossSection == T.Zero - ? throw new InvalidOperationException("Macroscopic cross section cannot be zero for mean free path calculation.") - : T.One / macroscopicCrossSection; - } - - /// Calculates the transmission probability through a material. - /// The thickness of the material in cm. - /// The number density of target nuclei (nuclei per cm³). - /// The transmission probability (0 to 1). - /// - /// Uses the relationship: T = e^(-Σ×x) = e^(-σ×N×x) - /// where T is transmission, Σ is macroscopic cross section, and x is thickness. - /// - public T CalculateTransmissionProbability(T thickness, T numberDensity) - { - T macroscopicCrossSection = CalculateMacroscopicCrossSection(numberDensity); - T exponent = -macroscopicCrossSection * thickness; - return T.CreateChecked(Math.Exp(double.CreateChecked(exponent))); - } - - /// Calculates the attenuation coefficient from cross section and number density. - /// The number density of target nuclei (nuclei per cm³). - /// The linear attenuation coefficient (cm⁻¹). - /// - /// The attenuation coefficient is equivalent to the macroscopic cross section. - /// Uses the relationship: μ = σ × N - /// where μ is attenuation coefficient, σ is cross section, and N is number density. - /// - public T CalculateAttenuationCoefficient(T numberDensity) => CalculateMacroscopicCrossSection(numberDensity); - - /// Determines if the cross section is typical for nuclear reactions. - /// True if the cross section is between 1 millibarn and 10 barns. - /// - /// Typical nuclear cross sections: - /// - Neutron capture: 1-1000 barns - /// - Nuclear fission: 1-1000 barns - /// - Elastic scattering: 1-20 barns - /// - High-energy reactions: millibarns to microbarns - /// - public bool IsTypicalNuclearReaction() - { - T crossSection = In(Units.Barn); - T lowerBound = T.CreateChecked(0.001); // 1 millibarn - T upperBound = T.CreateChecked(10.0); // 10 barns - return crossSection >= lowerBound && crossSection <= upperBound; - } - - /// Determines if the cross section is typical for high-energy particle physics. - /// True if the cross section is less than 1 millibarn. - public bool IsHighEnergyScale() - { - T crossSection = In(Units.Barn); - T threshold = T.CreateChecked(0.001); // 1 millibarn - return crossSection < threshold; - } -} diff --git a/Semantics.Quantities/Nuclear/RadioactiveActivity.cs b/Semantics.Quantities/Nuclear/RadioactiveActivity.cs deleted file mode 100644 index 946114d..0000000 --- a/Semantics.Quantities/Nuclear/RadioactiveActivity.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents radioactive activity with a specific unit of measurement. -/// Radioactive activity is the number of radioactive decays per unit time. -/// It is measured in becquerels (Bq) in the SI system. -/// -/// The numeric type for the radioactive activity value. -public sealed record RadioactiveActivity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of radioactive activity [T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.RadioactiveActivity; - - /// Initializes a new instance of the class. - public RadioactiveActivity() : base() { } - - /// Creates a new radioactive activity from a value in becquerels. - /// The radioactive activity in becquerels. - /// A new RadioactiveActivity instance. - public static RadioactiveActivity FromBecquerels(T becquerels) => Create(becquerels); - - /// Creates a new radioactive activity from a value in curies. - /// The radioactive activity in curies. - /// A new RadioactiveActivity instance. - public static RadioactiveActivity FromCuries(T curies) - { - T becquerels = curies * T.CreateChecked(3.7e10); - return Create(becquerels); - } - - /// Creates a new radioactive activity from a value in kilobecquerels. - /// The radioactive activity in kilobecquerels. - /// A new RadioactiveActivity instance. - public static RadioactiveActivity FromKilobecquerels(T kilobecquerels) - { - T becquerels = kilobecquerels * T.CreateChecked(1000); - return Create(becquerels); - } - - /// Creates a new radioactive activity from a value in megabecquerels. - /// The radioactive activity in megabecquerels. - /// A new RadioactiveActivity instance. - public static RadioactiveActivity FromMegabecquerels(T megabecquerels) - { - T becquerels = megabecquerels * T.CreateChecked(1e6); - return Create(becquerels); - } - - /// Creates a new radioactive activity from a value in millicuries. - /// The radioactive activity in millicuries. - /// A new RadioactiveActivity instance. - public static RadioactiveActivity FromMillicuries(T millicuries) - { - T becquerels = millicuries * T.CreateChecked(3.7e7); - return Create(becquerels); - } - - /// Calculates the number of atoms from activity and decay constant. - /// The decay constant (λ) in s⁻¹. - /// The number of radioactive atoms. - /// - /// Uses the relationship: A = λN - /// where A is activity, λ is decay constant, and N is number of atoms. - /// - public T CalculateNumberOfAtoms(T decayConstant) - { - if (decayConstant == T.Zero) - { - throw new ArgumentException("Decay constant cannot be zero.", nameof(decayConstant)); - } - - T activity = In(Units.Becquerel); - return activity / decayConstant; - } - - /// Calculates the half-life from activity and initial activity. - /// The initial activity. - /// The time elapsed since initial measurement. - /// The half-life. - /// - /// Uses the exponential decay law: A(t) = A₀ × e^(-λt) - /// Solving for half-life: t₁/₂ = ln(2) / λ - /// - public Time CalculateHalfLife(RadioactiveActivity initialActivity, Time timeElapsed) - { - Ensure.NotNull(initialActivity); - Ensure.NotNull(timeElapsed); - - T currentActivity = In(Units.Becquerel); - T initialActivityValue = initialActivity.In(Units.Becquerel); - T timeSeconds = timeElapsed.In(Units.Second); - - if (currentActivity >= initialActivityValue) - { - throw new ArgumentException("Current activity must be less than initial activity for decay calculation."); - } - - T ratio = currentActivity / initialActivityValue; - T decayConstant = -T.CreateChecked(Math.Log(double.CreateChecked(ratio))) / timeSeconds; - T halfLife = T.CreateChecked(Math.Log(2.0)) / decayConstant; - - return Time.Create(halfLife); - } - - /// Calculates the activity after a given time period. - /// The half-life of the radioactive material. - /// The time elapsed. - /// The activity after the specified time. - /// - /// Uses the exponential decay law: A(t) = A₀ × (1/2)^(t/t₁/₂) - /// where A(t) is activity at time t, A₀ is initial activity, and t₁/₂ is half-life. - /// - public RadioactiveActivity CalculateActivityAfterTime(Time halfLife, Time timeElapsed) - { - Ensure.NotNull(halfLife); - Ensure.NotNull(timeElapsed); - - T currentActivity = In(Units.Becquerel); - T halfLifeSeconds = halfLife.In(Units.Second); - T timeSeconds = timeElapsed.In(Units.Second); - - T exponent = timeSeconds / halfLifeSeconds; - T decayFactor = T.CreateChecked(Math.Pow(0.5, double.CreateChecked(exponent))); - T futureActivity = currentActivity * decayFactor; - - return Create(futureActivity); - } -} diff --git a/Semantics.Quantities/Optical/Illuminance.cs b/Semantics.Quantities/Optical/Illuminance.cs deleted file mode 100644 index 7ba4f5c..0000000 --- a/Semantics.Quantities/Optical/Illuminance.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents illuminance with a specific unit of measurement. -/// Illuminance is the luminous flux incident on a surface per unit area. -/// It is measured in lux (lx). -/// -/// The numeric type for the illuminance value. -public sealed record Illuminance : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of illuminance [J L⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.Illuminance; - - /// Initializes a new instance of the class. - public Illuminance() : base() { } - - /// Creates a new illuminance from a value in lux. - /// The illuminance in lux. - /// A new Illuminance instance. - public static Illuminance FromLux(T lux) => Create(lux); - - /// Creates a new illuminance from a value in foot-candles. - /// The illuminance in foot-candles. - /// A new Illuminance instance. - public static Illuminance FromFootCandles(T footCandles) - { - T lux = footCandles * PhysicalConstants.Generic.FootCandlesToLux(); - return Create(lux); - } - - /// Creates a new illuminance from a value in millilux. - /// The illuminance in millilux. - /// A new Illuminance instance. - public static Illuminance FromMillilux(T millilux) - { - T lux = millilux / T.CreateChecked(1000); - return Create(lux); - } - - /// Creates a new illuminance from a value in kilolux. - /// The illuminance in kilolux. - /// A new Illuminance instance. - public static Illuminance FromKilolux(T kilolux) - { - T lux = kilolux * T.CreateChecked(1000); - return Create(lux); - } - - /// Calculates luminous flux from illuminance and area. - /// The illuminated area. - /// The luminous flux. - /// - /// Uses the relationship: Φ = E × A - /// where Φ is luminous flux, E is illuminance, and A is area. - /// - public LuminousFlux CalculateLuminousFlux(Area area) - { - Ensure.NotNull(area); - - T illuminance = In(Units.Lux); - T areaSquareMeters = area.In(Units.SquareMeter); - T flux = illuminance * areaSquareMeters; - return LuminousFlux.Create(flux); - } - - /// Calculates the distance from a point source given luminous intensity. - /// The luminous intensity of the source. - /// The distance from the source. - /// - /// Uses the inverse square law: r = √(I / E) - /// where r is distance, I is luminous intensity, and E is illuminance. - /// - public Length CalculateDistanceFromSource(LuminousIntensity luminousIntensity) - { - Ensure.NotNull(luminousIntensity); - - T illuminance = In(Units.Lux); - T intensity = luminousIntensity.In(Units.Candela); - T distance = T.CreateChecked(Math.Sqrt(double.CreateChecked(intensity / illuminance))); - return Length.Create(distance); - } -} diff --git a/Semantics.Quantities/Optical/Luminance.cs b/Semantics.Quantities/Optical/Luminance.cs deleted file mode 100644 index 5aec829..0000000 --- a/Semantics.Quantities/Optical/Luminance.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents luminance with a specific unit of measurement. -/// Luminance is the luminous intensity per unit projected area in a given direction. -/// It is measured in candela per square meter (cd/m²). -/// -/// The numeric type for the luminance value. -public sealed record Luminance : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of luminance [J L⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.Luminance; - - /// Initializes a new instance of the class. - public Luminance() : base() { } - - /// Creates a new luminance from a value in candela per square meter. - /// The luminance in cd/m². - /// A new Luminance instance. - public static Luminance FromCandelaPerSquareMeter(T candelaPerSquareMeter) => Create(candelaPerSquareMeter); - - /// Creates a new luminance from a value in nits (same as cd/m²). - /// The luminance in nits. - /// A new Luminance instance. - public static Luminance FromNits(T nits) => Create(nits); - - /// Creates a new luminance from a value in foot-lamberts. - /// The luminance in foot-lamberts. - /// A new Luminance instance. - public static Luminance FromFootLamberts(T footLamberts) - { - T candelaPerSquareMeter = footLamberts * PhysicalConstants.Generic.FootLambertsToCandelasPerSquareMeter(); - return Create(candelaPerSquareMeter); - } - - /// Creates a new luminance from a value in stilbs. - /// The luminance in stilbs. - /// A new Luminance instance. - public static Luminance FromStilbs(T stilbs) - { - T candelaPerSquareMeter = stilbs * T.CreateChecked(10000); - return Create(candelaPerSquareMeter); - } - - /// Calculates luminous intensity from luminance and projected area. - /// The projected area in the viewing direction. - /// The luminous intensity. - /// - /// Uses the relationship: I = L × A_proj - /// where I is luminous intensity, L is luminance, and A_proj is projected area. - /// - public LuminousIntensity CalculateLuminousIntensity(Area projectedArea) - { - Ensure.NotNull(projectedArea); - - T luminance = In(Units.CandelaPerSquareMeter); - T areaSquareMeters = projectedArea.In(Units.SquareMeter); - T intensity = luminance * areaSquareMeters; - return LuminousIntensity.Create(intensity); - } - - /// Calculates illuminance from luminance assuming a Lambertian surface. - /// The illuminance for a Lambertian surface. - /// - /// For a perfect Lambertian surface: E = π × L - /// where E is illuminance and L is luminance. - /// - public Illuminance CalculateIlluminanceForLambertianSurface() - { - T luminance = In(Units.CandelaPerSquareMeter); - T illuminance = luminance * T.CreateChecked(Math.PI); - return Illuminance.Create(illuminance); - } -} diff --git a/Semantics.Quantities/Optical/LuminousFlux.cs b/Semantics.Quantities/Optical/LuminousFlux.cs deleted file mode 100644 index 01718b8..0000000 --- a/Semantics.Quantities/Optical/LuminousFlux.cs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents luminous flux with a specific unit of measurement. -/// Luminous flux is the measure of the perceived power of light. -/// It is measured in lumens (lm). -/// -/// The numeric type for the luminous flux value. -public sealed record LuminousFlux : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of luminous flux [J]. - public override PhysicalDimension Dimension => PhysicalDimensions.LuminousFlux; - - /// Initializes a new instance of the class. - public LuminousFlux() : base() { } - - /// Creates a new luminous flux from a value in lumens. - /// The luminous flux in lumens. - /// A new LuminousFlux instance. - public static LuminousFlux FromLumens(T lumens) => Create(lumens); - - /// Creates a new luminous flux from a value in millilumens. - /// The luminous flux in millilumens. - /// A new LuminousFlux instance. - public static LuminousFlux FromMillilumens(T millilumens) - { - T lumens = millilumens / T.CreateChecked(1000); - return Create(lumens); - } - - /// Creates a new luminous flux from a value in kilolumens. - /// The luminous flux in kilolumens. - /// A new LuminousFlux instance. - public static LuminousFlux FromKilolumens(T kilolumens) - { - T lumens = kilolumens * T.CreateChecked(1000); - return Create(lumens); - } - - /// Calculates illuminance from luminous flux and area. - /// The area over which the flux is distributed. - /// The illuminance. - /// - /// Uses the relationship: E = Φ / A - /// where E is illuminance, Φ is luminous flux, and A is area. - /// - public Illuminance CalculateIlluminance(Area area) - { - Ensure.NotNull(area); - - T flux = In(Units.Lumen); - T areaSquareMeters = area.In(Units.SquareMeter); - T illuminance = flux / areaSquareMeters; - return Illuminance.Create(illuminance); - } - - /// Calculates luminous efficacy from luminous flux and radiant flux. - /// The radiant flux (power) in watts. - /// The luminous efficacy in lumens per watt. - /// - /// Uses the relationship: η = Φ / P - /// where η is luminous efficacy, Φ is luminous flux, and P is radiant flux. - /// - public T CalculateLuminousEfficacy(Power radiantFlux) - { - Ensure.NotNull(radiantFlux); - - T flux = In(Units.Lumen); - T power = radiantFlux.In(Units.Watt); - return flux / power; - } - - /// - /// Calculates illuminance from luminous flux and area (E = Φ/A). - /// - /// The luminous flux. - /// The area. - /// The resulting illuminance. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Illuminance operator /(LuminousFlux flux, Area area) - { - Ensure.NotNull(flux); - Ensure.NotNull(area); - - T illuminanceValue = flux.Value / area.Value; - - return Illuminance.Create(illuminanceValue); - } -} diff --git a/Semantics.Quantities/Optical/LuminousIntensity.cs b/Semantics.Quantities/Optical/LuminousIntensity.cs deleted file mode 100644 index 721386b..0000000 --- a/Semantics.Quantities/Optical/LuminousIntensity.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents luminous intensity with a specific unit of measurement. -/// Luminous intensity is the luminous flux emitted per unit solid angle in a given direction. -/// It is one of the seven SI base units. -/// -/// The numeric type for the luminous intensity value. -public sealed record LuminousIntensity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of luminous intensity [J]. - public override PhysicalDimension Dimension => PhysicalDimensions.LuminousIntensity; - - /// Initializes a new instance of the class. - public LuminousIntensity() : base() { } - - /// Creates a new luminous intensity from a value in candelas. - /// The luminous intensity in candelas. - /// A new LuminousIntensity instance. - public static LuminousIntensity FromCandelas(T candelas) => Create(candelas); - - /// Creates a new luminous intensity from a value in millicandelas. - /// The luminous intensity in millicandelas. - /// A new LuminousIntensity instance. - public static LuminousIntensity FromMillicandelas(T millicandelas) - { - T candelas = millicandelas / T.CreateChecked(1000); - return Create(candelas); - } - - /// Calculates luminous flux from luminous intensity and solid angle. - /// The solid angle in steradians. - /// The luminous flux. - /// - /// Uses the relationship: Φ = I × Ω - /// where Φ is luminous flux, I is luminous intensity, and Ω is solid angle. - /// - public LuminousFlux CalculateLuminousFlux(T solidAngle) - { - T intensity = In(Units.Candela); - T flux = intensity * solidAngle; - return LuminousFlux.Create(flux); - } - - /// Calculates illuminance at a distance from a point source. - /// The distance from the source. - /// The illuminance at the specified distance. - /// - /// Uses the inverse square law: E = I / r² - /// where E is illuminance, I is luminous intensity, and r is distance. - /// - public Illuminance CalculateIlluminanceAtDistance(Length distance) - { - Ensure.NotNull(distance); - - T intensity = In(Units.Candela); - T distanceMeters = distance.In(Units.Meter); - T illuminance = intensity / (distanceMeters * distanceMeters); - return Illuminance.Create(illuminance); - } -} diff --git a/Semantics.Quantities/Optical/OpticalPower.cs b/Semantics.Quantities/Optical/OpticalPower.cs deleted file mode 100644 index e44d5a2..0000000 --- a/Semantics.Quantities/Optical/OpticalPower.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents optical power with a specific unit of measurement. -/// Optical power is the degree to which a lens, mirror, or other optical system converges or diverges light. -/// It is measured in diopters (D), which is the reciprocal of the focal length in meters. -/// -/// The numeric type for the optical power value. -public sealed record OpticalPower : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of optical power [L⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.OpticalPower; - - /// Initializes a new instance of the class. - public OpticalPower() : base() { } - - /// Creates a new optical power from a value in diopters. - /// The optical power in diopters. - /// A new OpticalPower instance. - public static OpticalPower FromDiopters(T diopters) => Create(diopters); - - /// Creates a new optical power from a focal length. - /// The focal length. - /// A new OpticalPower instance. - /// - /// Uses the relationship: P = 1 / f - /// where P is optical power and f is focal length in meters. - /// - public static OpticalPower FromFocalLength(Length focalLength) - { - Ensure.NotNull(focalLength); - - T focalLengthMeters = focalLength.In(Units.Meter); - if (focalLengthMeters == T.Zero) - { - throw new ArgumentException("Focal length cannot be zero.", nameof(focalLength)); - } - - T diopters = T.One / focalLengthMeters; - return Create(diopters); - } - - /// Creates a new optical power from a focal length in millimeters. - /// The focal length in millimeters. - /// A new OpticalPower instance. - public static OpticalPower FromFocalLengthMillimeters(T focalLengthMm) - { - if (focalLengthMm == T.Zero) - { - throw new ArgumentException("Focal length cannot be zero.", nameof(focalLengthMm)); - } - - T focalLengthMeters = focalLengthMm / T.CreateChecked(1000); - T diopters = T.One / focalLengthMeters; - return Create(diopters); - } - - /// Calculates the focal length from optical power. - /// The focal length. - /// - /// Uses the relationship: f = 1 / P - /// where f is focal length and P is optical power. - /// - public Length CalculateFocalLength() - { - T diopters = In(Units.Diopter); - if (diopters == T.Zero) - { - throw new InvalidOperationException("Cannot calculate focal length for zero optical power."); - } - - T focalLengthMeters = T.One / diopters; - return Length.Create(focalLengthMeters); - } - - /// Calculates the combined optical power of two lenses in contact. - /// The optical power of the second lens. - /// The combined optical power. - /// - /// For thin lenses in contact: P_total = P₁ + P₂ - /// where P_total is the combined power, P₁ and P₂ are individual powers. - /// - public OpticalPower CombineWith(OpticalPower other) - { - Ensure.NotNull(other); - - T power1 = In(Units.Diopter); - T power2 = other.In(Units.Diopter); - T combinedPower = power1 + power2; - return Create(combinedPower); - } - - /// Calculates the combined optical power of two lenses separated by a distance. - /// The optical power of the second lens. - /// The distance between the lenses. - /// The combined optical power. - /// - /// For thin lenses separated by distance d: P_total = P₁ + P₂ - d×P₁×P₂ - /// where d is the separation distance in meters. - /// - public OpticalPower CombineWithSeparation(OpticalPower other, Length separation) - { - Ensure.NotNull(other); - Ensure.NotNull(separation); - - T power1 = In(Units.Diopter); - T power2 = other.In(Units.Diopter); - T separationMeters = separation.In(Units.Meter); - - T combinedPower = power1 + power2 - (separationMeters * power1 * power2); - return Create(combinedPower); - } -} diff --git a/Semantics.Quantities/Optical/RefractiveIndex.cs b/Semantics.Quantities/Optical/RefractiveIndex.cs deleted file mode 100644 index 27470aa..0000000 --- a/Semantics.Quantities/Optical/RefractiveIndex.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents refractive index with a specific unit of measurement. -/// Refractive index is a dimensionless number that describes how fast light travels through a material. -/// It is the ratio of the speed of light in vacuum to the speed of light in the material. -/// -/// The numeric type for the refractive index value. -public sealed record RefractiveIndex : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of refractive index [1] (dimensionless). - public override PhysicalDimension Dimension => PhysicalDimensions.RefractiveIndex; - - /// Initializes a new instance of the class. - public RefractiveIndex() : base() { } - - /// Creates a new refractive index from a dimensionless value. - /// The refractive index value. - /// A new RefractiveIndex instance. - public static RefractiveIndex FromValue(T value) => Create(value); - - /// Creates a refractive index for vacuum (n = 1.0 exactly). - /// A new RefractiveIndex instance for vacuum. - public static RefractiveIndex Vacuum() => Create(T.One); - - /// Creates a refractive index for air at standard conditions (n ≈ 1.000293). - /// A new RefractiveIndex instance for air. - public static RefractiveIndex Air() => Create(T.CreateChecked(1.000293)); - - /// Creates a refractive index for water at 20°C (n ≈ 1.333). - /// A new RefractiveIndex instance for water. - public static RefractiveIndex Water() => Create(T.CreateChecked(1.333)); - - /// Creates a refractive index for crown glass (n ≈ 1.52). - /// A new RefractiveIndex instance for crown glass. - public static RefractiveIndex CrownGlass() => Create(T.CreateChecked(1.52)); - - /// Creates a refractive index for diamond (n ≈ 2.42). - /// A new RefractiveIndex instance for diamond. - public static RefractiveIndex Diamond() => Create(T.CreateChecked(2.42)); - - /// Calculates the critical angle for total internal reflection. - /// The refractive index of the external medium. - /// The critical angle in radians. - /// - /// Uses Snell's law: sin(θc) = n₂/n₁ - /// where θc is the critical angle, n₁ is this medium, and n₂ is the external medium. - /// Only valid when n₁ > n₂. - /// - public T CalculateCriticalAngle(RefractiveIndex externalMedium) - { - Ensure.NotNull(externalMedium); - - T n1 = In(Units.Radian); - T n2 = externalMedium.In(Units.Radian); - - if (n2 >= n1) - { - throw new ArgumentException("Critical angle only exists when the internal medium has a higher refractive index than the external medium."); - } - - T sinCritical = n2 / n1; - return T.CreateChecked(Math.Asin(double.CreateChecked(sinCritical))); - } - - /// Calculates the reflection coefficient at normal incidence (Fresnel reflection). - /// The refractive index of the external medium. - /// The reflection coefficient (fraction of light reflected). - /// - /// Uses the Fresnel equation for normal incidence: R = ((n₁-n₂)/(n₁+n₂))² - /// where R is reflectance, n₁ is this medium, and n₂ is the external medium. - /// - public T CalculateReflectionCoefficient(RefractiveIndex externalMedium) - { - Ensure.NotNull(externalMedium); - - T n1 = In(Units.Radian); - T n2 = externalMedium.In(Units.Radian); - T numerator = n1 - n2; - T denominator = n1 + n2; - return numerator * numerator / (denominator * denominator); - } - - /// Calculates the speed of light in this medium. - /// The speed of light in this medium. - /// - /// Uses the relationship: v = c / n - /// where v is the speed in the medium, c is the speed of light in vacuum, and n is the refractive index. - /// - public Velocity CalculateSpeedOfLight() - { - T n = In(Units.Radian); - T speedOfLightInVacuum = PhysicalConstants.Generic.SpeedOfLight(); - T speedInMedium = speedOfLightInVacuum / n; - return Velocity.Create(speedInMedium); - } -} diff --git a/Semantics.Quantities/PhysicalQuantity.cs b/Semantics.Quantities/PhysicalQuantity.cs new file mode 100644 index 0000000..aed2815 --- /dev/null +++ b/Semantics.Quantities/PhysicalQuantity.cs @@ -0,0 +1,97 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// Base record for all physical quantity types. Inherits arithmetic operators from +/// and adds , +/// , and same-dimension comparison. +/// +/// The derived physical quantity type. +/// The storage type for the quantity value. +public abstract record PhysicalQuantity + : SemanticQuantity + , IPhysicalQuantity + where TSelf : PhysicalQuantity, new() + where T : struct, INumber +{ + /// Gets the value stored in this quantity (in the dimension's SI base unit). + public T Value => Quantity; + + /// Gets whether this quantity satisfies structural physical constraints. + public virtual bool IsPhysicallyValid => !T.IsNaN(Value) && T.IsFinite(Value); + + /// Gets the physical dimension this quantity belongs to. Implemented by generated types. + public abstract DimensionInfo Dimension { get; } + + /// + /// Initializes a new instance of the class. + /// + protected PhysicalQuantity() : base() { } + + /// + /// Compares this quantity to another of the same physical dimension. Throws + /// if dimensions differ — quantities of different + /// dimensions are not ordered. + /// + public int CompareTo(IPhysicalQuantity? other) + { + if (other is null) + { + return 1; + } + + if (!Equals(Dimension, other.Dimension)) + { + throw new ArgumentException( + $"Cannot compare quantity of dimension '{Dimension.Name}' to quantity of dimension '{other.Dimension.Name}'.", + nameof(other)); + } + + return Value.CompareTo(other.Value); + } + + /// + /// Equality across the surface — two quantities + /// are equal iff they share a dimension and a value. Cross-dimension comparisons + /// return false (they don't throw — equality is total). + /// + public virtual bool Equals(IPhysicalQuantity? other) + { + if (other is null) + { + return false; + } + + return Equals(Dimension, other.Dimension) && Value.Equals(other.Value); + } + + /// + /// Determines whether one quantity is less than another of the same type. + /// + public static bool operator <(PhysicalQuantity? left, PhysicalQuantity? right) => + left is null ? right is not null : left.CompareTo(right) < 0; + + /// + /// Determines whether one quantity is less than or equal to another of the same type. + /// + public static bool operator <=(PhysicalQuantity? left, PhysicalQuantity? right) => + left is null || left.CompareTo(right) <= 0; + + /// + /// Determines whether one quantity is greater than another of the same type. + /// + public static bool operator >(PhysicalQuantity? left, PhysicalQuantity? right) => + left is not null && left.CompareTo(right) > 0; + + /// + /// Determines whether one quantity is greater than or equal to another of the same type. + /// + public static bool operator >=(PhysicalQuantity? left, PhysicalQuantity? right) => + left is null ? right is null : left.CompareTo(right) >= 0; +} diff --git a/Semantics.Quantities/Core/SemanticQuantity.cs b/Semantics.Quantities/SemanticQuantity.cs similarity index 99% rename from Semantics.Quantities/Core/SemanticQuantity.cs rename to Semantics.Quantities/SemanticQuantity.cs index 72649d7..4461ec1 100644 --- a/Semantics.Quantities/Core/SemanticQuantity.cs +++ b/Semantics.Quantities/SemanticQuantity.cs @@ -2,7 +2,7 @@ // All rights reserved. // Licensed under the MIT license. -namespace ktsu.Semantics; +namespace ktsu.Semantics.Quantities; using System.Numerics; diff --git a/Semantics.Quantities/Semantics.Quantities.csproj b/Semantics.Quantities/Semantics.Quantities.csproj index 2ae83f6..0deb3f1 100644 --- a/Semantics.Quantities/Semantics.Quantities.csproj +++ b/Semantics.Quantities/Semantics.Quantities.csproj @@ -1,12 +1,27 @@ - - net10.0;net9.0;net8.0;net7.0; + net10.0;net9.0;net8.0 + $(NoWarn);CA1716;CA2225;KTSU0003;IDE0032 + true + Generated - + + + + + + + + + + + + + + - diff --git a/Semantics.Quantities/Thermal/Entropy.cs b/Semantics.Quantities/Thermal/Entropy.cs deleted file mode 100644 index 002a12e..0000000 --- a/Semantics.Quantities/Thermal/Entropy.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents an entropy quantity with compile-time dimensional safety. -/// Entropy is a measure of the disorder or randomness in a thermodynamic system. -/// -public sealed record Entropy : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of entropy [M L² T⁻² Θ⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.Entropy; - - /// - /// Initializes a new instance of the Entropy class. - /// - public Entropy() : base() { } - - /// - /// Creates a new Entropy from a value in joules per kelvin. - /// - /// The entropy value in J/K. - /// A new Entropy instance. - public static Entropy FromJoulesPerKelvin(T joulesPerKelvin) => Create(joulesPerKelvin); - - /// - /// Creates a new Entropy from a value in calories per kelvin. - /// - /// The entropy value in cal/K. - /// A new Entropy instance. - public static Entropy FromCaloriesPerKelvin(T caloriesPerKelvin) => - Create(caloriesPerKelvin * PhysicalConstants.Generic.CalorieToJoule()); - - /// - /// Creates a new Entropy from a value in BTU per Rankine. - /// - /// The entropy value in BTU/°R. - /// A new Entropy instance. - public static Entropy FromBtuPerRankine(T btuPerRankine) => - Create(btuPerRankine * PhysicalConstants.Generic.BtuPerFahrenheitToJoulesPerKelvin()); - - /// Gets the entropy in joules per kelvin. - /// The entropy in J/K. - public T JoulesPerKelvin => Value; - - /// Gets the entropy in calories per kelvin. - /// The entropy in cal/K. - public T CaloriesPerKelvin => Value / PhysicalConstants.Generic.CalorieToJoule(); - - /// Gets the entropy in BTU per Rankine. - /// The entropy in BTU/°R. - public T BtuPerRankine => Value / PhysicalConstants.Generic.BtuPerFahrenheitToJoulesPerKelvin(); - - /// - /// Calculates entropy change using ΔS = Q/T for reversible processes. - /// - /// The heat transferred. - /// The absolute temperature. - /// The entropy change. - public static Entropy CalculateEntropyChange(Heat heat, Temperature temperature) - { - Ensure.NotNull(heat); - Ensure.NotNull(temperature); - return Create(heat.Value / temperature.Value); - } - - /// - /// Calculates the Gibbs free energy change: ΔG = ΔH - T·ΔS. - /// - /// The enthalpy change. - /// The absolute temperature. - /// The Gibbs free energy change. - public Energy CalculateGibbsFreeEnergy(Energy enthalpy, Temperature temperature) - { - Ensure.NotNull(enthalpy); - Ensure.NotNull(temperature); - return Energy.Create(enthalpy.Value - (temperature.Value * Value)); - } -} diff --git a/Semantics.Quantities/Thermal/Heat.cs b/Semantics.Quantities/Thermal/Heat.cs deleted file mode 100644 index 0d86402..0000000 --- a/Semantics.Quantities/Thermal/Heat.cs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a heat (thermal energy) quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Heat : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of heat [M L² T⁻²]. - public override PhysicalDimension Dimension => PhysicalDimensions.Heat; - - /// - /// Initializes a new instance of the class. - /// - public Heat() : base() { } - - /// - /// Creates a new Heat from a value in joules. - /// - /// The value in joules. - /// A new Heat instance. - public static Heat FromJoules(T joules) => Create(joules); - - /// - /// Creates a new Heat from a value in calories. - /// - /// The value in calories. - /// A new Heat instance. - public static Heat FromCalories(T calories) => Create(calories * PhysicalConstants.Generic.CalorieToJoule()); - - /// - /// Creates a new Heat from a value in BTU (British Thermal Units). - /// - /// The value in BTU. - /// A new Heat instance. - public static Heat FromBTU(T btu) => Create(btu * PhysicalConstants.Generic.BtuToJoule()); - - /// - /// Creates a new Heat from a value in kilowatt-hours. - /// - /// The value in kilowatt-hours. - /// A new Heat instance. - public static Heat FromKilowattHours(T kilowattHours) => Create(kilowattHours * PhysicalConstants.Generic.KilowattHourToJoule()); - - /// - /// Converts to joules. - /// - /// The heat in joules. - public T ToJoules() => Value; - - /// - /// Converts to calories. - /// - /// The heat in calories. - public T ToCalories() => Value / PhysicalConstants.Generic.CalorieToJoule(); - - /// - /// Converts to BTU. - /// - /// The heat in BTU. - public T ToBTU() => Value / PhysicalConstants.Generic.BtuToJoule(); - - /// - /// Converts to kilowatt-hours. - /// - /// The heat in kilowatt-hours. - public T ToKilowattHours() => Value / PhysicalConstants.Generic.KilowattHourToJoule(); - - /// - /// Converts Heat to Energy. - /// - /// The equivalent energy. - public Energy ToEnergy() => Energy.Create(Value); - - /// - /// Creates Heat from Energy. - /// - /// The energy to convert. - /// The equivalent heat. - public static Heat FromEnergy(Energy energy) - { - Ensure.NotNull(energy); - return Create(energy.Value); - } - - /// - /// Calculates power from heat and time (P = Q/t). - /// - /// The heat energy. - /// The time duration. - /// The resulting power. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Power operator /(Heat heat, Time time) - { - Ensure.NotNull(heat); - Ensure.NotNull(time); - - T powerValue = heat.Value / time.Value; - - return Power.Create(powerValue); - } - - /// - /// Calculates time from heat and power (t = Q/P). - /// - /// The heat energy. - /// The power. - /// The resulting time duration. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "CA2225:Provide named alternates for operator overloads", Justification = "Physics relationship operators represent fundamental equations, not arithmetic")] - public static Time operator /(Heat heat, Power power) - { - Ensure.NotNull(heat); - Ensure.NotNull(power); - - T timeValue = heat.Value / power.Value; - - return Time.Create(timeValue); - } -} diff --git a/Semantics.Quantities/Thermal/HeatCapacity.cs b/Semantics.Quantities/Thermal/HeatCapacity.cs deleted file mode 100644 index f7fde15..0000000 --- a/Semantics.Quantities/Thermal/HeatCapacity.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a heat capacity quantity with compile-time dimensional safety. -/// Heat capacity is the amount of heat required to change the temperature of an object by one degree. -/// -public sealed record HeatCapacity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of heat capacity [M L² T⁻² Θ⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.HeatCapacity; - - /// - /// Initializes a new instance of the HeatCapacity class. - /// - public HeatCapacity() : base() { } - - /// - /// Creates a new HeatCapacity from a value in joules per kelvin. - /// - /// The heat capacity value in J/K. - /// A new HeatCapacity instance. - public static HeatCapacity FromJoulesPerKelvin(T joulesPerKelvin) => Create(joulesPerKelvin); - - /// - /// Creates a new HeatCapacity from a value in calories per kelvin. - /// - /// The heat capacity value in cal/K. - /// A new HeatCapacity instance. - public static HeatCapacity FromCaloriesPerKelvin(T caloriesPerKelvin) => - Create(caloriesPerKelvin * PhysicalConstants.Generic.CalorieToJoule()); - - /// - /// Creates a new HeatCapacity from a value in BTU per Fahrenheit. - /// - /// The heat capacity value in BTU/°F. - /// A new HeatCapacity instance. - public static HeatCapacity FromBtuPerFahrenheit(T btuPerFahrenheit) => - Create(btuPerFahrenheit * PhysicalConstants.Generic.BtuPerFahrenheitToJoulesPerKelvin()); - - /// Gets the heat capacity in joules per kelvin. - /// The heat capacity in J/K. - public T JoulesPerKelvin => Value; - - /// Gets the heat capacity in calories per kelvin. - /// The heat capacity in cal/K. - public T CaloriesPerKelvin => Value / PhysicalConstants.Generic.CalorieToJoule(); - - /// Gets the heat capacity in BTU per Fahrenheit. - /// The heat capacity in BTU/°F. - public T BtuPerFahrenheit => Value / PhysicalConstants.Generic.BtuPerFahrenheitToJoulesPerKelvin(); - - /// - /// Calculates heat required for temperature change: Q = C·ΔT. - /// - /// The temperature change. - /// The heat required. - public Heat CalculateHeatRequired(Temperature temperatureChange) - { - Ensure.NotNull(temperatureChange); - return Heat.Create(Value * temperatureChange.Value); - } - - /// - /// Calculates temperature change from heat: ΔT = Q/C. - /// - /// The heat added or removed. - /// The temperature change. - public Temperature CalculateTemperatureChange(Heat heat) - { - Ensure.NotNull(heat); - return Temperature.Create(heat.Value / Value); - } - - /// - /// Calculates specific heat capacity: c = C/m. - /// - /// The mass of the object. - /// The specific heat capacity. - public SpecificHeat CalculateSpecificHeat(Mass mass) - { - Ensure.NotNull(mass); - return SpecificHeat.Create(Value / mass.Value); - } -} diff --git a/Semantics.Quantities/Thermal/HeatTransferCoefficient.cs b/Semantics.Quantities/Thermal/HeatTransferCoefficient.cs deleted file mode 100644 index 7d48ba2..0000000 --- a/Semantics.Quantities/Thermal/HeatTransferCoefficient.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a heat transfer coefficient quantity with compile-time dimensional safety. -/// Heat transfer coefficient characterizes convective heat transfer between a surface and a fluid. -/// -public sealed record HeatTransferCoefficient : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of heat transfer coefficient [M T⁻³ Θ⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.HeatTransferCoefficient; - - /// - /// Initializes a new instance of the HeatTransferCoefficient class. - /// - public HeatTransferCoefficient() : base() { } - - /// - /// Creates a new HeatTransferCoefficient from a value in watts per square meter-kelvin. - /// - /// The heat transfer coefficient value in W/(m²·K). - /// A new HeatTransferCoefficient instance. - public static HeatTransferCoefficient FromWattsPerSquareMeterKelvin(T wattsPerSquareMeterKelvin) => Create(wattsPerSquareMeterKelvin); - - /// - /// Creates a new HeatTransferCoefficient from a value in BTU per hour-square foot-Fahrenheit. - /// - /// The heat transfer coefficient value in BTU/(h·ft²·°F). - /// A new HeatTransferCoefficient instance. - public static HeatTransferCoefficient FromBtuPerHourSquareFootFahrenheit(T btuPerHourSquareFootFahrenheit) => - Create(btuPerHourSquareFootFahrenheit * PhysicalConstants.Generic.BtuPerHourSquareFootFahrenheitToWattsPerSquareMeterKelvin()); - - /// - /// Creates a new HeatTransferCoefficient from a value in calories per second-square centimeter-kelvin. - /// - /// The heat transfer coefficient value in cal/(s·cm²·K). - /// A new HeatTransferCoefficient instance. - public static HeatTransferCoefficient FromCaloriesPerSecondSquareCentimeterKelvin(T caloriesPerSecondSquareCentimeterKelvin) => - Create(caloriesPerSecondSquareCentimeterKelvin * T.CreateChecked(41840)); - - /// Gets the heat transfer coefficient in watts per square meter-kelvin. - /// The heat transfer coefficient in W/(m²·K). - public T WattsPerSquareMeterKelvin => Value; - - /// Gets the heat transfer coefficient in BTU per hour-square foot-Fahrenheit. - /// The heat transfer coefficient in BTU/(h·ft²·°F). - public T BtuPerHourSquareFootFahrenheit => Value / PhysicalConstants.Generic.BtuPerHourSquareFootFahrenheitToWattsPerSquareMeterKelvin(); - - /// Gets the heat transfer coefficient in calories per second-square centimeter-kelvin. - /// The heat transfer coefficient in cal/(s·cm²·K). - public T CaloriesPerSecondSquareCentimeterKelvin => Value / T.CreateChecked(41840); - - /// - /// Calculates convective heat transfer rate using Newton's law of cooling: Q̇ = h·A·ΔT. - /// - /// The heat transfer surface area. - /// The temperature difference between surface and fluid. - /// The heat transfer rate. - public Power CalculateHeatTransferRate(Area area, Temperature temperatureDifference) - { - Ensure.NotNull(area); - Ensure.NotNull(temperatureDifference); - return Power.Create(Value * area.Value * temperatureDifference.Value); - } - - /// - /// Calculates convective thermal resistance: R = 1/(h·A). - /// - /// The heat transfer surface area. - /// The convective thermal resistance. - public ThermalResistance CalculateThermalResistance(Area area) - { - Ensure.NotNull(area); - return ThermalResistance.Create(T.One / (Value * area.Value)); - } - - /// - /// Typical heat transfer coefficient ranges for reference. - /// - public static class TypicalValues - { - /// Natural convection in air: 5-25 W/(m²·K) - public static HeatTransferCoefficient NaturalConvectionAir => FromWattsPerSquareMeterKelvin(T.CreateChecked(15)); - - /// Forced convection in air: 25-250 W/(m²·K) - public static HeatTransferCoefficient ForcedConvectionAir => FromWattsPerSquareMeterKelvin(T.CreateChecked(100)); - - /// Natural convection in water: 500-1000 W/(m²·K) - public static HeatTransferCoefficient NaturalConvectionWater => FromWattsPerSquareMeterKelvin(T.CreateChecked(750)); - - /// Forced convection in water: 1000-15000 W/(m²·K) - public static HeatTransferCoefficient ForcedConvectionWater => FromWattsPerSquareMeterKelvin(T.CreateChecked(5000)); - - /// Boiling water: 2500-35000 W/(m²·K) - public static HeatTransferCoefficient BoilingWater => FromWattsPerSquareMeterKelvin(T.CreateChecked(15000)); - - /// Condensing steam: 5000-100000 W/(m²·K) - public static HeatTransferCoefficient CondensingSteam => FromWattsPerSquareMeterKelvin(T.CreateChecked(25000)); - } -} diff --git a/Semantics.Quantities/Thermal/SpecificHeat.cs b/Semantics.Quantities/Thermal/SpecificHeat.cs deleted file mode 100644 index 69a2e50..0000000 --- a/Semantics.Quantities/Thermal/SpecificHeat.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a specific heat capacity quantity with compile-time dimensional safety. -/// Specific heat is the amount of heat required to change the temperature of one unit mass by one degree. -/// -public sealed record SpecificHeat : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of specific heat [L² T⁻² Θ⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.SpecificHeat; - - /// - /// Initializes a new instance of the SpecificHeat class. - /// - public SpecificHeat() : base() { } - - /// - /// Creates a new SpecificHeat from a value in joules per kilogram-kelvin. - /// - /// The specific heat value in J/(kg·K). - /// A new SpecificHeat instance. - public static SpecificHeat FromJoulesPerKilogramKelvin(T joulesPerKilogramKelvin) => Create(joulesPerKilogramKelvin); - - /// - /// Creates a new SpecificHeat from a value in calories per gram-kelvin. - /// - /// The specific heat value in cal/(g·K). - /// A new SpecificHeat instance. - public static SpecificHeat FromCaloriesPerGramKelvin(T caloriesPerGramKelvin) => - Create(caloriesPerGramKelvin * T.CreateChecked(4184)); - - /// - /// Creates a new SpecificHeat from a value in BTU per pound-Fahrenheit. - /// - /// The specific heat value in BTU/(lb·°F). - /// A new SpecificHeat instance. - public static SpecificHeat FromBtuPerPoundFahrenheit(T btuPerPoundFahrenheit) => - Create(btuPerPoundFahrenheit * PhysicalConstants.Generic.BtuPerPoundFahrenheitToJoulesPerKilogramKelvin()); - - /// Gets the specific heat in joules per kilogram-kelvin. - /// The specific heat in J/(kg·K). - public T JoulesPerKilogramKelvin => Value; - - /// Gets the specific heat in calories per gram-kelvin. - /// The specific heat in cal/(g·K). - public T CaloriesPerGramKelvin => Value / T.CreateChecked(4184); - - /// Gets the specific heat in BTU per pound-Fahrenheit. - /// The specific heat in BTU/(lb·°F). - public T BtuPerPoundFahrenheit => Value / PhysicalConstants.Generic.BtuPerPoundFahrenheitToJoulesPerKilogramKelvin(); - - /// - /// Calculates heat required for temperature change: Q = m·c·ΔT. - /// - /// The mass of the substance. - /// The temperature change. - /// The heat required. - public Heat CalculateHeatRequired(Mass mass, Temperature temperatureChange) - { - Ensure.NotNull(mass); - Ensure.NotNull(temperatureChange); - return Heat.Create(mass.Value * Value * temperatureChange.Value); - } - - /// - /// Calculates temperature change from heat: ΔT = Q/(m·c). - /// - /// The heat added or removed. - /// The mass of the substance. - /// The temperature change. - public Temperature CalculateTemperatureChange(Heat heat, Mass mass) - { - Ensure.NotNull(heat); - Ensure.NotNull(mass); - return Temperature.Create(heat.Value / (mass.Value * Value)); - } - - /// - /// Calculates total heat capacity: C = m·c. - /// - /// The mass of the object. - /// The total heat capacity. - public HeatCapacity CalculateHeatCapacity(Mass mass) - { - Ensure.NotNull(mass); - return HeatCapacity.Create(mass.Value * Value); - } - - /// - /// Common specific heat values for reference (at room temperature). - /// - public static class CommonValues - { - /// Water: 4184 J/(kg·K) - public static SpecificHeat Water => FromJoulesPerKilogramKelvin(T.CreateChecked(4184)); - - /// Ice: 2090 J/(kg·K) - public static SpecificHeat Ice => FromJoulesPerKilogramKelvin(T.CreateChecked(2090)); - - /// Aluminum: 897 J/(kg·K) - public static SpecificHeat Aluminum => FromJoulesPerKilogramKelvin(T.CreateChecked(897)); - - /// Copper: 385 J/(kg·K) - public static SpecificHeat Copper => FromJoulesPerKilogramKelvin(T.CreateChecked(385)); - - /// Iron: 449 J/(kg·K) - public static SpecificHeat Iron => FromJoulesPerKilogramKelvin(T.CreateChecked(449)); - - /// Air: 1005 J/(kg·K) - public static SpecificHeat Air => FromJoulesPerKilogramKelvin(T.CreateChecked(1005)); - } -} diff --git a/Semantics.Quantities/Thermal/Temperature.cs b/Semantics.Quantities/Thermal/Temperature.cs deleted file mode 100644 index d23fb22..0000000 --- a/Semantics.Quantities/Thermal/Temperature.cs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a temperature quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record Temperature : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of temperature [Θ]. - public override PhysicalDimension Dimension => PhysicalDimensions.Temperature; - - /// - /// Initializes a new instance of the class. - /// - public Temperature() : base() { } - - /// - /// Creates a new instance with the specified value. - /// - /// The value for the quantity. - /// A new instance of the quantity. - /// Thrown when the temperature is below absolute zero (0 K). - public static new Temperature Create(T value) - { - if (T.IsNegative(value)) - { - throw new ArgumentException("Temperature cannot be below absolute zero (0 K).", nameof(value)); - } - - return new Temperature() with { Quantity = value }; - } - - /// - /// Creates a new Temperature from a value in Kelvin. - /// - /// The value in Kelvin. - /// A new Temperature instance. - public static Temperature FromKelvin(T kelvin) => Create(kelvin); - - /// - /// Creates a new Temperature from a value in Celsius. - /// - /// The value in Celsius. - /// A new Temperature instance. - public static Temperature FromCelsius(T celsius) => Create(celsius + PhysicalConstants.Generic.AbsoluteZeroInCelsius()); - - /// - /// Creates a new Temperature from a value in Fahrenheit. - /// - /// The value in Fahrenheit. - /// A new Temperature instance. - public static Temperature FromFahrenheit(T fahrenheit) => Create(((fahrenheit - PhysicalConstants.Generic.FahrenheitOffset()) * PhysicalConstants.Generic.FahrenheitToCelsiusSlope()) + PhysicalConstants.Generic.AbsoluteZeroInCelsius()); - - /// - /// Creates a new Temperature from a value in Rankine. - /// - /// The value in Rankine. - /// A new Temperature instance. - public static Temperature FromRankine(T rankine) => Create(rankine * PhysicalConstants.Generic.FahrenheitToCelsiusSlope()); - - /// - /// Converts to Kelvin. - /// - /// The temperature in Kelvin. - public T ToKelvin() => Value; - - /// - /// Converts to Celsius. - /// - /// The temperature in Celsius. - public T ToCelsius() => Value - PhysicalConstants.Generic.AbsoluteZeroInCelsius(); - - /// - /// Converts to Fahrenheit. - /// - /// The temperature in Fahrenheit. - public T ToFahrenheit() => ((Value - PhysicalConstants.Generic.AbsoluteZeroInCelsius()) * PhysicalConstants.Generic.CelsiusToFahrenheitSlope()) + PhysicalConstants.Generic.FahrenheitOffset(); - - /// - /// Converts to Rankine. - /// - /// The temperature in Rankine. - public T ToRankine() => Value * PhysicalConstants.Generic.CelsiusToFahrenheitSlope(); - - /// - /// Calculates absolute zero in this temperature scale. - /// - /// Absolute zero temperature. - public static Temperature AbsoluteZero => Create(T.Zero); - - /// - /// Calculates water freezing point (273.15 K, 0°C). - /// - /// Water freezing point. - public static Temperature WaterFreezingPoint => Create(PhysicalConstants.Generic.StandardTemperature()); - - /// - /// Calculates water boiling point (373.15 K, 100°C). - /// - /// Water boiling point. - public static Temperature WaterBoilingPoint => Create(PhysicalConstants.Generic.WaterBoilingPoint()); -} diff --git a/Semantics.Quantities/Thermal/ThermalConductivity.cs b/Semantics.Quantities/Thermal/ThermalConductivity.cs deleted file mode 100644 index 8d99fc2..0000000 --- a/Semantics.Quantities/Thermal/ThermalConductivity.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a thermal conductivity quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record ThermalConductivity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of thermalconductivity [M L T⁻³ Θ⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.ThermalConductivity; - - /// - /// Initializes a new instance of the class. - /// - public ThermalConductivity() : base() { } - - /// - /// Creates a new ThermalConductivity from a value in watts per meter-kelvin. - /// - /// The value in W/(m·K). - /// A new ThermalConductivity instance. - public static ThermalConductivity FromWattsPerMeterKelvin(T wattsPerMeterKelvin) => Create(wattsPerMeterKelvin); - - /// - /// Creates a new ThermalConductivity from a value in BTU per hour-foot-Fahrenheit. - /// - /// The value in BTU/(h·ft·°F). - /// A new ThermalConductivity instance. - public static ThermalConductivity FromBtuPerHourFootFahrenheit(T btuPerHourFootFahrenheit) => - Create(btuPerHourFootFahrenheit * PhysicalConstants.Generic.BtuPerHourFootFahrenheitToWattsPerMeterKelvin()); - - /// - /// Converts to watts per meter-kelvin. - /// - /// The thermal conductivity in W/(m·K). - public T ToWattsPerMeterKelvin() => Value; - - /// - /// Converts to BTU per hour-foot-Fahrenheit. - /// - /// The thermal conductivity in BTU/(h·ft·°F). - public T ToBtuPerHourFootFahrenheit() => Value / PhysicalConstants.Generic.BtuPerHourFootFahrenheitToWattsPerMeterKelvin(); - - /// - /// Calculates heat flow rate using Fourier's law: Q̇ = k·A·ΔT/L. - /// - /// The cross-sectional area. - /// The temperature difference. - /// The length/thickness. - /// The heat flow rate. - public Power CalculateHeatFlow(Area area, Temperature temperatureDifference, Length length) - { - Ensure.NotNull(area); - Ensure.NotNull(temperatureDifference); - Ensure.NotNull(length); - return Power.Create(Value * area.Value * temperatureDifference.Value / length.Value); - } - - /// - /// Calculates thermal resistance: R = L/(k·A). - /// - /// The length/thickness. - /// The cross-sectional area. - /// The thermal resistance. - public ThermalResistance CalculateThermalResistance(Length length, Area area) - { - Ensure.NotNull(length); - Ensure.NotNull(area); - return ThermalResistance.Create(length.Value / (Value * area.Value)); - } -} diff --git a/Semantics.Quantities/Thermal/ThermalDiffusivity.cs b/Semantics.Quantities/Thermal/ThermalDiffusivity.cs deleted file mode 100644 index 6adc712..0000000 --- a/Semantics.Quantities/Thermal/ThermalDiffusivity.cs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a thermal diffusivity quantity with compile-time dimensional safety. -/// Thermal diffusivity measures how quickly heat diffuses through a material. -/// -public sealed record ThermalDiffusivity : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of thermal diffusivity [L² T⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.ThermalDiffusivity; - - /// - /// Initializes a new instance of the ThermalDiffusivity class. - /// - public ThermalDiffusivity() : base() { } - - /// - /// Creates a new ThermalDiffusivity from a value in square meters per second. - /// - /// The thermal diffusivity value in m²/s. - /// A new ThermalDiffusivity instance. - public static ThermalDiffusivity FromSquareMetersPerSecond(T squareMetersPerSecond) => Create(squareMetersPerSecond); - - /// - /// Creates a new ThermalDiffusivity from a value in square feet per hour. - /// - /// The thermal diffusivity value in ft²/h. - /// A new ThermalDiffusivity instance. - public static ThermalDiffusivity FromSquareFeetPerHour(T squareFeetPerHour) => - Create(squareFeetPerHour * T.CreateChecked(2.581e-5)); - - /// - /// Creates a new ThermalDiffusivity from a value in square centimeters per second. - /// - /// The thermal diffusivity value in cm²/s. - /// A new ThermalDiffusivity instance. - public static ThermalDiffusivity FromSquareCentimetersPerSecond(T squareCentimetersPerSecond) => - Create(squareCentimetersPerSecond * T.CreateChecked(1e-4)); - - /// Gets the thermal diffusivity in square meters per second. - /// The thermal diffusivity in m²/s. - public T SquareMetersPerSecond => Value; - - /// Gets the thermal diffusivity in square feet per hour. - /// The thermal diffusivity in ft²/h. - public T SquareFeetPerHour => Value / T.CreateChecked(2.581e-5); - - /// Gets the thermal diffusivity in square centimeters per second. - /// The thermal diffusivity in cm²/s. - public T SquareCentimetersPerSecond => Value / T.CreateChecked(1e-4); - - /// - /// Calculates thermal diffusivity from material properties: α = k/(ρ·cp). - /// - /// The thermal conductivity. - /// The material density. - /// The specific heat capacity. - /// The thermal diffusivity. - public static ThermalDiffusivity FromMaterialProperties( - ThermalConductivity thermalConductivity, - Density density, - SpecificHeat specificHeat) - { - Ensure.NotNull(thermalConductivity); - Ensure.NotNull(density); - Ensure.NotNull(specificHeat); - return Create(thermalConductivity.Value / (density.Value * specificHeat.Value)); - } - - /// - /// Calculates the characteristic time for heat diffusion: t = L²/α. - /// - /// The characteristic length scale. - /// The characteristic diffusion time. - public Time CalculateDiffusionTime(Length characteristicLength) - { - Ensure.NotNull(characteristicLength); - T lengthSquared = characteristicLength.Value * characteristicLength.Value; - return Time.Create(lengthSquared / Value); - } - - /// - /// Calculates the thermal penetration depth: δ = √(α·t). - /// - /// The time duration. - /// The thermal penetration depth. - public Length CalculatePenetrationDepth(Time time) - { - Ensure.NotNull(time); - return Length.Create(T.CreateChecked(Math.Sqrt(double.CreateChecked(Value * time.Value)))); - } - - /// - /// Calculates thermal conductivity from diffusivity: k = α·ρ·cp. - /// - /// The material density. - /// The specific heat capacity. - /// The thermal conductivity. - public ThermalConductivity CalculateThermalConductivity(Density density, SpecificHeat specificHeat) - { - Ensure.NotNull(density); - Ensure.NotNull(specificHeat); - return ThermalConductivity.Create(Value * density.Value * specificHeat.Value); - } - - /// - /// Common thermal diffusivity values for reference (at room temperature). - /// - public static class CommonValues - { - /// Water: 1.43 × 10⁻⁷ m²/s - public static ThermalDiffusivity Water => FromSquareMetersPerSecond(T.CreateChecked(1.43e-7)); - - /// Air: 2.2 × 10⁻⁵ m²/s - public static ThermalDiffusivity Air => FromSquareMetersPerSecond(T.CreateChecked(2.2e-5)); - - /// Aluminum: 9.7 × 10⁻⁵ m²/s - public static ThermalDiffusivity Aluminum => FromSquareMetersPerSecond(T.CreateChecked(9.7e-5)); - - /// Steel: 1.2 × 10⁻⁵ m²/s - public static ThermalDiffusivity Steel => FromSquareMetersPerSecond(T.CreateChecked(1.2e-5)); - - /// Copper: 1.11 × 10⁻⁴ m²/s - public static ThermalDiffusivity Copper => FromSquareMetersPerSecond(T.CreateChecked(1.11e-4)); - - /// Concrete: 5.0 × 10⁻⁷ m²/s - public static ThermalDiffusivity Concrete => FromSquareMetersPerSecond(T.CreateChecked(5.0e-7)); - - /// Glass: 3.4 × 10⁻⁷ m²/s - public static ThermalDiffusivity Glass => FromSquareMetersPerSecond(T.CreateChecked(3.4e-7)); - } -} diff --git a/Semantics.Quantities/Thermal/ThermalExpansion.cs b/Semantics.Quantities/Thermal/ThermalExpansion.cs deleted file mode 100644 index 6358381..0000000 --- a/Semantics.Quantities/Thermal/ThermalExpansion.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a thermal expansion coefficient quantity with compile-time dimensional safety. -/// Thermal expansion coefficient describes how much a material expands per unit temperature change. -/// -public sealed record ThermalExpansion : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of thermal expansion coefficient [Θ⁻¹]. - public override PhysicalDimension Dimension => PhysicalDimensions.ThermalExpansion; - - /// - /// Initializes a new instance of the ThermalExpansion class. - /// - public ThermalExpansion() : base() { } - - /// - /// Creates a new ThermalExpansion from a value in per kelvin. - /// - /// The thermal expansion coefficient value in K⁻¹. - /// A new ThermalExpansion instance. - public static ThermalExpansion FromPerKelvin(T perKelvin) => Create(perKelvin); - - /// - /// Creates a new ThermalExpansion from a value in per Celsius. - /// - /// The thermal expansion coefficient value in °C⁻¹. - /// A new ThermalExpansion instance. - public static ThermalExpansion FromPerCelsius(T perCelsius) => Create(perCelsius); - - /// - /// Creates a new ThermalExpansion from a value in per Fahrenheit. - /// - /// The thermal expansion coefficient value in °F⁻¹. - /// A new ThermalExpansion instance. - public static ThermalExpansion FromPerFahrenheit(T perFahrenheit) => - Create(perFahrenheit * PhysicalConstants.Generic.CelsiusToFahrenheitSlope()); - - /// Gets the thermal expansion coefficient in per kelvin. - /// The thermal expansion coefficient in K⁻¹. - public T PerKelvin => Value; - - /// Gets the thermal expansion coefficient in per Celsius. - /// The thermal expansion coefficient in °C⁻¹. - public T PerCelsius => Value; - - /// Gets the thermal expansion coefficient in per Fahrenheit. - /// The thermal expansion coefficient in °F⁻¹. - public T PerFahrenheit => Value / PhysicalConstants.Generic.CelsiusToFahrenheitSlope(); - - /// - /// Calculates linear expansion: ΔL = α·L₀·ΔT. - /// - /// The original length. - /// The temperature change. - /// The change in length. - public Length CalculateLinearExpansion(Length originalLength, Temperature temperatureChange) - { - Ensure.NotNull(originalLength); - Ensure.NotNull(temperatureChange); - return Length.Create(Value * originalLength.Value * temperatureChange.Value); - } - - /// - /// Calculates final length after thermal expansion: L = L₀·(1 + α·ΔT). - /// - /// The original length. - /// The temperature change. - /// The final length. - public Length CalculateFinalLength(Length originalLength, Temperature temperatureChange) - { - Ensure.NotNull(originalLength); - Ensure.NotNull(temperatureChange); - T expansionFactor = T.One + (Value * temperatureChange.Value); - return Length.Create(originalLength.Value * expansionFactor); - } - - /// - /// Calculates area expansion coefficient (approximately 2α for small expansions). - /// - /// The area expansion coefficient. - public ThermalExpansion CalculateAreaExpansionCoefficient() => Create(Value * T.CreateChecked(2)); - - /// - /// Calculates volume expansion coefficient (approximately 3α for small expansions). - /// - /// The volume expansion coefficient. - public ThermalExpansion CalculateVolumeExpansionCoefficient() => Create(Value * T.CreateChecked(3)); - - /// - /// Common thermal expansion coefficients for reference (at room temperature). - /// - public static class CommonValues - { - /// Aluminum: 23.1 × 10⁻⁶ K⁻¹ - public static ThermalExpansion Aluminum => FromPerKelvin(T.CreateChecked(23.1e-6)); - - /// Steel: 11.0 × 10⁻⁶ K⁻¹ - public static ThermalExpansion Steel => FromPerKelvin(T.CreateChecked(11.0e-6)); - - /// Copper: 16.5 × 10⁻⁶ K⁻¹ - public static ThermalExpansion Copper => FromPerKelvin(T.CreateChecked(16.5e-6)); - - /// Concrete: 10.0 × 10⁻⁶ K⁻¹ - public static ThermalExpansion Concrete => FromPerKelvin(T.CreateChecked(10.0e-6)); - - /// Glass: 9.0 × 10⁻⁶ K⁻¹ - public static ThermalExpansion Glass => FromPerKelvin(T.CreateChecked(9.0e-6)); - - /// PVC: 52.0 × 10⁻⁶ K⁻¹ - public static ThermalExpansion PVC => FromPerKelvin(T.CreateChecked(52.0e-6)); - } -} diff --git a/Semantics.Quantities/Thermal/ThermalResistance.cs b/Semantics.Quantities/Thermal/ThermalResistance.cs deleted file mode 100644 index d8a4ad9..0000000 --- a/Semantics.Quantities/Thermal/ThermalResistance.cs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics; - -using System.Numerics; - -/// -/// Represents a thermal resistance quantity with compile-time dimensional safety. -/// -/// The storage type for the quantity value. -public sealed record ThermalResistance : PhysicalQuantity, T> - where T : struct, INumber -{ - /// Gets the physical dimension of thermalresistance [M⁻¹ L⁻² T³ Θ]. - public override PhysicalDimension Dimension => PhysicalDimensions.ThermalResistance; - - /// - /// Initializes a new instance of the class. - /// - public ThermalResistance() : base() { } - - /// - /// Creates a new ThermalResistance from a value in kelvin per watt. - /// - /// The value in K/W. - /// A new ThermalResistance instance. - public static ThermalResistance FromKelvinPerWatt(T kelvinPerWatt) => Create(kelvinPerWatt); - - /// - /// Creates a new ThermalResistance from a value in Fahrenheit-hour per BTU. - /// - /// The value in °F·h/BTU. - /// A new ThermalResistance instance. - public static ThermalResistance FromFahrenheitHourPerBtu(T fahrenheitHourPerBtu) => - Create(fahrenheitHourPerBtu * PhysicalConstants.Generic.FahrenheitHourPerBtuToKelvinPerWatt()); - - /// - /// Converts to kelvin per watt. - /// - /// The thermal resistance in K/W. - public T ToKelvinPerWatt() => Value; - - /// - /// Converts to Fahrenheit-hour per BTU. - /// - /// The thermal resistance in °F·h/BTU. - public T ToFahrenheitHourPerBtu() => Value / PhysicalConstants.Generic.FahrenheitHourPerBtuToKelvinPerWatt(); - - /// - /// Calculates heat flow rate using thermal resistance: Q̇ = ΔT/R. - /// - /// The temperature difference. - /// The heat flow rate. - public Power CalculateHeatFlow(Temperature temperatureDifference) - { - Ensure.NotNull(temperatureDifference); - return Power.Create(temperatureDifference.Value / Value); - } - - /// - /// Adds thermal resistances in series. - /// - /// The first thermal resistance. - /// The second thermal resistance. - /// The total thermal resistance. - public static ThermalResistance operator +(ThermalResistance left, ThermalResistance right) - { - Ensure.NotNull(left); - Ensure.NotNull(right); - return Create(left.Value + right.Value); - } - - /// - /// Adds thermal resistances in series (friendly alternate for operator +). - /// - /// The other thermal resistance. - /// The total thermal resistance. - public ThermalResistance Add(ThermalResistance other) - { - Ensure.NotNull(other); - return this + other; - } - - /// - /// Calculates parallel thermal resistance: 1/R_total = 1/R1 + 1/R2. - /// - /// The other thermal resistance. - /// The parallel thermal resistance. - public ThermalResistance InParallelWith(ThermalResistance other) - { - Ensure.NotNull(other); - return Create(T.CreateChecked(1) / ((T.CreateChecked(1) / Value) + (T.CreateChecked(1) / other.Value))); - } -} diff --git a/Semantics.Quantities/UnitConversionException.cs b/Semantics.Quantities/UnitConversionException.cs new file mode 100644 index 0000000..d93d63c --- /dev/null +++ b/Semantics.Quantities/UnitConversionException.cs @@ -0,0 +1,31 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Quantities; + +using System; + +/// +/// Thrown when a unit conversion cannot be performed — typically because the +/// target unit's dimension does not match the source quantity's dimension. +/// +/// +/// In the typed compile-time path (In(I<Dim>Unit)), +/// dimension mismatches fail at compile time. This exception remains for runtime +/// scenarios where a quantity is converted via the untyped surface. +/// +public sealed class UnitConversionException : ArgumentException +{ + /// Initializes a new instance of the class. + public UnitConversionException() { } + + /// Initializes a new instance of the class with a message. + /// The message that describes the conversion error. + public UnitConversionException(string message) : base(message) { } + + /// Initializes a new instance of the class with a message and inner exception. + /// The message that describes the conversion error. + /// The exception that caused this exception. + public UnitConversionException(string message, Exception inner) : base(message, inner) { } +} diff --git a/Semantics.Quantities/Core/UnitSystem.cs b/Semantics.Quantities/UnitSystem.cs similarity index 96% rename from Semantics.Quantities/Core/UnitSystem.cs rename to Semantics.Quantities/UnitSystem.cs index 12f7b98..713f966 100644 --- a/Semantics.Quantities/Core/UnitSystem.cs +++ b/Semantics.Quantities/UnitSystem.cs @@ -2,7 +2,7 @@ // All rights reserved. // Licensed under the MIT license. -namespace ktsu.Semantics; +namespace ktsu.Semantics.Quantities; /// /// Represents a unit system classification. diff --git a/Semantics.Quantities/Vector0Guards.cs b/Semantics.Quantities/Vector0Guards.cs new file mode 100644 index 0000000..eab9e2b --- /dev/null +++ b/Semantics.Quantities/Vector0Guards.cs @@ -0,0 +1,71 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Quantities; + +using System; +using System.Numerics; + +/// +/// Runtime guards used by generated quantity types +/// to enforce the non-negativity invariant declared in the unified-vector model. +/// +/// +/// Per the locked design decisions in docs/strategy-unified-vector-quantities.md: +/// +/// A Vector0 quantity is always non-negative. Construction with a negative value throws . +/// The conversion from a non-base unit can flip the sign (e.g. -460°F is below absolute zero in Kelvin); the guard runs after conversion to catch that. +/// +/// +public static class Vector0Guards +{ + /// + /// Returns unchanged when it is non-negative; throws + /// otherwise. Used in generated From{Unit} + /// factories to enforce the non-negativity invariant on Vector0 quantities. + /// + /// The numeric storage type. + /// The value (already converted to the SI base unit) to validate. + /// Name of the originating parameter, used for the exception message. + /// The validated, non-negative value. + /// When is negative. + public static T EnsureNonNegative(T value, string paramName) + where T : struct, INumber + { + if (T.Sign(value) < 0) + { + throw new ArgumentException( + $"Magnitude must be non-negative; received {value}.", + paramName); + } + + return value; + } + + /// + /// Returns unchanged when it is strictly positive; throws + /// otherwise. Used in generated From{Unit} + /// factories on V0 overloads that declare physicalConstraints.minExclusive: "0" + /// in dimensions.json (per #51). Examples: Wavelength, Period, + /// HalfLife — quantities for which zero is unphysical, distinct from the V0 + /// default that allows zero. + /// + /// The numeric storage type. + /// The value (already converted to the SI base unit) to validate. + /// Name of the originating parameter, used for the exception message. + /// The validated, strictly-positive value. + /// When is zero or negative. + public static T EnsurePositive(T value, string paramName) + where T : struct, INumber + { + if (T.Sign(value) <= 0) + { + throw new ArgumentException( + $"Value must be strictly positive; received {value}.", + paramName); + } + + return value; + } +} diff --git a/Semantics.SourceGenerators/AnalyzerReleases.Shipped.md b/Semantics.SourceGenerators/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000..c1b674f --- /dev/null +++ b/Semantics.SourceGenerators/AnalyzerReleases.Shipped.md @@ -0,0 +1,2 @@ +; Shipped analyzer releases. +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md diff --git a/Semantics.SourceGenerators/AnalyzerReleases.Unshipped.md b/Semantics.SourceGenerators/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000..63910aa --- /dev/null +++ b/Semantics.SourceGenerators/AnalyzerReleases.Unshipped.md @@ -0,0 +1,12 @@ +; Unshipped analyzer release. +; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------ +SEM001 | Semantics.SourceGenerators | Warning | Reports relationships in dimensions.json that reference unknown dimension names. +SEM002 | Semantics.SourceGenerators | Warning | Reports schema-level validation issues in dimensions.json (missing fields, duplicate type names, etc). +SEM003 | Semantics.SourceGenerators | Warning | Reports a relationship whose explicit `forms` list references a vector form not declared on a participating dimension. +SEM004 | Semantics.SourceGenerators | Warning | Reports a `dimensions.json` `availableUnits` entry that doesn't match any unit declared in `units.json`. +SEM005 | Semantics.SourceGenerators | Warning | Reports schema-level validation issues in logarithmic.json (missing or duplicate scale names, conversions with no linear type). diff --git a/Semantics.SourceGenerators/Generators/ConversionsGenerator.cs b/Semantics.SourceGenerators/Generators/ConversionsGenerator.cs new file mode 100644 index 0000000..acc9457 --- /dev/null +++ b/Semantics.SourceGenerators/Generators/ConversionsGenerator.cs @@ -0,0 +1,79 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators; + +using ktsu.CodeBlocker; +using Microsoft.CodeAnalysis; +using Semantics.SourceGenerators.Models; +using Semantics.SourceGenerators.Templates; + +/// +/// Source generator that creates the ConversionConstants.cs file from JSON metadata. +/// +[Generator] +public class ConversionsGenerator : GeneratorBase +{ + public ConversionsGenerator() : base("conversions.json") { } + + protected override void Generate(SourceProductionContext context, ConversionsMetadata metadata, CodeBlocker codeBlocker) + { + if (metadata.Conversions.Count == 0) + { + return; + } + + SourceFileTemplate sourceFileTemplate = new() + { + FileName = "ConversionConstants.g.cs", + Namespace = "ktsu.Semantics.Quantities.Units", + }; + + ClassTemplate constantsClass = new() + { + Comments = + [ + "/// ", + "/// Conversion constants used by generated unit definitions.", + "/// Values sourced from conversions.json metadata.", + "/// ", + ], + Keywords = + [ + "internal", + "static", + "class", + ], + Name = "ConversionConstants", + }; + + foreach (ConversionCategory category in metadata.Conversions) + { + foreach (ConversionFactor factor in category.Factors) + { + constantsClass.Members.Add(new FieldTemplate() + { + Comments = + [ + $"/// {factor.Description}", + ], + Keywords = + [ + "internal", + "const", + "double", + ], + Name = factor.Name, + DefaultValue = factor.Value, + }); + } + } + + sourceFileTemplate.Classes.Add(constantsClass); + + WriteSourceFileTo(codeBlocker, sourceFileTemplate); + + context.AddSource(sourceFileTemplate.FileName, codeBlocker.ToString()); + } +} diff --git a/Semantics.SourceGenerators/Generators/DimensionsGenerator.cs b/Semantics.SourceGenerators/Generators/DimensionsGenerator.cs new file mode 100644 index 0000000..a3ede5c --- /dev/null +++ b/Semantics.SourceGenerators/Generators/DimensionsGenerator.cs @@ -0,0 +1,154 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators; + +using System.Collections.Generic; +using System.Linq; +using ktsu.CodeBlocker; +using Microsoft.CodeAnalysis; +using Semantics.SourceGenerators.Models; +using Semantics.SourceGenerators.Templates; + +/// +/// Source generator that creates the PhysicalDimensions.cs file from JSON metadata. +/// +[Generator] +public class DimensionsGenerator : GeneratorBase +{ + public DimensionsGenerator() : base("dimensions.json") { } + + protected override void Generate(SourceProductionContext context, DimensionsMetadata metadata, CodeBlocker codeBlocker) + { + if (metadata.PhysicalDimensions == null || metadata.PhysicalDimensions.Count == 0) + { + return; + } + + SourceFileTemplate sourceFileTemplate = new() + { + FileName = "PhysicalDimensions.g.cs", + Namespace = "ktsu.Semantics.Quantities", + Usings = + [ + "System.Collections.Generic", + ], + }; + + // Generate DimensionInfo record + ClassTemplate dimensionInfoRecord = new() + { + Comments = + [ + "/// ", + "/// Dimension information record.", + "/// ", + ], + Keywords = ["public", "record"], + Name = "DimensionInfo(string Name, string Symbol, Dictionary DimensionalFormula, List Quantities)", + }; + sourceFileTemplate.Classes.Add(dimensionInfoRecord); + + // Generate PhysicalDimensions static class + ClassTemplate dimensionsClass = new() + { + Comments = + [ + "/// ", + "/// Static registry of physical dimensions.", + "/// ", + ], + Keywords = ["public", "static", "class"], + Name = "PhysicalDimensions", + }; + + IOrderedEnumerable sortedDimensions = metadata.PhysicalDimensions.OrderBy(d => d.Name); + + foreach (PhysicalDimension dimension in sortedDimensions) + { + string description = $"Physical dimension: {dimension.Name}"; + + // Build dimensional formula initializer + string formulaInit; + if (dimension.DimensionalFormula.Count > 0) + { + IEnumerable entries = dimension.DimensionalFormula.Select(kvp => $"[\"{kvp.Key}\"] = {kvp.Value}"); + formulaInit = $"new Dictionary {{ {string.Join(", ", entries)} }}"; + } + else + { + formulaInit = "new Dictionary()"; + } + + // Collect all type names from vector forms (base types + overloads) + List quantityNames = []; + VectorFormDefinition?[] forms = [dimension.Quantities.Vector0, dimension.Quantities.Vector1, dimension.Quantities.Vector2, dimension.Quantities.Vector3, dimension.Quantities.Vector4]; + foreach (VectorFormDefinition? form in forms) + { + if (form != null) + { + quantityNames.Add(form.Base); + foreach (OverloadDefinition overload in form.Overloads) + { + quantityNames.Add(overload.Name); + } + } + } + + // Build quantities list initializer + string quantitiesInit; + if (quantityNames.Count > 0) + { + IEnumerable names = quantityNames.Select(n => $"\"{n}\""); + quantitiesInit = $"new List {{ {string.Join(", ", names)} }}"; + } + else + { + quantitiesInit = "new List()"; + } + + dimensionsClass.Members.Add(new FieldTemplate() + { + Comments = [$"/// {description}"], + Keywords = ["public", "static", "readonly", "DimensionInfo"], + Name = dimension.Name, + DefaultValue = $"new(\"{dimension.Name}\", \"{dimension.Symbol}\", {formulaInit}, {quantitiesInit})", + }); + } + + // Generate the All property + string allDimensions = string.Join(", ", sortedDimensions.Select(d => d.Name)); + dimensionsClass.Members.Add(new FieldTemplate() + { + Comments = ["/// Gets a frozen collection of all standard physical dimensions."], + Keywords = ["public", "static", "IReadOnlySet"], + Name = "All", + DefaultValue = $"new HashSet([ {allDimensions} ])", + }); + + sourceFileTemplate.Classes.Add(dimensionsClass); + + // Emit per-dimension marker interfaces (I{Dim}Unit : IUnit) so generated + // quantity types can accept dimensionally-compatible units only. + foreach (PhysicalDimension dimension in sortedDimensions) + { + sourceFileTemplate.Classes.Add(new ClassTemplate() + { + Comments = + [ + "/// ", + $"/// Marker interface implemented by every unit of the {dimension.Name} dimension.", + "/// Generated quantities use this to make In(...) dimensionally type-safe at compile time.", + "/// ", + ], + Keywords = ["public", "interface"], + Name = $"I{dimension.Name}Unit", + Interfaces = ["IUnit"], + }); + } + + WriteSourceFileTo(codeBlocker, sourceFileTemplate); + context.AddSource(sourceFileTemplate.FileName, codeBlocker.ToString()); + } +} diff --git a/Semantics.SourceGenerators/Generators/GeneratorBase.cs b/Semantics.SourceGenerators/Generators/GeneratorBase.cs new file mode 100644 index 0000000..e74e16a --- /dev/null +++ b/Semantics.SourceGenerators/Generators/GeneratorBase.cs @@ -0,0 +1,75 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators; + +using System.Linq; +using System.Text.Json; +using ktsu.CodeBlocker; +using Microsoft.CodeAnalysis; +using Semantics.SourceGenerators.Templates; + +public abstract class GeneratorBase(string metadataFilename) : IIncrementalGenerator +{ + public virtual void Initialize(IncrementalGeneratorInitializationContext context) + { + // Find the conversions metadata JSON file + IncrementalValuesProvider metadataFiles = context.AdditionalTextsProvider + .Where(file => file.Path.EndsWith(metadataFilename, System.StringComparison.InvariantCulture)) + .Select((file, cancellationToken) => file.GetText(cancellationToken)?.ToString() ?? "") + .Where(content => !string.IsNullOrEmpty(content)); + + // Generate code from metadata + context.RegisterSourceOutput(metadataFiles, (ctx, jsonContent) => + { + if (string.IsNullOrEmpty(jsonContent)) + { + return; + } + + try + { + JsonSerializerOptions options = new() + { + PropertyNameCaseInsensitive = true + }; + + T metadata = JsonSerializer.Deserialize(jsonContent, options) ?? + throw new JsonException("Failed to deserialize metadata"); + + using CodeBlocker codeBlocker = CodeBlocker.Create(); + Generate(ctx, metadata, codeBlocker); + } + catch (JsonException ex) + { + // Report JSON parsing error + DiagnosticDescriptor descriptor = new( + "CONV001", + "JSON parsing error", + "Failed to parse metadata JSON: {0}", + "SourceGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + ctx.ReportDiagnostic(Diagnostic.Create(descriptor, Location.None, ex.Message)); + } + }); + } + + protected abstract void Generate(SourceProductionContext context, T metadata, CodeBlocker codeBlocker); + protected static void WriteHeaderTo(CodeBlocker codeBlocker) + { + codeBlocker.WriteLine("// Copyright (c) ktsu.dev"); + codeBlocker.WriteLine("// All rights reserved."); + codeBlocker.WriteLine("// Licensed under the MIT license."); + codeBlocker.WriteLine("// "); + codeBlocker.NewLine(); + } + + internal static void WriteSourceFileTo(CodeBlocker codeBlocker, SourceFileTemplate sourceFileTemplate) + { + WriteHeaderTo(codeBlocker); + codeBlocker.AddSourceFile(sourceFileTemplate); + } +} diff --git a/Semantics.SourceGenerators/Generators/LogarithmicScalesGenerator.cs b/Semantics.SourceGenerators/Generators/LogarithmicScalesGenerator.cs new file mode 100644 index 0000000..83ca5a4 --- /dev/null +++ b/Semantics.SourceGenerators/Generators/LogarithmicScalesGenerator.cs @@ -0,0 +1,277 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators; + +using System.Collections.Generic; +using System.Globalization; +using ktsu.CodeBlocker; +using Microsoft.CodeAnalysis; +using Semantics.SourceGenerators.Models; + +/// +/// Source generator that creates logarithmic-scale quantity types (decibel levels, +/// pitch intervals, pH) from logarithmic.json. Logarithmic scales don't obey linear +/// arithmetic, so they are emitted as standalone readonly partial record structs +/// that convert to and from their linear generated counterparts via +/// scale = multiplier · log_base(linear / reference). Bespoke members +/// (named constants, cross-scale conversions) live in hand-written partials. +/// +[Generator] +public class LogarithmicScalesGenerator : GeneratorBase +{ + private static readonly DiagnosticDescriptor InvalidScaleDefinition = new( + id: "SEM005", + title: "logarithmic.json scale definition is invalid", + messageFormat: "logarithmic.json validation issue: {0}", + category: "Semantics.SourceGenerators", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public LogarithmicScalesGenerator() : base("logarithmic.json") { } + + /// + protected override void Generate(SourceProductionContext context, LogarithmicMetadata metadata, CodeBlocker codeBlocker) + { + if (metadata.LogarithmicScales == null || metadata.LogarithmicScales.Count == 0) + { + return; + } + + HashSet seenNames = []; + foreach (LogarithmicScaleDefinition scale in metadata.LogarithmicScales) + { + if (string.IsNullOrWhiteSpace(scale.Name)) + { + Report(context, "a scale entry is missing its name"); + continue; + } + + if (!seenNames.Add(scale.Name)) + { + Report(context, $"duplicate scale name '{scale.Name}'"); + continue; + } + + EmitScale(context, scale); + } + } + + private static void Report(SourceProductionContext context, string message) => + context.ReportDiagnostic(Diagnostic.Create(InvalidScaleDefinition, Location.None, message)); + + private static void EmitScale(SourceProductionContext context, LogarithmicScaleDefinition scale) + { + using CodeBlocker cb = CodeBlocker.Create(); + string name = scale.Name; + string fullType = $"{name}"; + + WriteHeaderTo(cb); + cb.WriteLine("#nullable enable"); + cb.NewLine(); + cb.WriteLine("namespace ktsu.Semantics.Quantities;"); + cb.NewLine(); + cb.WriteLine("using System;"); + cb.WriteLine("using System.Globalization;"); + cb.WriteLine("using System.Numerics;"); + cb.NewLine(); + + cb.WriteLine("/// "); + cb.WriteLine($"/// {scale.Description}"); + cb.WriteLine("/// "); + cb.WriteLine("/// "); + if (!string.IsNullOrWhiteSpace(scale.Remarks)) + { + cb.WriteLine($"/// {scale.Remarks}"); + } + + cb.WriteLine("/// Logarithmic scales don't obey linear arithmetic, so this type is generated as a"); + cb.WriteLine("/// standalone companion (from logarithmic.json) rather than a physical dimension."); + cb.WriteLine("/// "); + cb.WriteLine("/// The floating-point storage type."); + cb.WriteLine("/// The scale value."); + cb.WriteLine($"public readonly partial record struct {fullType}(T Value) : IComparable<{fullType}>"); + cb.WriteLine("\twhere T : struct, INumber"); + using (new Scope(cb)) + { + WriteScalarFactory(cb, scale, fullType); + + foreach (LogarithmicConversionDefinition conversion in scale.Conversions) + { + if (string.IsNullOrWhiteSpace(conversion.Linear)) + { + Report(context, $"scale '{scale.Name}' has a conversion with no linear type"); + continue; + } + + WriteConversion(cb, scale, conversion, fullType); + } + + if (scale.Arithmetic) + { + WriteArithmetic(cb, fullType); + } + + WriteComparisons(cb, fullType); + WriteToString(cb, scale); + } + + context.AddSource($"{name}.g.cs", cb.ToString()); + } + + private static void WriteScalarFactory(CodeBlocker cb, LogarithmicScaleDefinition scale, string fullType) + { + cb.WriteLine("/// "); + cb.WriteLine("/// Creates a value from the raw scale number."); + cb.WriteLine("/// "); + cb.WriteLine("/// The raw scale value."); + cb.WriteLine($"/// A new ."); + cb.WriteLine($"public static {fullType} {scale.ScalarFactory}(T value) => new(value);"); + cb.NewLine(); + } + + private static void WriteConversion(CodeBlocker cb, LogarithmicScaleDefinition scale, LogarithmicConversionDefinition conversion, string fullType) + { + string linear = conversion.Linear; + string fromName = string.IsNullOrWhiteSpace(conversion.FromName) ? $"From{linear}" : conversion.FromName!; + string toName = string.IsNullOrWhiteSpace(conversion.ToName) ? $"To{linear}" : conversion.ToName!; + string multiplier = ToDoubleLiteral(conversion.Multiplier); + string logBase = ToDoubleLiteral(conversion.LogBase); + string? referenceExpr = BuildReferenceExpression(conversion.Reference); + + // scale = multiplier · log_base(linear / reference) + string ratioExpr = referenceExpr == null ? "linearValue" : "linearValue / reference"; + string logExpr = logBase switch + { + "10.0" => $"Math.Log10({ratioExpr})", + "2.0" => $"Math.Log2({ratioExpr})", + _ => $"Math.Log({ratioExpr}, {logBase})", + }; + + cb.WriteLine("/// "); + cb.WriteLine($"/// {conversion.FromSummary ?? $"Creates a value from the linear {linear}."}"); + cb.WriteLine("/// "); + cb.WriteLine($"/// The linear ."); + cb.WriteLine($"/// A new . A linear value of zero maps to negative infinity."); + cb.WriteLine($"public static {fullType} {fromName}({linear} linear)"); + using (new Scope(cb)) + { + cb.WriteLine("ArgumentNullException.ThrowIfNull(linear);"); + cb.WriteLine("double linearValue = double.CreateChecked(linear.Value);"); + if (referenceExpr != null) + { + cb.WriteLine($"double reference = {referenceExpr};"); + } + + cb.WriteLine($"return new(T.CreateChecked({multiplier} * {logExpr}));"); + } + + cb.NewLine(); + + // linear = reference · base^(scale / multiplier) + string inverseExpr = referenceExpr == null + ? $"Math.Pow({logBase}, scaleValue / {multiplier})" + : $"reference * Math.Pow({logBase}, scaleValue / {multiplier})"; + + cb.WriteLine("/// "); + cb.WriteLine($"/// {conversion.ToSummary ?? $"Converts this value to the linear {linear}."}"); + cb.WriteLine("/// "); + cb.WriteLine($"/// The linear ."); + cb.WriteLine($"public {linear} {toName}()"); + using (new Scope(cb)) + { + cb.WriteLine("double scaleValue = double.CreateChecked(Value);"); + if (referenceExpr != null) + { + cb.WriteLine($"double reference = {referenceExpr};"); + } + + cb.WriteLine($"return {linear}.Create(T.CreateChecked({inverseExpr}));"); + } + + cb.NewLine(); + } + + private static void WriteArithmetic(CodeBlocker cb, string fullType) + { + cb.WriteLine("/// Adds two values in log space (cascading two linear stages multiplies them)."); + cb.WriteLine("/// The first value."); + cb.WriteLine("/// The second value."); + cb.WriteLine("/// The summed value."); + cb.WriteLine($"public static {fullType} operator +({fullType} left, {fullType} right) => new(left.Value + right.Value);"); + cb.NewLine(); + cb.WriteLine("/// Subtracts one value from another in log space."); + cb.WriteLine("/// The value to subtract from."); + cb.WriteLine("/// The value to subtract."); + cb.WriteLine("/// The difference."); + cb.WriteLine($"public static {fullType} operator -({fullType} left, {fullType} right) => new(left.Value - right.Value);"); + cb.NewLine(); + cb.WriteLine("/// Adds two values (friendly alternate for operator +)."); + cb.WriteLine("/// The first value."); + cb.WriteLine("/// The second value."); + cb.WriteLine("/// The summed value."); + cb.WriteLine($"public static {fullType} Add({fullType} left, {fullType} right) => left + right;"); + cb.NewLine(); + cb.WriteLine("/// Subtracts one value from another (friendly alternate for operator -)."); + cb.WriteLine("/// The value to subtract from."); + cb.WriteLine("/// The value to subtract."); + cb.WriteLine("/// The difference."); + cb.WriteLine($"public static {fullType} Subtract({fullType} left, {fullType} right) => left - right;"); + cb.NewLine(); + } + + private static void WriteComparisons(CodeBlocker cb, string fullType) + { + cb.WriteLine("/// "); + cb.WriteLine($"public int CompareTo({fullType} other) => Value.CompareTo(other.Value);"); + cb.NewLine(); + (string op, string word)[] comparisons = + [ + ("<", "less than"), + (">", "greater than"), + ("<=", "less than or equal to"), + (">=", "greater than or equal to"), + ]; + foreach ((string op, string word) in comparisons) + { + cb.WriteLine($"/// Determines whether one value is {word} another."); + cb.WriteLine("/// The left value."); + cb.WriteLine("/// The right value."); + cb.WriteLine($"/// if is {word} ."); + cb.WriteLine($"public static bool operator {op}({fullType} left, {fullType} right) => left.CompareTo(right) {op} 0;"); + cb.NewLine(); + } + } + + private static void WriteToString(CodeBlocker cb, LogarithmicScaleDefinition scale) + { + string interpolated = scale.DisplayFormat.Replace("{0}", "{Value}"); + cb.WriteLine("/// Returns a culture-invariant string representation of this value."); + cb.WriteLine("/// The formatted value."); + cb.WriteLine($"public override string ToString() => string.Create(CultureInfo.InvariantCulture, $\"{interpolated}\");"); + } + + /// + /// Builds the C# expression for the conversion reference, or + /// when the reference is one (no scaling). + /// + private static string? BuildReferenceExpression(LogarithmicReferenceDefinition? reference) + { + if (reference == null) + { + return null; + } + + if (!string.IsNullOrWhiteSpace(reference.Constant)) + { + return $"PhysicalConstants.Generic.{reference.Constant}()"; + } + + return string.IsNullOrWhiteSpace(reference.Value) ? null : ToDoubleLiteral(reference.Value!); + } + + /// Normalises a metadata numeric string into a C# double literal. + private static string ToDoubleLiteral(string value) => + double.Parse(value, CultureInfo.InvariantCulture).ToString("0.0###############", CultureInfo.InvariantCulture); +} diff --git a/Semantics.SourceGenerators/Generators/MagnitudesGenerator.cs b/Semantics.SourceGenerators/Generators/MagnitudesGenerator.cs new file mode 100644 index 0000000..e2045aa --- /dev/null +++ b/Semantics.SourceGenerators/Generators/MagnitudesGenerator.cs @@ -0,0 +1,67 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators; + +using ktsu.CodeBlocker; +using Microsoft.CodeAnalysis; +using Semantics.SourceGenerators.Models; +using Semantics.SourceGenerators.Templates; + +/// +/// Source generator that creates the MetricMagnitudes.cs file from JSON metadata. +/// +[Generator] +public class MagnitudesGenerator : GeneratorBase +{ + public MagnitudesGenerator() : base("magnitudes.json") { } + + protected override void Generate(SourceProductionContext context, MagnitudesMetadata metadata, CodeBlocker codeBlocker) + { + if (metadata.Magnitudes.Count == 0) + { + return; + } + + SourceFileTemplate sourceFileTemplate = new() + { + FileName = "MetricMagnitudes.g.cs", + Namespace = "ktsu.Semantics.Quantities", + }; + + ClassTemplate magnitudesClass = new() + { + Comments = + [ + "/// ", + "/// Metric magnitude constants for unit scaling.", + "/// ", + ], + Keywords = ["public", "static", "class"], + Name = "MetricMagnitudes", + }; + + foreach (MagnitudeDefinition magnitude in metadata.Magnitudes) + { + string valueString = magnitude.Exponent switch + { + 0 => "1.0", + _ => $"1e{magnitude.Exponent}", + }; + + magnitudesClass.Members.Add(new FieldTemplate() + { + Comments = [$"/// {magnitude.Name} magnitude ({magnitude.Symbol}): 10^{magnitude.Exponent}"], + Keywords = ["public", "const", "double"], + Name = magnitude.Name, + DefaultValue = valueString, + }); + } + + sourceFileTemplate.Classes.Add(magnitudesClass); + + WriteSourceFileTo(codeBlocker, sourceFileTemplate); + context.AddSource(sourceFileTemplate.FileName, codeBlocker.ToString()); + } +} diff --git a/Semantics.SourceGenerators/Generators/PhysicalConstantsGenerator.cs b/Semantics.SourceGenerators/Generators/PhysicalConstantsGenerator.cs new file mode 100644 index 0000000..5168909 --- /dev/null +++ b/Semantics.SourceGenerators/Generators/PhysicalConstantsGenerator.cs @@ -0,0 +1,138 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators; + +using System.Collections.Generic; +using System.Linq; +using ktsu.CodeBlocker; +using Microsoft.CodeAnalysis; +using Semantics.SourceGenerators.Models; +using Semantics.SourceGenerators.Templates; + +/// +/// Source generator that creates the PhysicalConstants.cs file from JSON metadata. +/// +[Generator] +public class PhysicalConstantsGenerator : GeneratorBase +{ + public PhysicalConstantsGenerator() : base("domains.json") { } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "Description lowercasing for XML docs")] + protected override void Generate(SourceProductionContext context, DomainsMetadata metadata, CodeBlocker codeBlocker) + { + if (metadata.Domains == null || metadata.Domains.Count == 0) + { + return; + } + + SourceFileTemplate sourceFileTemplate = new() + { + FileName = "PhysicalConstants.g.cs", + Namespace = "ktsu.Semantics.Quantities", + Usings = + [ + "System.Globalization", + "System.Numerics", + "ktsu.PreciseNumber", + ], + }; + + ClassTemplate constantsClass = new() + { + Comments = + [ + "/// ", + "/// Provides fundamental physical constants used throughout the Semantics library.", + "/// All values are based on the 2019 redefinition of SI base units and CODATA 2018 values.", + "/// ", + ], + Keywords = ["public", "static", "class"], + Name = "PhysicalConstants", + }; + + // Generate nested class per domain + foreach (Domain domain in metadata.Domains.OrderBy(d => d.Name)) + { + if (domain.Constants == null || domain.Constants.Count == 0) + { + continue; + } + + ClassTemplate domainClass = new() + { + Comments = + [ + "/// ", + $"/// {domain.Description}", + "/// ", + ], + Keywords = ["public", "static", "class"], + Name = domain.Name, + }; + + foreach (ConstantDefinition constant in domain.Constants.OrderBy(c => c.Name)) + { + domainClass.Members.Add(new FieldTemplate() + { + Comments = [$"/// {constant.Description}"], + Keywords = ["public", "static", "readonly", "PreciseNumber"], + Name = constant.Name, + DefaultValue = $"PreciseNumber.Parse(\"{constant.Value}\", CultureInfo.InvariantCulture)", + }); + } + + constantsClass.NestedClasses.Add(domainClass); + } + + // Collect all constants for the Generic helper class + List allConstants = [.. metadata.Domains + .Where(d => d.Constants != null && d.Constants.Count > 0) + .SelectMany(d => d.Constants)]; + + if (allConstants.Count != 0) + { + ClassTemplate genericClass = new() + { + Comments = + [ + "/// ", + "/// Helper methods to get constants as generic numeric types.", + "/// ", + ], + Keywords = ["public", "static", "class"], + Name = "Generic", + }; + + foreach (ConstantDefinition constant in allConstants.OrderBy(c => c.Name)) + { + // Find which domain this constant belongs to + string domainName = metadata.Domains + .First(d => d.Constants != null && d.Constants.Any(c => c.Name == constant.Name)) + .Name; + + genericClass.Members.Add(new MethodTemplate() + { + Comments = [$"/// Gets {constant.Description.ToLowerInvariant()} as type T."], + Keywords = ["public", "static", "T"], + Name = $"{constant.Name}", + BodyFactory = (body) => + { + // PreciseNumber does not support T.CreateChecked conversion (its + // TryConvertToChecked throws NotSupportedException); To() is the + // supported materialisation path. + body.Write($" where T : struct, INumber => {domainName}.{constant.Name}.To();"); + }, + }); + } + + constantsClass.NestedClasses.Add(genericClass); + } + + sourceFileTemplate.Classes.Add(constantsClass); + + WriteSourceFileTo(codeBlocker, sourceFileTemplate); + context.AddSource(sourceFileTemplate.FileName, codeBlocker.ToString()); + } +} diff --git a/Semantics.SourceGenerators/Generators/PrecisionGenerator.cs b/Semantics.SourceGenerators/Generators/PrecisionGenerator.cs new file mode 100644 index 0000000..a6eed17 --- /dev/null +++ b/Semantics.SourceGenerators/Generators/PrecisionGenerator.cs @@ -0,0 +1,88 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators; + +using System.Linq; +using ktsu.CodeBlocker; +using Microsoft.CodeAnalysis; +using Semantics.SourceGenerators.Models; +using Semantics.SourceGenerators.Templates; + +/// +/// Source generator that creates the StorageTypes.cs file from JSON metadata. +/// +[Generator] +public class PrecisionGenerator : GeneratorBase +{ + public PrecisionGenerator() : base("precision.json") { } + + protected override void Generate(SourceProductionContext context, PrecisionMetadata metadata, CodeBlocker codeBlocker) + { + if (metadata.StorageTypes == null || metadata.StorageTypes.Count == 0) + { + return; + } + + SourceFileTemplate sourceFileTemplate = new() + { + FileName = "StorageTypes.g.cs", + Namespace = "ktsu.Semantics.Quantities", + Usings = + [ + "System", + "System.Collections.Generic", + ], + }; + + ClassTemplate storageClass = new() + { + Comments = + [ + "/// ", + "/// Available storage types for numeric values in the Semantics library.", + "/// ", + ], + Keywords = ["public", "static", "class"], + Name = "StorageTypes", + }; + + // Generate Type fields for each storage type + foreach (string storageType in metadata.StorageTypes.OrderBy(t => t)) + { + storageClass.Members.Add(new FieldTemplate() + { + Comments = [$"/// The {storageType} storage type."], + Keywords = ["public", "static", "readonly", "Type"], + Name = storageType.ToUpperInvariant(), + DefaultValue = $"typeof({storageType})", + }); + } + + // Generate All property as a field + string allTypes = string.Join(", ", metadata.StorageTypes.OrderBy(t => t).Select(t => t.ToUpperInvariant())); + storageClass.Members.Add(new FieldTemplate() + { + Comments = ["/// Gets all available storage types."], + Keywords = ["public", "static", "readonly", "IReadOnlyList"], + Name = "All", + DefaultValue = $"new List {{ {allTypes} }}", + }); + + // Generate Names property as a field + string allNames = string.Join(", ", metadata.StorageTypes.OrderBy(t => t).Select(t => $"\"{t}\"")); + storageClass.Members.Add(new FieldTemplate() + { + Comments = ["/// Gets the names of all available storage types."], + Keywords = ["public", "static", "readonly", "IReadOnlyList"], + Name = "Names", + DefaultValue = $"new List {{ {allNames} }}", + }); + + sourceFileTemplate.Classes.Add(storageClass); + + WriteSourceFileTo(codeBlocker, sourceFileTemplate); + context.AddSource(sourceFileTemplate.FileName, codeBlocker.ToString()); + } +} diff --git a/Semantics.SourceGenerators/Generators/QuantitiesGenerator.cs b/Semantics.SourceGenerators/Generators/QuantitiesGenerator.cs new file mode 100644 index 0000000..d886bab --- /dev/null +++ b/Semantics.SourceGenerators/Generators/QuantitiesGenerator.cs @@ -0,0 +1,1659 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using ktsu.CodeBlocker; +using Microsoft.CodeAnalysis; +using Semantics.SourceGenerators.Models; +using Semantics.SourceGenerators.Templates; + +/// +/// Source generator that creates quantity types from the unified vector schema in dimensions.json. +/// Uses a two-phase approach: first collects all cross-dimensional operators globally, +/// then generates each type with its assigned operators. +/// +[Generator] +public class QuantitiesGenerator : GeneratorBase +{ + private static readonly DiagnosticDescriptor UnknownDimensionReference = new( + id: "SEM001", + title: "Unknown dimension reference in physics relationship", + messageFormat: "Dimension '{0}' references unknown dimension '{1}' in {2}; the operator will not be generated. Check spelling and that the referenced dimension exists in dimensions.json.", + category: "Semantics.SourceGenerators", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor MetadataValidationFailed = new( + id: "SEM002", + title: "dimensions.json metadata validation failed", + messageFormat: "dimensions.json validation issue: {0}", + category: "Semantics.SourceGenerators", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor RelationshipFormMissing = new( + id: "SEM003", + title: "Relationship requires a vector form not declared on a participating dimension", + messageFormat: "Relationship in dimension '{0}' ({1}) explicitly requests form V{2}, but '{3}' does not declare that form. The operator will not be generated.", + category: "Semantics.SourceGenerators", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor UnknownUnitReference = new( + id: "SEM004", + title: "dimensions.json references a unit not declared in units.json", + messageFormat: "Unit '{0}' (referenced by dimension '{1}'.availableUnits) is not declared in units.json; the generated From{0} factory will use an identity conversion. Add the unit to units.json or fix the spelling.", + category: "Semantics.SourceGenerators", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public QuantitiesGenerator() : base("dimensions.json") { } + + /// + /// Holds the metadata that drives quantity emission. Combined from dimensions.json and + /// units.json so factory methods can apply per-unit conversion factors. Plain class + /// (not a positional record) because the netstandard2.0 source-generator target lacks + /// System.Runtime.CompilerServices.IsExternalInit. + /// + private sealed class CombinedMetadata + { + public DimensionsMetadata Dimensions { get; } + public UnitsMetadata Units { get; } + + public CombinedMetadata(DimensionsMetadata dimensions, UnitsMetadata units) + { + Dimensions = dimensions; + Units = units; + } + } + + /// + /// Override to load both dimensions.json and units.json. The base class only loads a single + /// metadata file; we need both because per-unit conversion factors are required to emit + /// From{Unit} factories that aren't the SI base unit. + /// + public override void Initialize(IncrementalGeneratorInitializationContext context) + { + IncrementalValueProvider dimensionsProvider = LoadJson(context, "dimensions.json"); + IncrementalValueProvider unitsProvider = LoadJson(context, "units.json"); + IncrementalValueProvider combined = dimensionsProvider.Combine(unitsProvider).Select(static (pair, _) => + pair.Left == null ? null : new CombinedMetadata(pair.Left, pair.Right ?? new UnitsMetadata())); + + context.RegisterSourceOutput(combined, (ctx, metadata) => + { + if (metadata == null) + { + return; + } + + using CodeBlocker codeBlocker = CodeBlocker.Create(); + GenerateInner(ctx, metadata.Dimensions, metadata.Units, codeBlocker); + }); + } + + private static IncrementalValueProvider LoadJson(IncrementalGeneratorInitializationContext context, string filename) + where TMeta : class + { + return context.AdditionalTextsProvider + .Where(file => file.Path.EndsWith(filename, StringComparison.InvariantCulture)) + .Select((file, ct) => file.GetText(ct)?.ToString() ?? "") + .Where(content => !string.IsNullOrEmpty(content)) + .Select((content, _) => + { + try + { + return JsonSerializer.Deserialize(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } + catch (JsonException) + { + return null; + } + }) + .Where(m => m != null) + .Collect() + .Select((arr, _) => arr.FirstOrDefault()); + } + + /// + /// The legacy abstract entry point is unused: the + /// overridden handles registration and calls + /// directly. This shim exists to satisfy the abstract contract. + /// + protected override void Generate(SourceProductionContext context, DimensionsMetadata metadata, CodeBlocker codeBlocker) + => GenerateInner(context, metadata, new UnitsMetadata(), codeBlocker); + + private void GenerateInner(SourceProductionContext context, DimensionsMetadata metadata, UnitsMetadata units, CodeBlocker codeBlocker) + { + if (metadata.PhysicalDimensions == null || metadata.PhysicalDimensions.Count == 0) + { + return; + } + + // Issue #60: surface schema-level metadata problems as diagnostics so they + // show up in the build log instead of crashing mid-emit. + List validationIssues = metadata.Validate(); + foreach (string issue in validationIssues) + { + context.ReportDiagnostic(Diagnostic.Create( + MetadataValidationFailed, + Location.None, + issue)); + } + + Dictionary unitMap = BuildUnitMap(units); + + // Issue #58/#48 follow-up: surface dimensions.json availableUnits entries that + // don't exist in units.json. The generator's BuildToBaseExpression silently falls + // back to identity conversion in that case, which is wrong for any non-base unit + // — a typo (e.g. "Kilometres" vs "Kilometers") would silently produce a factory + // with no scale factor. SEM004 catches that at build time. + ReportUnknownUnitReferences(context, metadata, unitMap); + + // Phase A: Build maps and collect operators + Dictionary dimensionMap = BuildDimensionMap(metadata); + Dictionary typeFormMap = BuildTypeFormMap(metadata); + List allOperators = CollectAllOperators(context, metadata, dimensionMap); + List allProducts = CollectAllProducts(context, metadata, dimensionMap); + Dictionary> operatorsByOwner = GroupBy(allOperators, o => o.OwnerTypeName); + Dictionary> productsByOwner = GroupBy(allProducts, p => p.SelfTypeName); + + // Phase B: Generate types + foreach (PhysicalDimension dim in metadata.PhysicalDimensions) + { + if (dim.Quantities.Vector0 != null) + { + EmitV0BaseType(context, dim, operatorsByOwner, typeFormMap, unitMap); + } + + if (dim.Quantities.Vector1 != null) + { + EmitV1BaseType(context, dim, operatorsByOwner, typeFormMap, unitMap); + } + + int[] vectorDims = [2, 3, 4]; + foreach (int d in vectorDims) + { + VectorFormDefinition? form = GetFormDef(dim, d); + if (form != null) + { + EmitVectorType(context, dim, d, form, operatorsByOwner, productsByOwner, typeFormMap); + } + } + + // Emit semantic overloads for all vector forms + int[] allForms = [0, 1, 2, 3, 4]; + foreach (int f in allForms) + { + VectorFormDefinition? form = GetFormDef(dim, f); + if (form != null) + { + foreach (OverloadDefinition overload in form.Overloads) + { + EmitOverloadType(context, dim, f, form.Base, overload, typeFormMap, unitMap); + } + } + } + } + } + + #region Phase A: Map Building and Operator Collection + + private static Dictionary BuildDimensionMap(DimensionsMetadata metadata) + { + Dictionary map = []; + foreach (PhysicalDimension dim in metadata.PhysicalDimensions) + { + map[dim.Name] = dim; + } + + return map; + } + + private static Dictionary BuildTypeFormMap(DimensionsMetadata metadata) + { + Dictionary map = []; + foreach (PhysicalDimension dim in metadata.PhysicalDimensions) + { + int[] forms = [0, 1, 2, 3, 4]; + foreach (int f in forms) + { + VectorFormDefinition? form = GetFormDef(dim, f); + if (form != null) + { + map[form.Base] = f; + foreach (OverloadDefinition overload in form.Overloads) + { + map[overload.Name] = f; + } + } + } + } + + return map; + } + + private static List CollectAllOperators(SourceProductionContext context, DimensionsMetadata metadata, Dictionary dimMap) + { + HashSet seen = []; + List result = []; + + foreach (PhysicalDimension dim in metadata.PhysicalDimensions) + { + // Process integrals: Self * Other = Result + foreach (RelationshipDefinition integral in dim.Integrals) + { + if (!dimMap.TryGetValue(integral.Other, out PhysicalDimension? otherDim)) + { + ReportUnknownReference(context, dim.Name, integral.Other, $"integrals[{integral.Other} -> {integral.Result}].other"); + continue; + } + + if (!dimMap.TryGetValue(integral.Result, out PhysicalDimension? resultDim)) + { + ReportUnknownReference(context, dim.Name, integral.Result, $"integrals[{integral.Other} -> {integral.Result}].result"); + continue; + } + + // V0(Other) is the scalar multiplier + string? v0Other = otherDim.Quantities.Vector0?.Base; + if (v0Other == null) + { + continue; + } + + // For integrals the "Other" multiplier is V0 only; the form propagates + // between Self and Result, so SEM003 should fire if either Self or + // Result is missing a declared form. (V0-only Other was already + // rejected above via the v0Other null check.) + int[] forms = ResolveForms( + context, + integral, + [0, 1, 2, 3, 4], + dim, + resultDim, + $"integrals[{integral.Other} -> {integral.Result}]"); + foreach (int vn in forms) + { + string? selfType = GetBaseTypeName(dim, vn); + string? resultType = GetBaseTypeName(resultDim, vn); + if (selfType == null || resultType == null) + { + continue; + } + + // Forward: VN(Self) * V0(Other) => VN(Result) + AddOp(result, seen, "*", selfType, v0Other, resultType, selfType); + // Commutative: V0(Other) * VN(Self) => VN(Result) + AddOp(result, seen, "*", v0Other, selfType, resultType, v0Other); + // Inverse: VN(Result) / V0(Other) => VN(Self) + AddOp(result, seen, "/", resultType, v0Other, selfType, resultType); + // Inverse: VN(Result) / VN(Self) => V0(Other) -- only if VN == V0 + if (vn == 0) + { + AddOp(result, seen, "/", resultType, selfType, v0Other, resultType); + } + } + } + + // Process derivatives: Self / Other = Result + foreach (RelationshipDefinition derivative in dim.Derivatives) + { + if (!dimMap.TryGetValue(derivative.Other, out PhysicalDimension? otherDim)) + { + ReportUnknownReference(context, dim.Name, derivative.Other, $"derivatives[{derivative.Other} -> {derivative.Result}].other"); + continue; + } + + if (!dimMap.TryGetValue(derivative.Result, out PhysicalDimension? resultDim)) + { + ReportUnknownReference(context, dim.Name, derivative.Result, $"derivatives[{derivative.Other} -> {derivative.Result}].result"); + continue; + } + + string? v0Other = otherDim.Quantities.Vector0?.Base; + if (v0Other == null) + { + continue; + } + + int[] forms = ResolveForms( + context, + derivative, + [0, 1, 2, 3, 4], + dim, + resultDim, + $"derivatives[{derivative.Other} -> {derivative.Result}]"); + foreach (int vn in forms) + { + string? selfType = GetBaseTypeName(dim, vn); + string? resultType = GetBaseTypeName(resultDim, vn); + if (selfType == null || resultType == null) + { + continue; + } + + // Forward: VN(Self) / V0(Other) => VN(Result) + AddOp(result, seen, "/", selfType, v0Other, resultType, selfType); + // Inverse integral: VN(Result) * V0(Other) => VN(Self) + AddOp(result, seen, "*", resultType, v0Other, selfType, resultType); + // Commutative inverse: V0(Other) * VN(Result) => VN(Self) + AddOp(result, seen, "*", v0Other, resultType, selfType, v0Other); + } + } + } + + return result; + } + + private static List CollectAllProducts(SourceProductionContext context, DimensionsMetadata metadata, Dictionary dimMap) + { + HashSet seen = []; + List result = []; + + foreach (PhysicalDimension dim in metadata.PhysicalDimensions) + { + // Dot products: VN(Self) . VN(Other) => V0(Result) + foreach (RelationshipDefinition dot in dim.DotProducts) + { + if (!dimMap.TryGetValue(dot.Other, out PhysicalDimension? otherDim)) + { + ReportUnknownReference(context, dim.Name, dot.Other, $"dotProducts[{dot.Other} -> {dot.Result}].other"); + continue; + } + + if (!dimMap.TryGetValue(dot.Result, out PhysicalDimension? resultDim)) + { + ReportUnknownReference(context, dim.Name, dot.Result, $"dotProducts[{dot.Other} -> {dot.Result}].result"); + continue; + } + + string? v0Result = resultDim.Quantities.Vector0?.Base; + if (v0Result == null) + { + continue; + } + + // Dot product is undefined for V0; default forms are V1+. + int[] forms = ResolveForms( + context, + dot, + [1, 2, 3, 4], + dim, + otherDim, + $"dotProducts[{dot.Other} -> {dot.Result}]"); + foreach (int vn in forms) + { + string? selfType = GetBaseTypeName(dim, vn); + string? otherType = GetBaseTypeName(otherDim, vn); + if (selfType == null || otherType == null) + { + continue; + } + + string key = $"Dot:{selfType}:{otherType}:{v0Result}"; + if (seen.Add(key)) + { + result.Add(new ProductInfo("Dot", selfType, otherType, v0Result, vn)); + } + } + } + + // Cross products: V3(Self) x V3(Other) => V3(Result) + foreach (RelationshipDefinition cross in dim.CrossProducts) + { + if (!dimMap.TryGetValue(cross.Other, out PhysicalDimension? otherDim)) + { + ReportUnknownReference(context, dim.Name, cross.Other, $"crossProducts[{cross.Other} -> {cross.Result}].other"); + continue; + } + + if (!dimMap.TryGetValue(cross.Result, out PhysicalDimension? resultDim)) + { + ReportUnknownReference(context, dim.Name, cross.Result, $"crossProducts[{cross.Other} -> {cross.Result}].result"); + continue; + } + + // Cross product is intrinsically 3D. Default to V3 only; explicit Forms + // other than [3] are accepted but the operator emit below only handles V3. + // Pass resultDim so SEM003 surfaces when the declared form is missing on + // the result type too (e.g. Force × Length → Torque at V2: Torque has no V2). + int[] forms = ResolveForms( + context, + cross, + [3], + dim, + otherDim, + $"crossProducts[{cross.Other} -> {cross.Result}]", + resultDim); + if (Array.IndexOf(forms, 3) < 0) + { + continue; + } + + string? selfV3 = GetBaseTypeName(dim, 3); + string? otherV3 = GetBaseTypeName(otherDim, 3); + string? resultV3 = GetBaseTypeName(resultDim, 3); + if (selfV3 == null || otherV3 == null || resultV3 == null) + { + continue; + } + + string key = $"Cross:{selfV3}:{otherV3}:{resultV3}"; + if (seen.Add(key)) + { + result.Add(new ProductInfo("Cross", selfV3, otherV3, resultV3, 3)); + } + } + } + + return result; + } + + private static void AddOp(List list, HashSet seen, string op, string left, string right, string ret, string owner) + { + // Skip self-division (base class already handles TSelf / TSelf => TStorage) + if (op == "/" && left == right) + { + return; + } + + string key = $"{op}:{left}:{right}:{ret}"; + if (seen.Add(key)) + { + list.Add(new OperatorInfo(op, left, right, ret, owner)); + } + } + + private static void ReportUnknownReference(SourceProductionContext context, string owningDimension, string unknownReference, string fieldPath) + { + context.ReportDiagnostic(Diagnostic.Create( + UnknownDimensionReference, + Location.None, + owningDimension, + unknownReference, + fieldPath)); + } + + /// + /// Resolves the forms at which a relationship should emit operators. When the metadata + /// declares explicitly, that list wins and + /// any form missing from one of the participating dimensions is reported as + /// SEM003. When the list is empty, returns + /// (which the caller filters silently — preserving the legacy behaviour for relationships + /// that haven't opted into form-specific declarations). + /// + private static int[] ResolveForms( + SourceProductionContext context, + RelationshipDefinition rel, + int[] defaultForms, + PhysicalDimension dim, + PhysicalDimension otherDim, + string fieldPath, + PhysicalDimension? resultDim = null) + { + if (rel.Forms.Count == 0) + { + return defaultForms; + } + + List kept = []; + foreach (int form in rel.Forms) + { + if (form < 0 || form > 4) + { + continue; + } + + if (GetBaseTypeName(dim, form) == null) + { + ReportFormMissing(context, dim.Name, fieldPath, form, dim.Name); + continue; + } + + if (GetBaseTypeName(otherDim, form) == null) + { + ReportFormMissing(context, dim.Name, fieldPath, form, otherDim.Name); + continue; + } + + if (resultDim != null && GetBaseTypeName(resultDim, form) == null) + { + ReportFormMissing(context, dim.Name, fieldPath, form, resultDim.Name); + continue; + } + + kept.Add(form); + } + + return [.. kept]; + } + + private static void ReportFormMissing(SourceProductionContext context, string owningDimension, string fieldPath, int form, string offendingDimension) + { + context.ReportDiagnostic(Diagnostic.Create( + RelationshipFormMissing, + Location.None, + owningDimension, + fieldPath, + form, + offendingDimension)); + } + + private static Dictionary BuildUnitMap(UnitsMetadata units) + { + Dictionary map = []; + if (units.UnitCategories == null) + { + return map; + } + + foreach (UnitCategory cat in units.UnitCategories) + { + foreach (UnitDefinition unit in cat.Units) + { + map[unit.Name] = unit; + } + } + + return map; + } + + /// + /// Walks every availableUnits entry across the dimensions metadata and emits + /// SEM004 for any unit name that doesn't appear in . + /// Deduplicates by (unit, dimension) so a typo on a unit shared by many dimensions + /// reports once per offending dimension instead of per-form/overload. + /// + private static void ReportUnknownUnitReferences( + SourceProductionContext context, + DimensionsMetadata metadata, + Dictionary unitMap) + { + // If units.json wasn't loaded the map is empty; treating every unit as "unknown" + // would flood the build log. The CombinedMetadata loader already supplies a + // non-null UnitsMetadata even when units.json is missing — check for that case + // and bail rather than report a useless wall of warnings. + if (unitMap.Count == 0) + { + return; + } + + HashSet seen = []; + foreach (PhysicalDimension dim in metadata.PhysicalDimensions) + { + foreach (string unitName in dim.AvailableUnits) + { + if (string.IsNullOrEmpty(unitName) || unitMap.ContainsKey(unitName)) + { + continue; + } + + string key = $"{dim.Name}::{unitName}"; + if (!seen.Add(key)) + { + continue; + } + + context.ReportDiagnostic(Diagnostic.Create( + UnknownUnitReference, + Location.None, + unitName, + dim.Name)); + } + } + } + + /// + /// Emits one From{Unit} static factory per entry in . + /// The first unit is treated as the SI base unit (no conversion). Subsequent units use the + /// conversion factor / magnitude / offset declared in . + /// When is true, the converted value is wrapped with + /// Vector0Guards.EnsureNonNegative so a negative input — including one that becomes + /// negative after a unit conversion (e.g. FromCelsius(-300)) — throws + /// . This locks in the V0 non-negativity invariant + /// from #50 across every per-unit factory introduced for #48. + /// When is also true (V0 overloads with + /// physicalConstraints.minExclusive: "0" per #51) the guard is upgraded to + /// Vector0Guards.EnsurePositive, which rejects zero as well as negative values. + /// is ignored when is false. + /// + private static void AddUnitFactories( + ClassTemplate cls, + List availableUnits, + Dictionary unitMap, + string typeName, + string fullType, + string crefForComment, + bool applyV0Guard, + bool strictPositive = false) + { + if (availableUnits == null || availableUnits.Count == 0) + { + return; + } + + string guardMethod = strictPositive ? "EnsurePositive" : "EnsureNonNegative"; + + string baseUnit = availableUnits[0]; + foreach (string unitName in availableUnits) + { + bool isBase = unitName == baseUnit; + string conversionExpr = isBase + ? "value" + : BuildToBaseExpression(unitName, unitMap); + + string body = applyV0Guard + ? $" => Create(Vector0Guards.{guardMethod}({conversionExpr}, nameof(value)));" + : $" => Create({conversionExpr});"; + + // Issue #49: factory names are the unit's singular lemma — the unit name verbatim + // (e.g. From{Meter}, From{Kilogram}, From{MeterPerSecond}). units.json carries the + // singular lemma for every unit including compounds, so the generator never has to + // know English pluralisation: the rule is purely mechanical, From{Name}. + string factorySuffix = unitName; + + List comments = + [ + "/// ", + $"/// Creates a new {crefForComment} from a value in {unitName}.", + "/// ", + $"/// The value in {unitName}.", + $"/// A new {crefForComment} instance.", + ]; + if (applyV0Guard) + { + comments.Add("/// Thrown when the resulting magnitude would be negative."); + } + + cls.Members.Add(new MethodTemplate() + { + Comments = comments, + Keywords = ["public", "static", fullType], + Name = $"From{factorySuffix}", + Parameters = [new ParameterTemplate { Type = "T", Name = "value" }], + BodyFactory = (b) => b.Write(body), + }); + } + } + + /// + /// Builds the C# expression converting value in to the SI + /// base unit. Honours magnitude (Kilo, Centi, …), conversionFactor (lookup in + /// ), and offset (additive, after scaling). + /// + private static string BuildToBaseExpression(string unitName, Dictionary unitMap) + { + // If we don't have unit metadata, fall back to identity. The dimensions.json author is + // responsible for keeping availableUnits in sync with units.json; if a unit is missing, + // the emitted factory passes the value through unchanged so the build still succeeds. + // (A future SEM00x diagnostic could surface this gap.) + if (!unitMap.TryGetValue(unitName, out UnitDefinition? unit) || unit == null) + { + return "value"; + } + + string scaled = "value"; + bool hasMagnitude = !string.IsNullOrEmpty(unit.Magnitude) && unit.Magnitude != "1"; + bool hasFactor = !string.IsNullOrEmpty(unit.ConversionFactor) && unit.ConversionFactor != "1"; + + if (hasMagnitude) + { + scaled = $"(value * T.CreateChecked(MetricMagnitudes.{unit.Magnitude}))"; + } + else if (hasFactor) + { + scaled = $"(value * T.CreateChecked(Units.ConversionConstants.{unit.ConversionFactor}))"; + } + + bool hasOffset = !string.IsNullOrEmpty(unit.Offset) && unit.Offset != "0"; + if (hasOffset) + { + scaled = $"({scaled} + T.CreateChecked(Units.ConversionConstants.{unit.Offset}))"; + } + + return scaled; + } + + /// + /// Adds the per-quantity surface required by + /// (#59): a Dimension override returning PhysicalDimensions.{dim}, plus a + /// typed In(I{dim}Unit) method that converts the stored SI-base value into the + /// caller's unit. Emitted for V0 and V1 (scalar-storage) types only; vector V2+ types + /// have per-component conversion needs and are deferred. + /// + private static void AddDimensionAndInMembers(ClassTemplate cls, PhysicalDimension dim) + { + cls.Members.Add(new FieldTemplate() + { + Comments = [$"/// Gets the physical dimension this quantity belongs to."], + Keywords = ["public", "override", "DimensionInfo"], + Name = $"Dimension => PhysicalDimensions.{dim.Name}", + }); + + cls.Members.Add(new MethodTemplate() + { + Comments = + [ + "/// ", + $"/// Converts this quantity's SI-base value to the value in .", + "/// Cross-dimension calls (e.g. passing a non-" + dim.Name + " unit) fail at compile time.", + "/// ", + "/// The dimensionally-compatible target unit.", + "/// The value expressed in .", + ], + Keywords = ["public", "T"], + Name = "In", + Parameters = + [ + new ParameterTemplate { Type = $"global::ktsu.Semantics.Quantities.I{dim.Name}Unit", Name = "unit" }, + ], + BodyFactory = (body) => body.Write(" => unit.FromBase(Value);"), + }); + } + + private static Dictionary> GroupBy(List items, Func keySelector) + { + Dictionary> groups = []; + foreach (T item in items) + { + string key = keySelector(item); + if (!groups.TryGetValue(key, out List? group)) + { + group = []; + groups[key] = group; + } + + group.Add(item); + } + + return groups; + } + + #endregion + + #region Phase B: Type Generation + + private void EmitV0BaseType( + SourceProductionContext context, + PhysicalDimension dim, + Dictionary> operatorsByOwner, + Dictionary typeFormMap, + Dictionary unitMap) + { + VectorFormDefinition v0 = dim.Quantities.Vector0!; + string typeName = v0.Base; + string fullType = $"{typeName}"; + + using CodeBlocker cb = CodeBlocker.Create(); + + SourceFileTemplate sourceFile = new() + { + FileName = $"{typeName}.g.cs", + Namespace = "ktsu.Semantics.Quantities", + Usings = ["System.Numerics"], + }; + + ClassTemplate cls = new() + { + Comments = + [ + "/// ", + $"/// Magnitude (Vector0) quantity for the {dim.Name} dimension.", + "/// ", + "/// The numeric storage type.", + ], + Keywords = ["public", "partial", "record"], + Name = fullType, + BaseClass = $"PhysicalQuantity<{fullType}, T>", + Interfaces = [$"IVector0<{fullType}, T>"], + Constraints = ["where T : struct, INumber"], + }; + + // Zero property (satisfies IVector0) + cls.Members.Add(new FieldTemplate() + { + Comments = ["/// Gets a quantity with value zero."], + Keywords = ["public", "static", fullType], + Name = "Zero => Create(T.Zero)", + }); + + // Factory methods for every available unit (#48). The V0 non-negativity invariant from + // #50 is enforced by AddUnitFactories — for non-base units the guard runs *after* the + // conversion, so an input that is non-negative in its source unit but negative in the + // SI base unit (e.g. -300 °C → -26.85 K) still throws. + AddUnitFactories( + cls, + dim.AvailableUnits, + unitMap, + typeName, + fullType, + "", + applyV0Guard: true); + + // Dimension override + typed In() (#59). + AddDimensionAndInMembers(cls, dim); + + // V0 - V0 returns the same V0 of T.Abs(left - right) (locked decision in #52). + // We emit this on every V0 base type so the derived operator wins overload resolution + // over PhysicalQuantity's plain subtraction (which can produce a negative magnitude + // and would trip the non-negativity guard from #50). + cls.Members.Add(new MethodTemplate() + { + Comments = + [ + "/// ", + $"/// Subtracts two {typeName} values, returning the absolute difference as a non-negative {typeName}.", + "/// Magnitude subtraction stays a magnitude (per the unified-vector model).", + "/// ", + ], + Attributes = ["System.Diagnostics.CodeAnalysis.SuppressMessage(\"Usage\", \"CA2225:Operator overloads have named alternates\", Justification = \"Physics quantity operator\")"], + Keywords = ["public", "static", fullType], + Name = "operator -", + Parameters = + [ + new ParameterTemplate { Type = fullType, Name = "left" }, + new ParameterTemplate { Type = fullType, Name = "right" }, + ], + BodyFactory = (body) => body.Write(" => Create(T.Abs(left.Quantity - right.Quantity));"), + }); + + // Cross-dimensional operators + EmitScalarOperators(cls, typeName, operatorsByOwner, typeFormMap); + + + sourceFile.Classes.Add(cls); + WriteSourceFileTo(cb, sourceFile); + context.AddSource(sourceFile.FileName, cb.ToString()); + } + + private void EmitV1BaseType( + SourceProductionContext context, + PhysicalDimension dim, + Dictionary> operatorsByOwner, + Dictionary typeFormMap, + Dictionary unitMap) + { + VectorFormDefinition v1 = dim.Quantities.Vector1!; + string typeName = v1.Base; + string fullType = $"{typeName}"; + string? v0TypeName = dim.Quantities.Vector0?.Base; + + using CodeBlocker cb = CodeBlocker.Create(); + + SourceFileTemplate sourceFile = new() + { + FileName = $"{typeName}.g.cs", + Namespace = "ktsu.Semantics.Quantities", + Usings = ["System.Numerics"], + }; + + ClassTemplate cls = new() + { + Comments = + [ + "/// ", + $"/// Signed one-dimensional (Vector1) quantity for the {dim.Name} dimension.", + "/// ", + "/// The numeric storage type.", + ], + Keywords = ["public", "partial", "record"], + Name = fullType, + BaseClass = $"PhysicalQuantity<{fullType}, T>", + Interfaces = [$"IVector1<{fullType}, T>"], + Constraints = ["where T : struct, INumber"], + }; + + // Zero property (satisfies IVector1) + cls.Members.Add(new FieldTemplate() + { + Comments = ["/// Gets a quantity with value zero."], + Keywords = ["public", "static", fullType], + Name = "Zero => Create(T.Zero)", + }); + + // Factory methods for every available unit. + // V1 quantities are signed; no V0 non-negativity guard. + AddUnitFactories( + cls, + dim.AvailableUnits, + unitMap, + typeName, + fullType, + "", + applyV0Guard: false); + + // Dimension override + typed In() (#59). + AddDimensionAndInMembers(cls, dim); + + // Magnitude method returning V0 base + if (v0TypeName != null) + { + cls.Members.Add(new MethodTemplate() + { + Comments = + [ + "/// ", + $"/// Gets the magnitude of this quantity as a .", + "/// ", + $"/// The non-negative magnitude.", + ], + Keywords = ["public", $"{v0TypeName}"], + Name = "Magnitude", + Parameters = [], + BodyFactory = (body) => body.Write($" => {v0TypeName}.Create(T.Abs(Value));"), + }); + } + + // Cross-dimensional operators + EmitScalarOperators(cls, typeName, operatorsByOwner, typeFormMap); + + sourceFile.Classes.Add(cls); + WriteSourceFileTo(cb, sourceFile); + context.AddSource(sourceFile.FileName, cb.ToString()); + } + + private void EmitVectorType( + SourceProductionContext context, + PhysicalDimension dim, + int dims, + VectorFormDefinition form, + Dictionary> operatorsByOwner, + Dictionary> productsByOwner, + Dictionary typeFormMap) + { + string[] components = dims switch + { + 2 => ["X", "Y"], + 3 => ["X", "Y", "Z"], + 4 => ["X", "Y", "Z", "W"], + _ => throw new ArgumentOutOfRangeException(nameof(dims)), + }; + + string typeName = form.Base; + string fullType = $"{typeName}"; + string interfaceName = $"IVector{dims}<{fullType}, T>"; + string? v0TypeName = dim.Quantities.Vector0?.Base; + + using CodeBlocker cb = CodeBlocker.Create(); + + WriteHeaderTo(cb); + cb.WriteLine("#pragma warning disable IDE0040 // Accessibility modifiers required"); + cb.WriteLine("#pragma warning disable CA2225 // Operator overloads have named alternates"); + cb.NewLine(); + + cb.WriteLine("namespace ktsu.Semantics.Quantities;"); + cb.NewLine(); + cb.WriteLine("using System;"); + cb.WriteLine("using System.Numerics;"); + cb.NewLine(); + + cb.WriteLine("/// "); + cb.WriteLine($"/// {dims}D vector representation of {dim.Name}."); + cb.WriteLine("/// "); + cb.WriteLine("/// The numeric component type."); + cb.WriteLine($"public partial record {fullType} : {interfaceName}"); + cb.WriteLine("\twhere T : struct, INumber"); + + using (new ScopeWithTrailingSemicolon(cb)) + { + WriteVectorComponentProperties(cb, components); + WriteVectorStaticProperties(cb, fullType, components); + + // Typed Magnitude() method returning V0 base + if (v0TypeName != null) + { + cb.WriteLine($"/// Gets the magnitude as a ."); + cb.WriteLine($"public {v0TypeName} Magnitude() => {v0TypeName}.Create(Length());"); + cb.NewLine(); + } + + WriteVectorMethods(cb, fullType, components, dims); + WriteVectorOperators(cb, fullType, components); + + // Cross-dimensional operators (inlined for VN types) + EmitVectorCrossDimOperators(cb, typeName, fullType, components, operatorsByOwner, typeFormMap); + + // Typed dot product methods + if (productsByOwner.TryGetValue(typeName, out List? products)) + { + foreach (ProductInfo prod in products) + { + if (prod.Method == "Dot") + { + string dotExpr = string.Join(" + ", components.Select(c => $"({c} * other.{c})")); + cb.WriteLine($"/// Typed dot product: {typeName} . {prod.OtherTypeName} = {prod.ReturnTypeName}."); + cb.WriteLine($"public {prod.ReturnTypeName} Dot({prod.OtherTypeName} other) => {prod.ReturnTypeName}.Create({dotExpr});"); + cb.NewLine(); + } + else if (prod.Method == "Cross" && dims == 3) + { + cb.WriteLine($"/// Typed cross product: {typeName} x {prod.OtherTypeName} = {prod.ReturnTypeName}."); + cb.WriteLine($"public {prod.ReturnTypeName} Cross({prod.OtherTypeName} other)"); + using (new Scope(cb)) + { + cb.WriteLine($"return new() {{ X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }};"); + } + + cb.NewLine(); + } + } + } + } + + context.AddSource($"{typeName}.g.cs", cb.ToString()); + } + + private void EmitOverloadType( + SourceProductionContext context, + PhysicalDimension dim, + int vectorForm, + string baseTypeName, + OverloadDefinition overload, + Dictionary typeFormMap, + Dictionary unitMap) + { + string typeName = overload.Name; + string fullType = $"{typeName}"; + string baseFullType = $"{baseTypeName}"; + + // V0/V1 overloads inherit from PhysicalQuantity + if (vectorForm <= 1) + { + using CodeBlocker cb = CodeBlocker.Create(); + + string interfaceName = vectorForm == 0 ? $"IVector0<{fullType}, T>" : $"IVector1<{fullType}, T>"; + + SourceFileTemplate sourceFile = new() + { + FileName = $"{typeName}.g.cs", + Namespace = "ktsu.Semantics.Quantities", + Usings = ["System.Numerics"], + }; + + ClassTemplate cls = new() + { + Comments = + [ + "/// ", + $"/// {overload.Description}", + $"/// Semantic overload of .", + "/// ", + "/// The numeric storage type.", + ], + Keywords = ["public", "partial", "record"], + Name = fullType, + BaseClass = $"PhysicalQuantity<{fullType}, T>", + Interfaces = [interfaceName], + Constraints = ["where T : struct, INumber"], + }; + + // Zero property + cls.Members.Add(new FieldTemplate() + { + Comments = ["/// Gets a quantity with value zero."], + Keywords = ["public", "static", fullType], + Name = "Zero => Create(T.Zero)", + }); + + // Factory methods for every available unit (#48); overloads inherit the dimension's + // units. V0 overloads enforce the same non-negativity invariant as their V0 base + // type (#50). V0 overloads that declare physicalConstraints.minExclusive in + // dimensions.json (#51, e.g. Wavelength, Period, HalfLife) get the stricter + // EnsurePositive guard so a zero input is rejected too. V1 overloads accept + // any sign. + bool strictPositive = vectorForm == 0 + && overload.PhysicalConstraints?.MinExclusive == "0"; + AddUnitFactories( + cls, + dim.AvailableUnits, + unitMap, + typeName, + fullType, + typeName, + applyV0Guard: vectorForm == 0, + strictPositive: strictPositive); + + // Dimension override + typed In() (#59). + AddDimensionAndInMembers(cls, dim); + + // Implicit widening to base type + cls.Members.Add(new MethodTemplate() + { + Comments = [$"/// Implicit conversion to {baseTypeName}."], + Keywords = ["public", "static", "implicit", "operator"], + Name = baseFullType, + Parameters = [new ParameterTemplate { Type = fullType, Name = "value" }], + BodyFactory = (body) => body.Write($" => {baseFullType}.Create(value.Value);"), + }); + + // Explicit narrowing from base type + cls.Members.Add(new MethodTemplate() + { + Comments = [$"/// Explicit conversion from {baseTypeName}."], + Keywords = ["public", "static", "explicit", "operator"], + Name = fullType, + Parameters = [new ParameterTemplate { Type = baseFullType, Name = "value" }], + BodyFactory = (body) => body.Write($" => Create(value.Value);"), + }); + + // Factory-style narrowing from base + cls.Members.Add(new MethodTemplate() + { + Comments = [$"/// Creates a {typeName} from a {baseTypeName} value."], + Keywords = ["public", "static", fullType], + Name = "From", + Parameters = [new ParameterTemplate { Type = baseFullType, Name = "value" }], + BodyFactory = (body) => body.Write(" => Create(value.Value);"), + }); + + // V0 overload subtraction returns the same V0 of T.Abs(left - right) (locked + // in #52). The overload-typed operator hides the base PhysicalQuantity's plain + // subtraction so overloads stay in their own type and the magnitude invariant + // is preserved. + if (vectorForm == 0) + { + cls.Members.Add(new MethodTemplate() + { + Comments = [$"/// Subtracts two {typeName} values, returning the absolute difference as a non-negative {typeName}."], + Attributes = ["System.Diagnostics.CodeAnalysis.SuppressMessage(\"Usage\", \"CA2225:Operator overloads have named alternates\", Justification = \"Physics quantity operator\")"], + Keywords = ["public", "static", fullType], + Name = "operator -", + Parameters = + [ + new ParameterTemplate { Type = fullType, Name = "left" }, + new ParameterTemplate { Type = fullType, Name = "right" }, + ], + BodyFactory = (body) => body.Write(" => Create(T.Abs(left.Quantity - right.Quantity));"), + }); + } + + // Relationship methods (e.g., Diameter.ToRadius(), Diameter.FromRadius()) + foreach (KeyValuePair rel in overload.Relationships) + { + // rel.Key is like "toRadius" or "fromRadius", rel.Value is the C# expression + string methodName = char.ToUpperInvariant(rel.Key[0]) + rel.Key.Substring(1); + + if (methodName.StartsWith("To", StringComparison.Ordinal)) + { + // Instance method: e.g., ToRadius() returns Radius + string targetName = methodName.Substring(2); + string targetType = $"{targetName}"; + string expr = rel.Value; // uses "Value" referring to this instance + cls.Members.Add(new MethodTemplate() + { + Comments = [$"/// Converts this {typeName} to a {targetName}."], + Keywords = ["public", targetType], + Name = methodName, + Parameters = [], + BodyFactory = (body) => body.Write($" => {targetType}.Create({expr});"), + }); + } + else if (methodName.StartsWith("From", StringComparison.Ordinal)) + { + // Static factory: e.g., FromRadius(Radius value) returns this type + string sourceName = methodName.Substring(4); + string sourceType = $"{sourceName}"; + // Replace "Value" with "source.Value" since this is a static method + string expr = rel.Value.Replace("Value", "source.Value"); + cls.Members.Add(new MethodTemplate() + { + Comments = [$"/// Creates a {typeName} from a {sourceName} value."], + Keywords = ["public", "static", fullType], + Name = methodName, + Parameters = [new ParameterTemplate { Type = sourceType, Name = "source" }], + BodyFactory = (body) => body.Write($" => Create({expr});"), + }); + } + } + + sourceFile.Classes.Add(cls); + WriteSourceFileTo(cb, sourceFile); + context.AddSource(sourceFile.FileName, cb.ToString()); + } + else + { + // V2/3/4 overloads: these are more complex, generate as standalone records + // For now, V2+ overloads are rare and can be added later + // The strategy document shows them mainly for V3 (Position3D, Translation3D) + EmitVectorOverloadType(context, dim, vectorForm, baseTypeName, overload); + } + } + + private void EmitVectorOverloadType( + SourceProductionContext context, + PhysicalDimension dim, + int dims, + string baseTypeName, + OverloadDefinition overload) + { + string[] components = dims switch + { + 2 => ["X", "Y"], + 3 => ["X", "Y", "Z"], + 4 => ["X", "Y", "Z", "W"], + _ => throw new ArgumentOutOfRangeException(nameof(dims)), + }; + + string typeName = overload.Name; + string fullType = $"{typeName}"; + string baseFullType = $"{baseTypeName}"; + string interfaceName = $"IVector{dims}<{fullType}, T>"; + + using CodeBlocker cb = CodeBlocker.Create(); + + WriteHeaderTo(cb); + cb.WriteLine("#pragma warning disable IDE0040 // Accessibility modifiers required"); + cb.WriteLine("#pragma warning disable CA2225 // Operator overloads have named alternates"); + cb.NewLine(); + + cb.WriteLine("namespace ktsu.Semantics.Quantities;"); + cb.NewLine(); + cb.WriteLine("using System;"); + cb.WriteLine("using System.Numerics;"); + cb.NewLine(); + + cb.WriteLine("/// "); + cb.WriteLine($"/// {overload.Description}"); + cb.WriteLine($"/// Semantic overload of ."); + cb.WriteLine("/// "); + cb.WriteLine($"public partial record {fullType} : {interfaceName}"); + cb.WriteLine("\twhere T : struct, INumber"); + + using (new ScopeWithTrailingSemicolon(cb)) + { + WriteVectorComponentProperties(cb, components); + WriteVectorStaticProperties(cb, fullType, components); + WriteVectorMethods(cb, fullType, components, dims); + WriteVectorOperators(cb, fullType, components); + + // Implicit widening to base + string baseInit = string.Join(", ", components.Select(c => $"{c} = value.{c}")); + cb.WriteLine($"/// Implicit conversion to {baseTypeName}."); + cb.WriteLine($"public static implicit operator {baseFullType}({fullType} value) => new() {{ {baseInit} }};"); + cb.NewLine(); + + // Explicit narrowing from base + cb.WriteLine($"/// Explicit conversion from {baseTypeName}."); + cb.WriteLine($"public static explicit operator {fullType}({baseFullType} value) => new() {{ {baseInit} }};"); + cb.NewLine(); + } + + context.AddSource($"{typeName}.g.cs", cb.ToString()); + } + + #endregion + + #region Operator Emission Helpers + + private static void EmitScalarOperators( + ClassTemplate cls, + string ownerTypeName, + Dictionary> operatorsByOwner, + Dictionary typeFormMap) + { + if (!operatorsByOwner.TryGetValue(ownerTypeName, out List? ops)) + { + return; + } + + foreach (OperatorInfo op in ops) + { + int leftForm = GetFormOrDefault(typeFormMap, op.LeftTypeName); + int rightForm = GetFormOrDefault(typeFormMap, op.RightTypeName); + + // For V0/V1 owner types, use Multiply/Divide helpers when both operands are V0/V1 + if (leftForm <= 1 && rightForm <= 1) + { + string helperName = op.Op == "*" ? "Multiply" : "Divide"; + cls.Members.Add(new MethodTemplate() + { + Comments = + [ + "/// ", + $"/// {(op.Op == "*" ? "Multiplies" : "Divides")} {op.LeftTypeName} {(op.Op == "*" ? "by" : "by")} {op.RightTypeName} to produce {op.ReturnTypeName}.", + "/// ", + ], + Attributes = ["System.Diagnostics.CodeAnalysis.SuppressMessage(\"Usage\", \"CA2225:Operator overloads have named alternates\", Justification = \"Physics quantity operator\")"], + Keywords = ["public", "static", $"{op.ReturnTypeName}"], + Name = $"operator {op.Op}", + Parameters = + [ + new ParameterTemplate { Type = $"{op.LeftTypeName}", Name = "left" }, + new ParameterTemplate { Type = $"{op.RightTypeName}", Name = "right" }, + ], + BodyFactory = (body) => body.Write($" => {helperName}<{op.ReturnTypeName}>(left, right);"), + }); + } + else + { + // One operand is V2+ (VN type, multi-component) + // The owner is V0/V1, the other operand is VN + // Generate inline: left.Value {op} right.X, etc. OR left.X {op} right.Value, etc. + EmitInlineCrossDimOp(cls, op, typeFormMap); + } + } + } + + private static void EmitInlineCrossDimOp( + ClassTemplate cls, + OperatorInfo op, + Dictionary typeFormMap) + { + int leftForm = GetFormOrDefault(typeFormMap, op.LeftTypeName); + int rightForm = GetFormOrDefault(typeFormMap, op.RightTypeName); + int resultForm = GetFormOrDefault(typeFormMap, op.ReturnTypeName); + + string[] resultComponents = resultForm switch + { + 2 => ["X", "Y"], + 3 => ["X", "Y", "Z"], + 4 => ["X", "Y", "Z", "W"], + _ => [], + }; + + if (resultComponents.Length == 0) + { + return; + } + + string bodyExpr; + if (leftForm >= 2 && rightForm <= 1) + { + // VN op V0: component-wise with right.Value + string initExpr = string.Join(", ", resultComponents.Select(c => $"{c} = left.{c} {op.Op} right.Value")); + bodyExpr = $" => new() {{ {initExpr} }};"; + } + else if (leftForm <= 1 && rightForm >= 2) + { + // V0 op VN: component-wise with left.Value + if (op.Op == "*") + { + string initExpr = string.Join(", ", resultComponents.Select(c => $"{c} = left.Value {op.Op} right.{c}")); + bodyExpr = $" => new() {{ {initExpr} }};"; + } + else + { + // V0 / VN doesn't make physical sense, skip + return; + } + } + else + { + return; + } + + cls.Members.Add(new MethodTemplate() + { + Comments = + [ + "/// ", + $"/// {(op.Op == "*" ? "Multiplies" : "Divides")} {op.LeftTypeName} {(op.Op == "*" ? "by" : "by")} {op.RightTypeName} to produce {op.ReturnTypeName}.", + "/// ", + ], + Attributes = ["System.Diagnostics.CodeAnalysis.SuppressMessage(\"Usage\", \"CA2225:Operator overloads have named alternates\", Justification = \"Physics quantity operator\")"], + Keywords = ["public", "static", $"{op.ReturnTypeName}"], + Name = $"operator {op.Op}", + Parameters = + [ + new ParameterTemplate { Type = $"{op.LeftTypeName}", Name = "left" }, + new ParameterTemplate { Type = $"{op.RightTypeName}", Name = "right" }, + ], + BodyFactory = (body) => body.Write(bodyExpr), + }); + } + + private static void EmitVectorCrossDimOperators( + CodeBlocker cb, + string ownerTypeName, + string ownerFullType, + string[] components, + Dictionary> operatorsByOwner, + Dictionary typeFormMap) + { + if (!operatorsByOwner.TryGetValue(ownerTypeName, out List? ops)) + { + return; + } + + foreach (OperatorInfo op in ops) + { + int leftForm = GetFormOrDefault(typeFormMap, op.LeftTypeName); + int rightForm = GetFormOrDefault(typeFormMap, op.RightTypeName); + int resultForm = GetFormOrDefault(typeFormMap, op.ReturnTypeName); + + string[] resultComponents = resultForm switch + { + 2 => ["X", "Y"], + 3 => ["X", "Y", "Z"], + 4 => ["X", "Y", "Z", "W"], + _ => [], + }; + + if (leftForm >= 2 && rightForm <= 1) + { + // VN * V0 or VN / V0 => VN result + string initExpr = string.Join(", ", resultComponents.Select(c => $"{c} = left.{c} {op.Op} right.Value")); + cb.WriteLine($"/// {op.LeftTypeName} {op.Op} {op.RightTypeName} = {op.ReturnTypeName}."); + cb.WriteLine($"public static {op.ReturnTypeName} operator {op.Op}({op.LeftTypeName} left, {op.RightTypeName} right) => new() {{ {initExpr} }};"); + cb.NewLine(); + } + else if (leftForm <= 1 && rightForm >= 2 && op.Op == "*") + { + // V0 * VN => VN result (commutative multiplication) + string initExpr = string.Join(", ", resultComponents.Select(c => $"{c} = left.Value * right.{c}")); + cb.WriteLine($"/// {op.LeftTypeName} {op.Op} {op.RightTypeName} = {op.ReturnTypeName}."); + cb.WriteLine($"public static {op.ReturnTypeName} operator {op.Op}({op.LeftTypeName} left, {op.RightTypeName} right) => new() {{ {initExpr} }};"); + cb.NewLine(); + } + else if (leftForm <= 1 && rightForm <= 1) + { + // Both V0/V1 - use Multiply/Divide. But wait, this owner is a VN type. + // This shouldn't happen because VN types don't get V0*V0 operators assigned. + // Skip. + } + } + } + + #endregion + + #region Vector Generation Helpers (reused from original) + + private static void WriteVectorComponentProperties(CodeBlocker cb, string[] components) + { + foreach (string comp in components) + { + cb.WriteLine($"/// Gets the {comp} component."); + cb.WriteLine($"public T {comp} {{ get; init; }}"); + cb.NewLine(); + } + } + + private static void WriteVectorStaticProperties(CodeBlocker cb, string fullType, string[] components) + { + string zeroInit = string.Join(", ", components.Select(c => $"{c} = T.Zero")); + cb.WriteLine("/// Gets a vector with all components set to zero."); + cb.WriteLine($"public static {fullType} Zero => new() {{ {zeroInit} }};"); + cb.NewLine(); + + string oneInit = string.Join(", ", components.Select(c => $"{c} = T.One")); + cb.WriteLine("/// Gets a vector with all components set to one."); + cb.WriteLine($"public static {fullType} One => new() {{ {oneInit} }};"); + cb.NewLine(); + + foreach (string comp in components) + { + string unitInit = string.Join(", ", components.Select(c => $"{c} = {(c == comp ? "T.One" : "T.Zero")}")); + cb.WriteLine($"/// Gets the unit vector for the {comp}-axis."); + cb.WriteLine($"public static {fullType} Unit{comp} => new() {{ {unitInit} }};"); + cb.NewLine(); + } + } + + private static void WriteVectorMethods(CodeBlocker cb, string fullType, string[] components, int dims) + { + string sumOfSquares = string.Join(" + ", components.Select(c => $"({c} * {c})")); + + cb.WriteLine("/// Calculates the length of the vector."); + cb.WriteLine("public T Length()"); + using (new Scope(cb)) + { + cb.WriteLine($"T sum = {sumOfSquares};"); + cb.WriteLine("double asDouble = double.CreateChecked(sum);"); + cb.WriteLine("return T.CreateChecked(Math.Sqrt(asDouble));"); + } + + cb.NewLine(); + + cb.WriteLine("/// Calculates the squared length of the vector."); + cb.WriteLine($"public T LengthSquared() => {sumOfSquares};"); + cb.NewLine(); + + string dotExpr = string.Join(" + ", components.Select(c => $"({c} * other.{c})")); + cb.WriteLine("/// Calculates the dot product of two vectors."); + cb.WriteLine($"public T Dot({fullType} other) => {dotExpr};"); + cb.NewLine(); + + if (dims == 3) + { + cb.WriteLine("/// Calculates the cross product of two vectors."); + cb.WriteLine($"public {fullType} Cross({fullType} other)"); + using (new Scope(cb)) + { + cb.WriteLine($"return new() {{ X = (Y * other.Z) - (Z * other.Y), Y = (Z * other.X) - (X * other.Z), Z = (X * other.Y) - (Y * other.X) }};"); + } + + cb.NewLine(); + } + + cb.WriteLine("/// Calculates the distance between two vectors."); + cb.WriteLine($"public T Distance({fullType} other)"); + using (new Scope(cb)) + { + foreach (string comp in components) + { + cb.WriteLine($"T d{comp} = {comp} - other.{comp};"); + } + + string distSum = string.Join(" + ", components.Select(c => $"(d{c} * d{c})")); + cb.WriteLine($"T sum = {distSum};"); + cb.WriteLine("double asDouble = double.CreateChecked(sum);"); + cb.WriteLine("return T.CreateChecked(Math.Sqrt(asDouble));"); + } + + cb.NewLine(); + + cb.WriteLine("/// Calculates the squared distance between two vectors."); + cb.WriteLine($"public T DistanceSquared({fullType} other)"); + using (new Scope(cb)) + { + foreach (string comp in components) + { + cb.WriteLine($"T d{comp} = {comp} - other.{comp};"); + } + + string distSqSum = string.Join(" + ", components.Select(c => $"(d{c} * d{c})")); + cb.WriteLine($"return {distSqSum};"); + } + + cb.NewLine(); + + cb.WriteLine("/// Returns a normalized version of the vector."); + cb.WriteLine($"public {fullType} Normalize()"); + using (new Scope(cb)) + { + cb.WriteLine("T len = Length();"); + string normInit = string.Join(", ", components.Select(c => $"{c} = {c} / len")); + cb.WriteLine($"return new() {{ {normInit} }};"); + } + + cb.NewLine(); + } + + private static void WriteVectorOperators(CodeBlocker cb, string fullType, string[] components) + { + string addInit = string.Join(", ", components.Select(c => $"{c} = left.{c} + right.{c}")); + cb.WriteLine("/// Adds two vectors."); + cb.WriteLine($"public static {fullType} operator +({fullType} left, {fullType} right) => new() {{ {addInit} }};"); + cb.NewLine(); + + string subInit = string.Join(", ", components.Select(c => $"{c} = left.{c} - right.{c}")); + cb.WriteLine("/// Subtracts two vectors."); + cb.WriteLine($"public static {fullType} operator -({fullType} left, {fullType} right) => new() {{ {subInit} }};"); + cb.NewLine(); + + string mulInit = string.Join(", ", components.Select(c => $"{c} = vector.{c} * scalar")); + cb.WriteLine("/// Multiplies a vector by a scalar."); + cb.WriteLine($"public static {fullType} operator *({fullType} vector, T scalar) => new() {{ {mulInit} }};"); + cb.NewLine(); + + string mulRevInit = string.Join(", ", components.Select(c => $"{c} = scalar * vector.{c}")); + cb.WriteLine("/// Multiplies a scalar by a vector."); + cb.WriteLine($"public static {fullType} operator *(T scalar, {fullType} vector) => new() {{ {mulRevInit} }};"); + cb.NewLine(); + + string divInit = string.Join(", ", components.Select(c => $"{c} = vector.{c} / scalar")); + cb.WriteLine("/// Divides a vector by a scalar."); + cb.WriteLine($"public static {fullType} operator /({fullType} vector, T scalar) => new() {{ {divInit} }};"); + cb.NewLine(); + + string negInit = string.Join(", ", components.Select(c => $"{c} = -vector.{c}")); + cb.WriteLine("/// Negates a vector."); + cb.WriteLine($"public static {fullType} operator -({fullType} vector) => new() {{ {negInit} }};"); + } + + #endregion + + #region Helper Methods + + private static VectorFormDefinition? GetFormDef(PhysicalDimension dim, int form) => form switch + { + 0 => dim.Quantities.Vector0, + 1 => dim.Quantities.Vector1, + 2 => dim.Quantities.Vector2, + 3 => dim.Quantities.Vector3, + 4 => dim.Quantities.Vector4, + _ => null, + }; + + private static string? GetBaseTypeName(PhysicalDimension dim, int form) => GetFormDef(dim, form)?.Base; + + private static int GetFormOrDefault(Dictionary map, string key) + { + if (map.TryGetValue(key, out int value)) + { + return value; + } + + return -1; + } + + #endregion + + #region Internal Types + + private sealed class OperatorInfo(string op, string leftTypeName, string rightTypeName, string returnTypeName, string ownerTypeName) + { + public string Op { get; } = op; + public string LeftTypeName { get; } = leftTypeName; + public string RightTypeName { get; } = rightTypeName; + public string ReturnTypeName { get; } = returnTypeName; + public string OwnerTypeName { get; } = ownerTypeName; + } + + private sealed class ProductInfo(string method, string selfTypeName, string otherTypeName, string returnTypeName, int vectorForm) + { + public string Method { get; } = method; + public string SelfTypeName { get; } = selfTypeName; + public string OtherTypeName { get; } = otherTypeName; + public string ReturnTypeName { get; } = returnTypeName; + public int VectorForm { get; } = vectorForm; + } + + #endregion +} diff --git a/Semantics.SourceGenerators/Generators/UnitsGenerator.cs b/Semantics.SourceGenerators/Generators/UnitsGenerator.cs new file mode 100644 index 0000000..a42da8f --- /dev/null +++ b/Semantics.SourceGenerators/Generators/UnitsGenerator.cs @@ -0,0 +1,259 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using ktsu.CodeBlocker; +using Microsoft.CodeAnalysis; +using Semantics.SourceGenerators.Models; +using Semantics.SourceGenerators.Templates; + +/// +/// Source generator that creates the per-unit record types and the static Units +/// catalogue from units.json, cross-referenced with dimensions.json so each unit can +/// be tagged with its per-dimension marker interface. +/// +/// +/// Each emitted unit implements plus +/// the I{Dim}Unit marker(s) emitted by , so +/// generated quantities can accept dimensionally-correct units only at compile time. +/// +[Generator] +public class UnitsGenerator : GeneratorBase +{ + public UnitsGenerator() : base("units.json") { } + + private sealed class CombinedMetadata + { + public UnitsMetadata Units { get; } + public DimensionsMetadata Dimensions { get; } + + public CombinedMetadata(UnitsMetadata units, DimensionsMetadata dimensions) + { + Units = units; + Dimensions = dimensions; + } + } + + public override void Initialize(IncrementalGeneratorInitializationContext context) + { + IncrementalValueProvider unitsProvider = LoadJson(context, "units.json"); + IncrementalValueProvider dimensionsProvider = LoadJson(context, "dimensions.json"); + IncrementalValueProvider combined = unitsProvider.Combine(dimensionsProvider).Select(static (pair, _) => + pair.Left == null ? null : new CombinedMetadata(pair.Left, pair.Right ?? new DimensionsMetadata())); + + context.RegisterSourceOutput(combined, (ctx, metadata) => + { + if (metadata == null) + { + return; + } + + using CodeBlocker codeBlocker = CodeBlocker.Create(); + GenerateInner(ctx, metadata.Units, metadata.Dimensions, codeBlocker); + }); + } + + private static IncrementalValueProvider LoadJson(IncrementalGeneratorInitializationContext context, string filename) + where TMeta : class + { + return context.AdditionalTextsProvider + .Where(file => file.Path.EndsWith(filename, StringComparison.InvariantCulture)) + .Select((file, ct) => file.GetText(ct)?.ToString() ?? "") + .Where(content => !string.IsNullOrEmpty(content)) + .Select((content, _) => + { + try + { + return JsonSerializer.Deserialize(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + } + catch (JsonException) + { + return null; + } + }) + .Where(m => m != null) + .Collect() + .Select((arr, _) => arr.FirstOrDefault()); + } + + protected override void Generate(SourceProductionContext context, UnitsMetadata metadata, CodeBlocker codeBlocker) + => GenerateInner(context, metadata, new DimensionsMetadata(), codeBlocker); + + private static void GenerateInner(SourceProductionContext context, UnitsMetadata units, DimensionsMetadata dimensions, CodeBlocker codeBlocker) + { + Dictionary> unitToDimensions = []; + foreach (PhysicalDimension dim in dimensions.PhysicalDimensions ?? []) + { + foreach (string unitName in dim.AvailableUnits ?? []) + { + if (!unitToDimensions.TryGetValue(unitName, out List? list)) + { + list = []; + unitToDimensions[unitName] = list; + } + + list.Add(dim.Name); + } + } + + SourceFileTemplate sourceFileTemplate = new() + { + FileName = "Units.g.cs", + Namespace = "ktsu.Semantics.Quantities.Units", + Usings = + [ + "ktsu.Semantics.Quantities", + "static ktsu.Semantics.Quantities.Units.ConversionConstants", + ], + }; + + List catalogueUnitNames = []; + + foreach (UnitCategory category in units.UnitCategories) + { + foreach (UnitDefinition unit in category.Units) + { + List dims = unitToDimensions.TryGetValue(unit.Name, out List? d) ? d : []; + + List interfaces = ["IUnit"]; + foreach (string dimName in dims) + { + interfaces.Add($"I{dimName}Unit"); + } + + string factorExpr = BuildToBaseFactorExpression(unit); + string offsetExpr = string.IsNullOrEmpty(unit.Offset) || unit.Offset == "0" + ? "0d" + : unit.Offset; + string dimensionExpr = dims.Count > 0 + ? $"PhysicalDimensions.{dims[0]}" + : "null!"; + + ClassTemplate unitClass = new() + { + Comments = + [ + "/// ", + $"/// {unit.Description}", + "/// ", + ], + Keywords = ["public", "sealed", "record"], + Name = unit.Name, + Interfaces = interfaces, + Members = + [ + new ConstructorTemplate() + { + Comments = ["/// Initializes a new instance of the unit."], + Keywords = ["public"], + Name = unit.Name, + }, + new FieldTemplate() + { + Comments = ["/// Gets the full name of the unit."], + Keywords = ["public", "string"], + Name = $"Name => \"{unit.Name}\"", + }, + new FieldTemplate() + { + Comments = ["/// Gets the symbol/abbreviation of the unit."], + Keywords = ["public", "string"], + Name = $"Symbol => \"{unit.Symbol}\"", + }, + new FieldTemplate() + { + Comments = ["/// Gets the unit system this unit belongs to."], + Keywords = ["public", "UnitSystem"], + Name = $"System => UnitSystem.{unit.System}", + }, + new FieldTemplate() + { + Comments = ["/// Gets the physical dimension this unit measures."], + Keywords = ["public", "DimensionInfo"], + Name = $"Dimension => {dimensionExpr}", + }, + new FieldTemplate() + { + Comments = ["/// Gets the multiplication factor used in the to-base affine conversion."], + Keywords = ["public", "double"], + Name = $"ToBaseFactor => {factorExpr}", + }, + new FieldTemplate() + { + Comments = ["/// Gets the additive offset used in the to-base affine conversion."], + Keywords = ["public", "double"], + Name = $"ToBaseOffset => {offsetExpr}", + }, + ], + }; + + sourceFileTemplate.Classes.Add(unitClass); + catalogueUnitNames.Add(unit.Name); + } + } + + // Emit the static Units catalogue with one singleton per unit. + ClassTemplate unitsCatalogue = new() + { + Comments = + [ + "/// ", + "/// Static catalogue exposing one singleton per declared unit. Generated quantity", + "/// types accept these on their typed In(...) methods.", + "/// ", + ], + Keywords = ["public", "static", "class"], + Name = "Units", + }; + + foreach (string unitName in catalogueUnitNames.OrderBy(n => n, StringComparer.Ordinal)) + { + unitsCatalogue.Members.Add(new FieldTemplate() + { + Comments = [$"/// Singleton {unitName} instance."], + Keywords = ["public", "static", "readonly", unitName], + Name = unitName, + DefaultValue = $"new {unitName}()", + }); + } + + sourceFileTemplate.Classes.Add(unitsCatalogue); + + WriteSourceFileTo(codeBlocker, sourceFileTemplate); + context.AddSource(sourceFileTemplate.FileName, codeBlocker.ToString()); + } + + /// + /// Builds the literal-double expression for the unit's to-base multiplication factor. + /// Folds Magnitude (metric prefix) and ConversionFactor (named constant) + /// together so the runtime IUnit.ToBase default implementation sees a single scalar. + /// + private static string BuildToBaseFactorExpression(UnitDefinition unit) + { + bool hasMagnitude = !string.IsNullOrEmpty(unit.Magnitude) && unit.Magnitude != "1"; + bool hasFactor = !string.IsNullOrEmpty(unit.ConversionFactor) && unit.ConversionFactor != "1"; + + if (hasMagnitude && hasFactor) + { + return $"MetricMagnitudes.{unit.Magnitude} * {unit.ConversionFactor}"; + } + + if (hasMagnitude) + { + return $"MetricMagnitudes.{unit.Magnitude}"; + } + + if (hasFactor) + { + return $"{unit.ConversionFactor}"; + } + + return "1d"; + } +} diff --git a/Semantics.SourceGenerators/Metadata/conversions.json b/Semantics.SourceGenerators/Metadata/conversions.json new file mode 100644 index 0000000..38ceff1 --- /dev/null +++ b/Semantics.SourceGenerators/Metadata/conversions.json @@ -0,0 +1,570 @@ +{ + "conversions": [ + { + "category": "Length", + "description": "Length unit conversion factors", + "factors": [ + { + "name": "FeetToMeters", + "description": "Foot to meter conversion: 0.3048 m/ft (exact by definition)", + "value": "0.3048" + }, + { + "name": "InchesToMeters", + "description": "Inch to meter conversion: 0.0254 m/in (exact by definition)", + "value": "0.0254" + }, + { + "name": "YardToMeters", + "description": "Yard to meter conversion: 0.9144 m/yd (exact by definition)", + "value": "0.9144" + }, + { + "name": "MileToMeters", + "description": "Mile to meter conversion: 1609.344 m/mi (exact by definition)", + "value": "1609.344" + }, + { + "name": "AngstromToMeters", + "description": "Angstrom to meter conversion: 1e-10 m/Å (exact by definition)", + "value": "1e-10" + }, + { + "name": "NauticalMileToMeters", + "description": "Nautical mile to meter conversion: 1852 m/nmi (exact by definition)", + "value": "1852" + } + ] + }, + { + "category": "Mass", + "description": "Mass unit conversion factors", + "factors": [ + { + "name": "PoundMassToKilogram", + "description": "Pound mass to kilogram: 0.453592 kg/lb (exact)", + "value": "0.453592" + }, + { + "name": "PoundToKilograms", + "description": "Pound to kilogram conversion: 0.45359237 kg/lb (exact by definition)", + "value": "0.45359237" + }, + { + "name": "OunceToKilograms", + "description": "Ounce to kilogram conversion: 0.028349523125 kg/oz (exact)", + "value": "0.028349523125" + }, + { + "name": "TonToKilograms", + "description": "Metric ton to kilogram conversion: 1000 kg/t (exact by definition)", + "value": "1000" + }, + { + "name": "StoneToKilograms", + "description": "Stone to kilogram conversion: 6.35029318 kg/st (14 lb, exact)", + "value": "6.35029318" + }, + { + "name": "ShortTonToKilograms", + "description": "Short ton to kilogram conversion: 907.18474 kg/ton (2000 lb, exact)", + "value": "907.18474" + }, + { + "name": "AtomicMassUnitToKilograms", + "description": "Atomic mass unit to kilogram: 1.66053906660e-27 kg/u (2018 CODATA)", + "value": "1.66053906660e-27" + } + ] + }, + { + "category": "Volume", + "description": "Volume unit conversion factors", + "factors": [ + { + "name": "LiterToCubicMeters", + "description": "Liter to cubic meter conversion: 0.001 m³/L (exact by definition)", + "value": "0.001" + }, + { + "name": "GallonToCubicMeters", + "description": "US gallon to cubic meter conversion: 0.003785411784 m³/gal (exact)", + "value": "0.003785411784" + }, + { + "name": "CubicCentimeterToCubicMeters", + "description": "Cubic centimeter to cubic meter: 1e-6 m³/cm³ (exact by definition)", + "value": "1e-6" + }, + { + "name": "CubicFootToCubicMeters", + "description": "Cubic foot to cubic meter: 0.028316846592 m³/ft³ (exact)", + "value": "0.028316846592" + }, + { + "name": "CubicInchToCubicMeters", + "description": "Cubic inch to cubic meter: 1.6387064e-5 m³/in³ (exact)", + "value": "1.6387064e-5" + }, + { + "name": "ImperialGallonToCubicMeters", + "description": "Imperial gallon to cubic meter: 0.00454609 m³/imp gal (exact by definition)", + "value": "0.00454609" + }, + { + "name": "USQuartToCubicMeters", + "description": "US liquid quart to cubic meter: 0.000946352946 m³/qt (exact)", + "value": "0.000946352946" + }, + { + "name": "USPintToCubicMeters", + "description": "US liquid pint to cubic meter: 0.000473176473 m³/pt (exact)", + "value": "0.000473176473" + }, + { + "name": "USFluidOunceToCubicMeters", + "description": "US fluid ounce to cubic meter: 2.95735295625e-5 m³/fl oz (exact)", + "value": "2.95735295625e-5" + } + ] + }, + { + "category": "Time", + "description": "Time unit conversion factors", + "factors": [ + { + "name": "MinuteToSeconds", + "description": "Minute to second conversion: 60 s/min (exact)", + "value": "60" + }, + { + "name": "HourToSeconds", + "description": "Hour to second conversion: 3600 s/h (exact)", + "value": "3600" + }, + { + "name": "DayToSeconds", + "description": "Day to second conversion: 86400 s/day (exact)", + "value": "86400" + }, + { + "name": "YearToSeconds", + "description": "Year to second conversion: 31557600 s/year (365.25 days, exact)", + "value": "31557600" + }, + { + "name": "WeekToSeconds", + "description": "Week to second conversion: 604800 s/wk (exact)", + "value": "604800" + } + ] + }, + { + "category": "Temperature", + "description": "Temperature conversion factors and offsets", + "factors": [ + { + "name": "CelsiusToKelvinOffset", + "description": "Celsius to Kelvin temperature offset: 273.15 K (exact by definition)", + "value": "273.15" + }, + { + "name": "FahrenheitScale", + "description": "Fahrenheit-to-Kelvin degree scale factor: 5/9 (exact)", + "value": "0.5555555555555556" + }, + { + "name": "FahrenheitToKelvinOffset", + "description": "Fahrenheit to Kelvin affine offset: 459.67 × 5/9 ≈ 255.372 K (exact)", + "value": "255.37222222222223" + } + ] + }, + { + "category": "Angular", + "description": "Angular unit conversion factors", + "factors": [ + { + "name": "DegreeToRadians", + "description": "Degree to radian conversion: π/180 rad/° (exact)", + "value": "0.017453292519943295769236907684886127134428718885417254560971914401710091146034494436822415696345097379101040706699150667990539631694451077627806983" + }, + { + "name": "GradianToRadians", + "description": "Gradian to radian conversion: π/200 rad/grad (exact)", + "value": "0.015707963267948966" + }, + { + "name": "RevolutionToRadians", + "description": "Revolution to radian conversion: 2π rad/rev (exact)", + "value": "6.283185307179586" + } + ] + }, + { + "category": "Energy", + "description": "Energy unit conversion factors", + "factors": [ + { + "name": "CalorieToJoules", + "description": "Calorie to joule conversion: 4.184 J/cal (exact, thermochemical calorie)", + "value": "4.184" + }, + { + "name": "KilowattHourToJoules", + "description": "Kilowatt-hour to joule conversion: 3600000 J/kWh (exact)", + "value": "3600000" + }, + { + "name": "HorsepowerToWatts", + "description": "Mechanical horsepower to watt conversion: 745.6998715822702 W/hp (exact)", + "value": "745.6998715822702" + }, + { + "name": "ElectronVoltToJoules", + "description": "Electron volt to joule conversion: 1.602176634e-19 J/eV (exact, based on elementary charge)", + "value": "1.602176634e-19" + }, + { + "name": "KilocalorieToJoules", + "description": "Kilocalorie to joule conversion: 4184 J/kcal (exact, thermochemical)", + "value": "4184" + }, + { + "name": "WattHourToJoules", + "description": "Watt-hour to joule conversion: 3600 J/Wh (exact)", + "value": "3600" + }, + { + "name": "ErgToJoules", + "description": "Erg to joule conversion: 1e-7 J/erg (exact by definition)", + "value": "1e-7" + }, + { + "name": "BtuToJoules", + "description": "British thermal unit (IT) to joule conversion: 1055.05585262 J/BTU (exact)", + "value": "1055.05585262" + } + ] + }, + { + "category": "Pressure", + "description": "Pressure unit conversion factors", + "factors": [ + { + "name": "BarToPascals", + "description": "Bar to pascal conversion: 100000 Pa/bar (exact by definition)", + "value": "100000" + }, + { + "name": "AtmosphereToPascals", + "description": "Atmosphere to pascal conversion: 101325 Pa/atm (exact by definition)", + "value": "101325" + }, + { + "name": "PsiToPascals", + "description": "PSI to pascal conversion: 6894.757293168361 Pa/psi (exact)", + "value": "6894.757293168361" + }, + { + "name": "TorrToPascals", + "description": "Torr to pascal conversion: 101325/760 ≈ 133.322 Pa/Torr (exact)", + "value": "133.32236842105263" + } + ] + }, + { + "category": "Area", + "description": "Area unit conversion factors", + "factors": [ + { + "name": "SquareFootToSquareMeters", + "description": "Square foot to square meter conversion: 0.09290304 m²/ft² (exact)", + "value": "0.09290304" + }, + { + "name": "SquareInchToSquareMeters", + "description": "Square inch to square meter conversion: 0.00064516 m²/in² (exact)", + "value": "0.00064516" + }, + { + "name": "BarnToSquareMeters", + "description": "Barn to square meter conversion: 1e-28 m² (exact by definition)", + "value": "1e-28" + }, + { + "name": "SquareKilometerToSquareMeters", + "description": "Square kilometer to square meter: 1e6 m²/km² (exact by definition)", + "value": "1e6" + }, + { + "name": "SquareCentimeterToSquareMeters", + "description": "Square centimeter to square meter: 1e-4 m²/cm² (exact by definition)", + "value": "1e-4" + }, + { + "name": "SquareMileToSquareMeters", + "description": "Square mile to square meter: 2589988.110336 m²/mi² (exact)", + "value": "2589988.110336" + }, + { + "name": "HectareToSquareMeters", + "description": "Hectare to square meter: 10000 m²/ha (exact by definition)", + "value": "10000" + }, + { + "name": "AcreToSquareMeters", + "description": "Acre to square meter: 4046.8564224 m²/ac (exact)", + "value": "4046.8564224" + } + ] + }, + { + "category": "Velocity", + "description": "Velocity unit conversion factors", + "factors": [ + { + "name": "KilometerPerHourToMeterPerSecond", + "description": "Kilometers per hour to meters per second conversion: 0.2777777777777778 m/s per km/h (exact)", + "value": "0.2777777777777778" + }, + { + "name": "MilePerHourToMeterPerSecond", + "description": "Miles per hour to meters per second conversion: 0.44704 m/s per mph (exact)", + "value": "0.44704" + }, + { + "name": "FootPerSecondToMeterPerSecond", + "description": "Feet per second to meters per second: 0.3048 m/s per ft/s (exact)", + "value": "0.3048" + }, + { + "name": "KnotToMeterPerSecond", + "description": "Knot to meters per second: 1852/3600 ≈ 0.514444 m/s per kn (exact)", + "value": "0.5144444444444445" + } + ] + }, + { + "category": "AngularVelocity", + "description": "Angular velocity unit conversion factors", + "factors": [ + { + "name": "RevolutionPerMinuteToRadianPerSecond", + "description": "RPM to rad/s conversion: π/30 rad/s per rpm (exact)", + "value": "0.10471975511965977" + } + ] + }, + { + "category": "Torque", + "description": "Torque unit conversion factors", + "factors": [ + { + "name": "PoundFootToNewtonMeters", + "description": "Pound-foot to Newton-meter conversion: 1.3558179483314004 N⋅m per lb⋅ft (exact)", + "value": "1.3558179483314004" + } + ] + }, + { + "category": "Concentration", + "description": "Concentration unit conversion factors", + "factors": [ + { + "name": "MolarToCubicMeter", + "description": "Molar to cubic meter concentration conversion: 1000.0 mol/m³ per mol/L (exact)", + "value": "1000.0" + }, + { + "name": "MillimolarToMolePerCubicMeter", + "description": "Millimolar to mole per cubic meter: 1 mol/m³ per mM (exact)", + "value": "1.0" + }, + { + "name": "MicromolarToMolePerCubicMeter", + "description": "Micromolar to mole per cubic meter: 0.001 mol/m³ per μM (exact)", + "value": "0.001" + } + ] + }, + { + "category": "FluidMechanics", + "description": "Fluid mechanics unit conversion factors", + "factors": [ + { + "name": "StokesToSquareMeterPerSecond", + "description": "Stokes to square meter per second: 1e-4 m²/s per St (exact by definition)", + "value": "1e-4" + }, + { + "name": "PoiseToPascalSecond", + "description": "Poise to pascal second: 0.1 Pa·s per P (exact by definition)", + "value": "0.1" + }, + { + "name": "LiterPerSecondToCubicMeterPerSecond", + "description": "Liter per second to cubic meter per second: 0.001 m³/s per L/s (exact by definition)", + "value": "0.001" + }, + { + "name": "CentipoiseToPascalSecond", + "description": "Centipoise to pascal second: 0.001 Pa·s per cP (exact by definition)", + "value": "0.001" + }, + { + "name": "DynePerCentimeterToNewtonPerMeter", + "description": "Dyne per centimeter to newton per meter: 0.001 N/m per dyn/cm (exact)", + "value": "0.001" + }, + { + "name": "GramPerCubicCentimeterToKilogramPerCubicMeter", + "description": "Gram per cubic centimeter to kilogram per cubic meter: 1000 kg/m³ per g/cm³ (exact)", + "value": "1000" + }, + { + "name": "GramPerLiterToKilogramPerCubicMeter", + "description": "Gram per liter to kilogram per cubic meter: 1 kg/m³ per g/L (exact)", + "value": "1.0" + } + ] + }, + { + "category": "Electromagnetism", + "description": "Electromagnetic unit conversion factors", + "factors": [ + { + "name": "GaussToTesla", + "description": "Gauss to Tesla: 1e-4 T per G (exact by definition)", + "value": "1e-4" + }, + { + "name": "AmpereHourToCoulombs", + "description": "Ampere-hour to coulomb conversion: 3600 C/Ah (exact)", + "value": "3600" + } + ] + }, + { + "category": "Chemistry", + "description": "Chemistry unit conversion factors", + "factors": [ + { + "name": "GramPerMoleToKilogramPerMole", + "description": "Gram per mole to kilogram per mole: 0.001 kg/mol per g/mol (exact by definition)", + "value": "0.001" + }, + { + "name": "KilojoulePerMoleToJoulePerMole", + "description": "Kilojoule per mole to joule per mole: 1000 J/mol per kJ/mol (exact by definition)", + "value": "1000" + }, + { + "name": "CaloriePerMoleToJoulePerMole", + "description": "Calorie per mole to joule per mole: 4.184 J/mol per cal/mol (exact, thermochemical)", + "value": "4.184" + }, + { + "name": "EnzymeUnitToKatals", + "description": "Enzyme unit (1 μmol/min) to katal: 1/6e7 ≈ 1.6667e-8 kat/U (exact)", + "value": "1.6666666666666667e-8" + } + ] + }, + { + "category": "Acceleration", + "description": "Acceleration unit conversion factors", + "factors": [ + { + "name": "StandardGravityToMeterPerSecondSquared", + "description": "Standard gravity to meters per second squared: 9.80665 m/s² per g (exact by definition)", + "value": "9.80665" + } + ] + }, + { + "category": "Force", + "description": "Force unit conversion factors", + "factors": [ + { + "name": "DyneToNewtons", + "description": "Dyne to newton conversion: 1e-5 N/dyn (exact by definition)", + "value": "1e-5" + }, + { + "name": "PoundForceToNewtons", + "description": "Pound-force to newton conversion: 4.4482216152605 N/lbf (exact)", + "value": "4.4482216152605" + } + ] + }, + { + "category": "Radiation", + "description": "Radiological unit conversion factors", + "factors": [ + { + "name": "CurieToBecquerels", + "description": "Curie to becquerel conversion: 3.7e10 Bq/Ci (exact by definition)", + "value": "3.7e10" + }, + { + "name": "RadToGrays", + "description": "Rad to gray conversion: 0.01 Gy/rad (exact by definition)", + "value": "0.01" + }, + { + "name": "RemToSieverts", + "description": "Rem to sievert conversion: 0.01 Sv/rem (exact by definition)", + "value": "0.01" + }, + { + "name": "RoentgenToCoulombsPerKilogram", + "description": "Roentgen to coulomb per kilogram: 2.58e-4 C/kg per R (exact by definition)", + "value": "2.58e-4" + } + ] + }, + { + "category": "Photometry", + "description": "Photometric unit conversion factors", + "factors": [ + { + "name": "FootCandleToLux", + "description": "Foot-candle to lux conversion: 1/0.09290304 ≈ 10.7639 lx/fc (exact)", + "value": "10.763910416709722" + }, + { + "name": "FootLambertToCandelaPerSquareMeter", + "description": "Foot-lambert to candela per square meter: 1/(π·0.09290304) ≈ 3.4263 cd/m² per fL (exact)", + "value": "3.4262590996353905" + } + ] + }, + { + "category": "Ratio", + "description": "Dimensionless ratio conversion factors", + "factors": [ + { + "name": "PercentToRatio", + "description": "Percent to ratio: 0.01 (exact by definition)", + "value": "0.01" + }, + { + "name": "PartPerMillionToRatio", + "description": "Parts per million to ratio: 1e-6 (exact by definition)", + "value": "1e-6" + }, + { + "name": "PartPerBillionToRatio", + "description": "Parts per billion to ratio: 1e-9 (exact by definition)", + "value": "1e-9" + }, + { + "name": "PercentByWeightToRatio", + "description": "Percent by weight to mass-fraction ratio: 0.01 (exact by definition)", + "value": "0.01" + } + ] + } + ] +} diff --git a/Semantics.SourceGenerators/Metadata/dimensions.json b/Semantics.SourceGenerators/Metadata/dimensions.json new file mode 100644 index 0000000..375bf80 --- /dev/null +++ b/Semantics.SourceGenerators/Metadata/dimensions.json @@ -0,0 +1,1377 @@ +{ + "physicalDimensions": [ + { + "name": "Dimensionless", + "symbol": "1", + "dimensionalFormula": {}, + "availableUnits": ["Dimensionless", "Radian", "Degree", "Gradian", "Revolution", "Milliradian", "Percent", "PartPerMillion", "PartPerBillion", "PercentByWeight"], + "quantities": { + "vector0": { + "base": "Ratio", + "overloads": [ + { "name": "RefractiveIndex", "description": "Ratio of speed of light in vacuum to speed in a medium." }, + { "name": "ReynoldsNumber", "description": "Ratio of inertial to viscous forces in fluid flow." }, + { "name": "SpecificGravity", "description": "Ratio of a substance's density to a reference density." }, + { "name": "MachNumber", "description": "Ratio of flow velocity to local speed of sound." }, + { "name": "SoundAbsorption", "description": "Fraction of incident sound energy absorbed by a surface." }, + { "name": "NoiseReductionCoefficient", "description": "Average sound absorption across standard frequencies." }, + { "name": "SoundTransmissionClass", "description": "Single-number rating of airborne sound insulation." }, + { "name": "Gain", "description": "Linear amplitude factor applied to a signal (1 = unity)." } + ] + }, + "vector1": { + "base": "SignedRatio", + "overloads": [ + { "name": "ReflectionCoefficient", "description": "Signed ratio of reflected to incident wave amplitude." } + ] + } + }, + "integrals": [ + { "other": "Dimensionless", "result": "Dimensionless" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Length", + "symbol": "L", + "dimensionalFormula": { "length": 1 }, + "availableUnits": [ + "Meter", "Kilometer", "Centimeter", "Millimeter", + "Micrometer", "Nanometer", "Angstrom", + "Foot", "Inch", "Yard", "Mile", "NauticalMile" + ], + "quantities": { + "vector0": { + "base": "Length", + "overloads": [ + { "name": "Width", "description": "Horizontal extent of an object or space." }, + { "name": "Height", "description": "Vertical extent of an object or space." }, + { "name": "Depth", "description": "Extent into a surface or volume." }, + { "name": "Radius", "description": "Distance from center to edge of a circle or sphere." }, + { + "name": "Diameter", + "description": "Distance across a circle or sphere through its center.", + "relationships": { "toRadius": "Value / T.CreateChecked(2)", "fromRadius": "Value * T.CreateChecked(2)" } + }, + { "name": "Distance", "description": "Separation between two points in space." }, + { "name": "Altitude", "description": "Height above a reference level." }, + { "name": "Wavelength", "description": "Spatial period of a periodic wave.", "physicalConstraints": { "minExclusive": "0" } }, + { "name": "Thickness", "description": "Extent through the thinnest dimension." }, + { "name": "Perimeter", "description": "Total boundary length of a 2D shape." } + ] + }, + "vector1": { + "base": "Displacement1D", + "overloads": [ + { "name": "Offset", "description": "Signed distance from a reference point along one axis." } + ] + }, + "vector2": { "base": "Displacement2D" }, + "vector3": { + "base": "Displacement3D", + "overloads": [ + { "name": "Position3D", "description": "Location in 3D space relative to an origin." }, + { "name": "Translation3D", "description": "Movement applied to an object in 3D space." } + ] + }, + "vector4": { "base": "Displacement4D" } + }, + "integrals": [ + { "other": "Length", "result": "Area" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Mass", + "symbol": "M", + "dimensionalFormula": { "mass": 1 }, + "availableUnits": ["Kilogram", "Gram", "Ton", "Pound", "Ounce", "Stone", "ShortTon", "AtomicMassUnit"], + "quantities": { + "vector0": { + "base": "Mass", + "overloads": [ + { "name": "AtomicMass", "description": "Mass of a single atom or molecule." } + ] + } + }, + "integrals": [ + { "other": "Acceleration", "result": "Force" }, + { "other": "Velocity", "result": "Momentum" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Time", + "symbol": "T", + "dimensionalFormula": { "time": 1 }, + "availableUnits": [ + "Second", "Millisecond", "Microsecond", + "Minute", "Hour", "Day", "Year", "Week", "Nanosecond" + ], + "quantities": { + "vector0": { + "base": "Duration", + "overloads": [ + { "name": "Period", "description": "Time interval for one complete cycle.", "physicalConstraints": { "minExclusive": "0" } }, + { "name": "HalfLife", "description": "Time for half of a substance to decay.", "physicalConstraints": { "minExclusive": "0" } }, + { "name": "TimeConstant", "description": "Characteristic response time of a system." }, + { "name": "Latency", "description": "Delay before a response begins." }, + { "name": "ReverberationTime", "description": "Time for sound to decay by 60 dB in an enclosed space." }, + { "name": "DecayTime", "description": "Time for exponential decay to a specific fraction." } + ] + } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ElectricCurrent", + "symbol": "I", + "dimensionalFormula": { "electricCurrent": 1 }, + "availableUnits": ["Ampere", "Milliampere", "Kiloampere"], + "quantities": { + "vector0": { "base": "CurrentMagnitude" }, + "vector1": { "base": "Current1D" }, + "vector3": { "base": "Current3D" } + }, + "integrals": [ + { "other": "Time", "result": "ElectricCharge" }, + { "other": "ElectricResistance", "result": "ElectricPotential" }, + { "other": "ElectricPotential", "result": "Power" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Temperature", + "symbol": "Θ", + "dimensionalFormula": { "temperature": 1 }, + "availableUnits": ["Kelvin", "Celsius", "Fahrenheit", "Rankine"], + "quantities": { + "vector0": { "base": "Temperature" }, + "vector1": { + "base": "TemperatureDelta", + "overloads": [ + { "name": "TemperatureRise", "description": "Increase in temperature." }, + { "name": "TemperatureDrop", "description": "Decrease in temperature." } + ] + } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "AmountOfSubstance", + "symbol": "N", + "dimensionalFormula": { "amountOfSubstance": 1 }, + "availableUnits": ["Mole", "Kilomole", "Millimole"], + "quantities": { + "vector0": { "base": "AmountOfSubstance" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "LuminousIntensity", + "symbol": "J", + "dimensionalFormula": { "luminousIntensity": 1 }, + "availableUnits": ["Candela", "Millicandela"], + "quantities": { + "vector0": { "base": "LuminousIntensity" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Area", + "symbol": "L²", + "dimensionalFormula": { "length": 2 }, + "availableUnits": ["SquareMeter", "SquareKilometer", "SquareCentimeter", "SquareFoot", "SquareInch", "SquareMile", "Hectare", "Acre"], + "quantities": { + "vector0": { + "base": "Area", + "overloads": [ + { "name": "SurfaceArea", "description": "Total area of the outer surface of a body." }, + { "name": "CrossSectionalArea", "description": "Area of a cross-section perpendicular to an axis." } + ] + } + }, + "integrals": [ + { "other": "Length", "result": "Volume" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "NuclearCrossSection", + "symbol": "L²", + "dimensionalFormula": { "length": 2 }, + "availableUnits": ["SquareMeter", "Barn"], + "quantities": { + "vector0": { "base": "NuclearCrossSection" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Volume", + "symbol": "L³", + "dimensionalFormula": { "length": 3 }, + "availableUnits": ["CubicMeter", "Liter", "Milliliter", "CubicCentimeter", "CubicFoot", "CubicInch", "Gallon", "ImperialGallon", "USQuart", "USPint", "USFluidOunce"], + "quantities": { + "vector0": { + "base": "Volume", + "overloads": [ + { "name": "Capacity", "description": "Maximum volume a container can hold." } + ] + } + }, + "integrals": [], + "derivatives": [ + { "other": "Length", "result": "Area" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Velocity", + "symbol": "L T⁻¹", + "dimensionalFormula": { "length": 1, "time": -1 }, + "availableUnits": [ + "MeterPerSecond", "KilometerPerHour", "MilePerHour", "FootPerSecond", "Knot" + ], + "quantities": { + "vector0": { + "base": "Speed", + "overloads": [ + { "name": "FlowSpeed", "description": "Speed of fluid flow." }, + { "name": "WindSpeed", "description": "Speed of wind movement." }, + { "name": "GroundSpeed", "description": "Speed relative to the ground." }, + { "name": "Airspeed", "description": "Speed relative to the air." }, + { "name": "SoundSpeed", "description": "Speed of sound propagation in a medium." }, + { "name": "PhaseVelocity", "description": "Speed at which a wave phase propagates." }, + { "name": "GroupVelocity", "description": "Speed at which the wave envelope propagates." } + ] + }, + "vector1": { "base": "Velocity1D" }, + "vector2": { "base": "Velocity2D" }, + "vector3": { + "base": "Velocity3D", + "overloads": [ + { "name": "WindVelocity3D", "description": "Wind velocity vector in 3D space." } + ] + }, + "vector4": { "base": "Velocity4D" } + }, + "integrals": [ + { "other": "Time", "result": "Length" } + ], + "derivatives": [ + { "other": "Time", "result": "Acceleration" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Acceleration", + "symbol": "L T⁻²", + "dimensionalFormula": { "length": 1, "time": -2 }, + "availableUnits": ["MeterPerSecondSquared", "StandardGravity"], + "quantities": { + "vector0": { + "base": "AccelerationMagnitude", + "overloads": [ + { "name": "GravitationalAcceleration", "description": "Acceleration due to gravity." } + ] + }, + "vector1": { "base": "Acceleration1D" }, + "vector2": { "base": "Acceleration2D" }, + "vector3": { + "base": "Acceleration3D", + "overloads": [ + { "name": "GravitationalField3D", "description": "Gravitational acceleration field in 3D." } + ] + }, + "vector4": { "base": "Acceleration4D" } + }, + "integrals": [ + { "other": "Time", "result": "Velocity" } + ], + "derivatives": [ + { "other": "Time", "result": "Jerk" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Jerk", + "symbol": "L T⁻³", + "dimensionalFormula": { "length": 1, "time": -3 }, + "availableUnits": ["MeterPerSecondCubed"], + "quantities": { + "vector0": { "base": "JerkMagnitude" }, + "vector1": { "base": "Jerk1D" }, + "vector2": { "base": "Jerk2D" }, + "vector3": { "base": "Jerk3D" }, + "vector4": { "base": "Jerk4D" } + }, + "integrals": [ + { "other": "Time", "result": "Acceleration" } + ], + "derivatives": [ + { "other": "Time", "result": "Snap" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Snap", + "symbol": "L T⁻⁴", + "dimensionalFormula": { "length": 1, "time": -4 }, + "availableUnits": ["MeterPerSecondQuartic"], + "quantities": { + "vector0": { "base": "SnapMagnitude" }, + "vector1": { "base": "Snap1D" }, + "vector2": { "base": "Snap2D" }, + "vector3": { "base": "Snap3D" }, + "vector4": { "base": "Snap4D" } + }, + "integrals": [ + { "other": "Time", "result": "Jerk" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Frequency", + "symbol": "T⁻¹", + "dimensionalFormula": { "time": -1 }, + "availableUnits": ["Hertz", "Kilohertz", "Megahertz"], + "quantities": { + "vector0": { + "base": "Frequency", + "overloads": [ + { "name": "SamplingRate", "description": "Rate at which samples are taken." }, + { "name": "ClockSpeed", "description": "Operating frequency of a processor." }, + { "name": "Bandwidth", "description": "Range of frequencies in a signal." }, + { "name": "Pitch", "description": "Perceived fundamental frequency of a sound." } + ] + } + }, + "integrals": [ + { "other": "Time", "result": "Dimensionless" }, + { "other": "Length", "result": "Velocity" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "RadioactiveActivity", + "symbol": "T⁻¹", + "dimensionalFormula": { "time": -1 }, + "availableUnits": ["Becquerel", "Curie"], + "quantities": { + "vector0": { "base": "RadioactiveActivity" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "AngularDisplacement", + "symbol": "1", + "dimensionalFormula": {}, + "availableUnits": ["Radian", "Degree", "Gradian", "Revolution", "Milliradian"], + "quantities": { + "vector0": { + "base": "Angle", + "overloads": [ + { "name": "FieldOfView", "description": "Angular extent of observable area." }, + { "name": "ApertureAngle", "description": "Opening angle of a cone or lens." } + ] + }, + "vector1": { + "base": "SignedAngle", + "overloads": [ + { "name": "Rotation", "description": "Amount of angular rotation applied." }, + { "name": "Phase", "description": "Phase angle of a periodic signal." }, + { "name": "Bearing", "description": "Direction relative to a reference." }, + { "name": "Heading", "description": "Direction of travel or orientation." } + ] + }, + "vector3": { "base": "AngularDisplacement3D" } + }, + "integrals": [], + "derivatives": [ + { "other": "Time", "result": "AngularVelocity" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "AngularVelocity", + "symbol": "T⁻¹", + "dimensionalFormula": { "time": -1 }, + "availableUnits": ["RadianPerSecond", "RevolutionPerMinute"], + "quantities": { + "vector0": { "base": "AngularSpeed" }, + "vector1": { "base": "AngularVelocity1D" }, + "vector3": { "base": "AngularVelocity3D" } + }, + "integrals": [ + { "other": "Time", "result": "AngularDisplacement" } + ], + "derivatives": [ + { "other": "Time", "result": "AngularAcceleration" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "AngularAcceleration", + "symbol": "T⁻²", + "dimensionalFormula": { "time": -2 }, + "availableUnits": ["RadianPerSecondSquared"], + "quantities": { + "vector0": { "base": "AngularAccelerationMagnitude" }, + "vector1": { "base": "AngularAcceleration1D" }, + "vector3": { "base": "AngularAcceleration3D" } + }, + "integrals": [ + { "other": "Time", "result": "AngularVelocity" } + ], + "derivatives": [ + { "other": "Time", "result": "AngularJerk" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "AngularJerk", + "symbol": "T⁻³", + "dimensionalFormula": { "time": -3 }, + "availableUnits": ["RadianPerSecondCubed"], + "quantities": { + "vector0": { "base": "AngularJerkMagnitude" }, + "vector1": { "base": "AngularJerk1D" }, + "vector3": { "base": "AngularJerk3D" } + }, + "integrals": [ + { "other": "Time", "result": "AngularAcceleration" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Torque", + "symbol": "M L² T⁻²", + "dimensionalFormula": { "mass": 1, "length": 2, "time": -2 }, + "availableUnits": ["NewtonMeter", "PoundFoot"], + "quantities": { + "vector0": { "base": "TorqueMagnitude" }, + "vector1": { "base": "Torque1D" }, + "vector3": { "base": "Torque3D" } + }, + "integrals": [ + { "other": "AngularDisplacement", "result": "Energy" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "AngularMomentum", + "symbol": "M L² T⁻¹", + "dimensionalFormula": { "mass": 1, "length": 2, "time": -1 }, + "availableUnits": ["KilogramMeterSquaredPerSecond"], + "quantities": { + "vector0": { "base": "AngularMomentumMagnitude" }, + "vector1": { "base": "AngularMomentum1D" }, + "vector3": { "base": "AngularMomentum3D" } + }, + "integrals": [], + "derivatives": [ + { "other": "Time", "result": "Torque" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "MomentOfInertia", + "symbol": "M L²", + "dimensionalFormula": { "mass": 1, "length": 2 }, + "availableUnits": ["KilogramMeterSquared"], + "quantities": { + "vector0": { "base": "MomentOfInertia" } + }, + "integrals": [ + { "other": "AngularVelocity", "result": "AngularMomentum" }, + { "other": "AngularAcceleration", "result": "Torque" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Force", + "symbol": "M L T⁻²", + "dimensionalFormula": { "mass": 1, "length": 1, "time": -2 }, + "availableUnits": ["Newton", "Kilonewton", "Dyne", "PoundForce"], + "quantities": { + "vector0": { + "base": "ForceMagnitude", + "overloads": [ + { "name": "Weight", "description": "Gravitational force on an object." }, + { "name": "Thrust", "description": "Propulsive force." }, + { "name": "Drag", "description": "Resistive force opposing motion through a medium." }, + { "name": "Lift", "description": "Force perpendicular to flow direction." }, + { "name": "Tension", "description": "Pulling force along a rope, cable, or similar." }, + { "name": "NormalForce", "description": "Contact force perpendicular to a surface." }, + { "name": "Friction", "description": "Force resisting relative motion between surfaces." } + ] + }, + "vector1": { "base": "Force1D" }, + "vector2": { "base": "Force2D" }, + "vector3": { + "base": "Force3D", + "overloads": [ + { "name": "WeightVector", "description": "Gravitational force vector." }, + { "name": "ThrustVector", "description": "Propulsive force vector." } + ] + }, + "vector4": { "base": "Force4D" } + }, + "integrals": [ + { "other": "Length", "result": "Energy" }, + { "other": "Time", "result": "Momentum" }, + { "other": "Velocity", "result": "Power" } + ], + "derivatives": [ + { "other": "Area", "result": "Pressure" } + ], + "dotProducts": [ + { "other": "Length", "result": "Energy" } + ], + "crossProducts": [ + { "other": "Length", "result": "Torque", "forms": [3] } + ] + }, + { + "name": "Momentum", + "symbol": "M L T⁻¹", + "dimensionalFormula": { "mass": 1, "length": 1, "time": -1 }, + "availableUnits": ["NewtonSecond"], + "quantities": { + "vector0": { "base": "MomentumMagnitude" }, + "vector1": { "base": "Momentum1D" }, + "vector2": { "base": "Momentum2D" }, + "vector3": { "base": "Momentum3D" }, + "vector4": { "base": "Momentum4D" } + }, + "integrals": [], + "derivatives": [ + { "other": "Time", "result": "Force" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Pressure", + "symbol": "M L⁻¹ T⁻²", + "dimensionalFormula": { "mass": 1, "length": -1, "time": -2 }, + "availableUnits": ["Pascal", "Kilopascal", "Bar", "Atmosphere", "Psi", "Torr"], + "quantities": { + "vector0": { + "base": "Pressure", + "overloads": [ + { "name": "Stress", "description": "Internal force per unit area in a material." }, + { "name": "AtmosphericPressure", "description": "Pressure exerted by the atmosphere." }, + { "name": "GaugePressure", "description": "Pressure relative to atmospheric pressure." }, + { "name": "BulkModulus", "description": "Resistance of a substance to uniform compression." }, + { "name": "YoungsModulus", "description": "Ratio of stress to strain in a material." }, + { "name": "ShearModulus", "description": "Ratio of shear stress to shear strain." }, + { "name": "SoundPressure", "description": "RMS pressure deviation caused by a sound wave." } + ] + } + }, + "integrals": [ + { "other": "Volume", "result": "Energy" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Energy", + "symbol": "M L² T⁻²", + "dimensionalFormula": { "mass": 1, "length": 2, "time": -2 }, + "availableUnits": ["Joule", "Kilojoule", "ElectronVolt", "Calorie", "Kilocalorie", "KilowattHour", "WattHour", "Erg", "Btu"], + "quantities": { + "vector0": { + "base": "Energy", + "overloads": [ + { "name": "Work", "description": "Energy transferred by a force." }, + { "name": "Heat", "description": "Energy transferred due to temperature difference." }, + { "name": "KineticEnergy", "description": "Energy of motion." }, + { "name": "PotentialEnergy", "description": "Energy of position or configuration." }, + { "name": "ThermalEnergy", "description": "Internal energy due to particle motion." } + ] + } + }, + "integrals": [], + "derivatives": [ + { "other": "Time", "result": "Power" }, + { "other": "Length", "result": "Force" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Power", + "symbol": "M L² T⁻³", + "dimensionalFormula": { "mass": 1, "length": 2, "time": -3 }, + "availableUnits": ["Watt", "Kilowatt", "Megawatt", "Horsepower"], + "quantities": { + "vector0": { + "base": "Power", + "overloads": [ + { "name": "HeatFlowRate", "description": "Rate of heat energy transfer." }, + { "name": "SoundPower", "description": "Acoustic energy emitted by a source per unit time." } + ] + } + }, + "integrals": [ + { "other": "Time", "result": "Energy" } + ], + "derivatives": [ + { "other": "ElectricPotential", "result": "ElectricCurrent" }, + { "other": "ElectricCurrent", "result": "ElectricPotential" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "AbsorbedDose", + "symbol": "L² T⁻²", + "dimensionalFormula": { "length": 2, "time": -2 }, + "availableUnits": ["Gray", "Rad"], + "quantities": { + "vector0": { "base": "AbsorbedDose" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "EquivalentDose", + "symbol": "L² T⁻²", + "dimensionalFormula": { "length": 2, "time": -2 }, + "availableUnits": ["Sievert", "Rem"], + "quantities": { + "vector0": { "base": "EquivalentDose" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Density", + "symbol": "M L⁻³", + "dimensionalFormula": { "mass": 1, "length": -3 }, + "availableUnits": ["KilogramPerCubicMeter", "GramPerCubicCentimeter", "GramPerLiter"], + "quantities": { + "vector0": { "base": "Density" } + }, + "integrals": [ + { "other": "Volume", "result": "Mass" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ElectricCharge", + "symbol": "I T", + "dimensionalFormula": { "electricCurrent": 1, "time": 1 }, + "availableUnits": ["Coulomb", "AmpereHour"], + "quantities": { + "vector0": { "base": "ChargeMagnitude" }, + "vector1": { "base": "Charge" } + }, + "integrals": [], + "derivatives": [ + { "other": "Time", "result": "ElectricCurrent" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ElectricPotential", + "symbol": "M L² T⁻³ I⁻¹", + "dimensionalFormula": { + "mass": 1, "length": 2, "time": -3, "electricCurrent": -1 + }, + "availableUnits": ["Volt", "Kilovolt"], + "quantities": { + "vector0": { + "base": "VoltageMagnitude", + "overloads": [ + { "name": "EMF", "description": "Electromotive force." }, + { "name": "VoltageDrop", "description": "Decrease in voltage across a component." } + ] + }, + "vector1": { "base": "Voltage" } + }, + "integrals": [ + { "other": "ElectricCurrent", "result": "Power" } + ], + "derivatives": [ + { "other": "Length", "result": "ElectricField" }, + { "other": "ElectricResistance", "result": "ElectricCurrent" }, + { "other": "ElectricCurrent", "result": "ElectricResistance" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ElectricField", + "symbol": "M L T⁻³ I⁻¹", + "dimensionalFormula": { + "mass": 1, "length": 1, "time": -3, "electricCurrent": -1 + }, + "availableUnits": ["VoltPerMeter"], + "quantities": { + "vector0": { "base": "ElectricFieldMagnitude" }, + "vector1": { "base": "ElectricField1D" }, + "vector2": { "base": "ElectricField2D" }, + "vector3": { "base": "ElectricField3D" } + }, + "integrals": [ + { "other": "Length", "result": "ElectricPotential" }, + { "other": "Area", "result": "ElectricFlux" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ElectricResistance", + "symbol": "M L² T⁻³ I⁻²", + "dimensionalFormula": { + "mass": 1, "length": 2, "time": -3, "electricCurrent": -2 + }, + "availableUnits": ["Ohm", "Kilohm", "Megohm"], + "quantities": { + "vector0": { + "base": "Resistance", + "overloads": [ + { "name": "Impedance", "description": "Magnitude of total opposition to alternating current." } + ] + } + }, + "integrals": [ + { "other": "ElectricCurrent", "result": "ElectricPotential" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ElectricCapacitance", + "symbol": "M⁻¹ L⁻² T⁴ I²", + "dimensionalFormula": { + "mass": -1, "length": -2, "time": 4, "electricCurrent": 2 + }, + "availableUnits": ["Farad", "Microfarad", "Nanofarad", "Picofarad"], + "quantities": { + "vector0": { "base": "Capacitance" } + }, + "integrals": [ + { "other": "ElectricPotential", "result": "ElectricCharge" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "LuminousFlux", + "symbol": "J", + "dimensionalFormula": { "luminousIntensity": 1 }, + "availableUnits": ["Lumen"], + "quantities": { + "vector0": { "base": "LuminousFlux" } + }, + "integrals": [], + "derivatives": [ + { "other": "Area", "result": "Illuminance" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Illuminance", + "symbol": "J L⁻²", + "dimensionalFormula": { "luminousIntensity": 1, "length": -2 }, + "availableUnits": ["Lux", "FootCandle"], + "quantities": { + "vector0": { "base": "Illuminance" } + }, + "integrals": [ + { "other": "Area", "result": "LuminousFlux" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "OpticalPower", + "symbol": "L⁻¹", + "dimensionalFormula": { "length": -1 }, + "availableUnits": ["Diopter"], + "quantities": { + "vector0": { "base": "OpticalPower" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Concentration", + "symbol": "N L⁻³", + "dimensionalFormula": { "amountOfSubstance": 1, "length": -3 }, + "availableUnits": ["MolePerCubicMeter", "Molar", "Millimolar", "Micromolar"], + "quantities": { + "vector0": { "base": "Concentration" } + }, + "integrals": [ + { "other": "Volume", "result": "AmountOfSubstance" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Entropy", + "symbol": "M L² T⁻² Θ⁻¹", + "dimensionalFormula": { "mass": 1, "length": 2, "time": -2, "temperature": -1 }, + "availableUnits": ["JoulePerKelvin"], + "quantities": { + "vector0": { + "base": "Entropy", + "overloads": [ + { "name": "HeatCapacity", "description": "Amount of heat required to change temperature by one unit." } + ] + } + }, + "integrals": [ + { "other": "Temperature", "result": "Energy" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "SpecificHeat", + "symbol": "L² T⁻² Θ⁻¹", + "dimensionalFormula": { "length": 2, "time": -2, "temperature": -1 }, + "availableUnits": ["JoulePerKilogramKelvin"], + "quantities": { + "vector0": { + "base": "SpecificHeat", + "overloads": [ + { "name": "SpecificEntropy", "description": "Entropy per unit mass." } + ] + } + }, + "integrals": [ + { "other": "Mass", "result": "Entropy" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ThermalConductivity", + "symbol": "M L T⁻³ Θ⁻¹", + "dimensionalFormula": { "mass": 1, "length": 1, "time": -3, "temperature": -1 }, + "availableUnits": ["WattPerMeterKelvin"], + "quantities": { + "vector0": { "base": "ThermalConductivity" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "HeatTransferCoefficient", + "symbol": "M T⁻³ Θ⁻¹", + "dimensionalFormula": { "mass": 1, "time": -3, "temperature": -1 }, + "availableUnits": ["WattPerSquareMeterKelvin"], + "quantities": { + "vector0": { "base": "HeatTransferCoefficient" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ThermalExpansion", + "symbol": "Θ⁻¹", + "dimensionalFormula": { "temperature": -1 }, + "availableUnits": ["PerKelvin"], + "quantities": { + "vector0": { + "base": "ThermalExpansionCoefficient" + } + }, + "integrals": [ + { "other": "Temperature", "result": "Dimensionless" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "KinematicViscosity", + "symbol": "L² T⁻¹", + "dimensionalFormula": { "length": 2, "time": -1 }, + "availableUnits": ["SquareMeterPerSecond", "Stokes"], + "quantities": { + "vector0": { + "base": "KinematicViscosity", + "overloads": [ + { "name": "ThermalDiffusivity", "description": "Rate of heat transfer relative to heat storage." } + ] + } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "DynamicViscosity", + "symbol": "M L⁻¹ T⁻¹", + "dimensionalFormula": { "mass": 1, "length": -1, "time": -1 }, + "availableUnits": ["PascalSecond", "Poise", "Centipoise"], + "quantities": { + "vector0": { "base": "DynamicViscosity" } + }, + "integrals": [], + "derivatives": [ + { "other": "Density", "result": "KinematicViscosity" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "VolumetricFlowRate", + "symbol": "L³ T⁻¹", + "dimensionalFormula": { "length": 3, "time": -1 }, + "availableUnits": ["CubicMeterPerSecond", "LiterPerSecond"], + "quantities": { + "vector0": { "base": "VolumetricFlowRate" } + }, + "integrals": [ + { "other": "Time", "result": "Volume" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "MassFlowRate", + "symbol": "M T⁻¹", + "dimensionalFormula": { "mass": 1, "time": -1 }, + "availableUnits": ["KilogramPerSecond"], + "quantities": { + "vector0": { "base": "MassFlowRate" } + }, + "integrals": [ + { "other": "Time", "result": "Mass" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "SurfaceTension", + "symbol": "M T⁻²", + "dimensionalFormula": { "mass": 1, "time": -2 }, + "availableUnits": ["NewtonPerMeter", "DynePerCentimeter"], + "quantities": { + "vector0": { "base": "SurfaceTension" } + }, + "integrals": [ + { "other": "Length", "result": "Force" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "MolarMass", + "symbol": "M N⁻¹", + "dimensionalFormula": { "mass": 1, "amountOfSubstance": -1 }, + "availableUnits": ["KilogramPerMole", "GramPerMole", "Dalton"], + "quantities": { + "vector0": { "base": "MolarMass" } + }, + "integrals": [ + { "other": "AmountOfSubstance", "result": "Mass" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "CatalyticActivity", + "symbol": "N T⁻¹", + "dimensionalFormula": { "amountOfSubstance": 1, "time": -1 }, + "availableUnits": ["Katal", "EnzymeUnit"], + "quantities": { + "vector0": { + "base": "CatalyticActivity", + "overloads": [ + { "name": "EnzymeActivity", "description": "Rate of enzyme-catalyzed reaction." } + ] + } + }, + "integrals": [ + { "other": "Time", "result": "AmountOfSubstance" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ReactionRate", + "symbol": "N L⁻³ T⁻¹", + "dimensionalFormula": { "amountOfSubstance": 1, "length": -3, "time": -1 }, + "availableUnits": ["MolePerCubicMeterSecond"], + "quantities": { + "vector0": { "base": "ReactionRate" } + }, + "integrals": [ + { "other": "Time", "result": "Concentration" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "MolarEnergy", + "symbol": "M L² T⁻² N⁻¹", + "dimensionalFormula": { "mass": 1, "length": 2, "time": -2, "amountOfSubstance": -1 }, + "availableUnits": ["JoulePerMole", "KilojoulePerMole", "CaloriePerMole"], + "quantities": { + "vector0": { + "base": "MolarEnergy", + "overloads": [ + { "name": "ActivationEnergy", "description": "Minimum energy for a chemical reaction to occur." }, + { "name": "MolarEnthalpy", "description": "Enthalpy change per mole of substance." } + ] + } + }, + "integrals": [ + { "other": "AmountOfSubstance", "result": "Energy" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ElectricConductance", + "symbol": "M⁻¹ L⁻² T³ I²", + "dimensionalFormula": { "mass": -1, "length": -2, "time": 3, "electricCurrent": 2 }, + "availableUnits": ["Siemens"], + "quantities": { + "vector0": { + "base": "Conductance", + "overloads": [ + { "name": "Admittance", "description": "Measure of how easily a circuit allows current flow (inverse of impedance)." } + ] + } + }, + "integrals": [ + { "other": "ElectricPotential", "result": "ElectricCurrent" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "MagneticFluxDensity", + "symbol": "M T⁻² I⁻¹", + "dimensionalFormula": { "mass": 1, "time": -2, "electricCurrent": -1 }, + "availableUnits": ["Tesla", "Gauss"], + "quantities": { + "vector0": { "base": "MagneticFluxDensityMagnitude" }, + "vector3": { "base": "MagneticFluxDensity3D" } + }, + "integrals": [ + { "other": "Area", "result": "MagneticFlux" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "MagneticFlux", + "symbol": "M L² T⁻² I⁻¹", + "dimensionalFormula": { "mass": 1, "length": 2, "time": -2, "electricCurrent": -1 }, + "availableUnits": ["Weber"], + "quantities": { + "vector0": { "base": "MagneticFlux" } + }, + "integrals": [], + "derivatives": [ + { "other": "Time", "result": "ElectricPotential" } + ], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Inductance", + "symbol": "M L² T⁻² I⁻²", + "dimensionalFormula": { "mass": 1, "length": 2, "time": -2, "electricCurrent": -2 }, + "availableUnits": ["Henry"], + "quantities": { + "vector0": { "base": "Inductance" } + }, + "integrals": [ + { "other": "ElectricCurrent", "result": "MagneticFlux" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Irradiance", + "symbol": "M T⁻³", + "dimensionalFormula": { "mass": 1, "time": -3 }, + "availableUnits": ["WattPerSquareMeter"], + "quantities": { + "vector0": { + "base": "Irradiance", + "overloads": [ + { "name": "SoundIntensity", "description": "Power per unit area carried by a sound wave." }, + { "name": "HeatFlux", "description": "Rate of heat energy transfer per unit area." }, + { "name": "EnergyFluxDensity", "description": "Rate of energy transfer per unit area." } + ] + } + }, + "integrals": [ + { "other": "Area", "result": "Power" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "AcousticImpedance", + "symbol": "M L⁻² T⁻¹", + "dimensionalFormula": { "mass": 1, "length": -2, "time": -1 }, + "availableUnits": ["PascalSecondPerMeter"], + "quantities": { + "vector0": { "base": "AcousticImpedance" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Exposure", + "symbol": "M⁻¹ T I", + "dimensionalFormula": { "mass": -1, "time": 1, "electricCurrent": 1 }, + "availableUnits": ["CoulombPerKilogram", "Roentgen"], + "quantities": { + "vector0": { "base": "Exposure" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Permittivity", + "symbol": "M⁻¹ L⁻³ T⁴ I²", + "dimensionalFormula": { + "mass": -1, "length": -3, "time": 4, "electricCurrent": 2 + }, + "availableUnits": ["FaradPerMeter"], + "quantities": { + "vector0": { "base": "Permittivity" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ElectricConductivity", + "symbol": "M⁻¹ L⁻³ T³ I²", + "dimensionalFormula": { + "mass": -1, "length": -3, "time": 3, "electricCurrent": 2 + }, + "availableUnits": ["SiemensPerMeter"], + "quantities": { + "vector0": { "base": "ElectricConductivity" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ElectricFlux", + "symbol": "M L³ T⁻³ I⁻¹", + "dimensionalFormula": { + "mass": 1, "length": 3, "time": -3, "electricCurrent": -1 + }, + "availableUnits": ["VoltMeter"], + "quantities": { + "vector0": { "base": "ElectricFlux" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ElectricPowerDensity", + "symbol": "M L⁻¹ T⁻³", + "dimensionalFormula": { + "mass": 1, "length": -1, "time": -3 + }, + "availableUnits": ["WattPerCubicMeter"], + "quantities": { + "vector0": { "base": "ElectricPowerDensity" } + }, + "integrals": [ + { "other": "Volume", "result": "Power" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Sensitivity", + "symbol": "M⁻¹ L⁻¹ T² I", + "dimensionalFormula": { + "mass": -1, "length": -1, "time": 2, "electricCurrent": 1 + }, + "availableUnits": ["VoltPerPascal"], + "quantities": { + "vector0": { "base": "Sensitivity" } + }, + "integrals": [ + { "other": "Pressure", "result": "ElectricPotential" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "ThermalResistance", + "symbol": "Θ M⁻¹ L⁻² T³", + "dimensionalFormula": { + "temperature": 1, "mass": -1, "length": -2, "time": 3 + }, + "availableUnits": ["KelvinPerWatt"], + "quantities": { + "vector0": { "base": "ThermalResistance" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "RateConstant", + "symbol": "T⁻¹", + "dimensionalFormula": { + "time": -1 + }, + "availableUnits": ["PerSecond"], + "quantities": { + "vector0": { "base": "RateConstant" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Luminance", + "symbol": "J L⁻²", + "dimensionalFormula": { + "luminousIntensity": 1, "length": -2 + }, + "availableUnits": ["CandelaPerSquareMeter", "Nit", "FootLambert"], + "quantities": { + "vector0": { "base": "Luminance" } + }, + "integrals": [ + { "other": "Area", "result": "LuminousIntensity" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Loudness", + "symbol": "1", + "dimensionalFormula": {}, + "availableUnits": ["Sone"], + "quantities": { + "vector0": { "base": "Loudness" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + }, + { + "name": "Sharpness", + "symbol": "1", + "dimensionalFormula": {}, + "availableUnits": ["Acum"], + "quantities": { + "vector0": { "base": "Sharpness" } + }, + "integrals": [], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] + } + ] +} diff --git a/Semantics.SourceGenerators/Metadata/domains.json b/Semantics.SourceGenerators/Metadata/domains.json new file mode 100644 index 0000000..00bf43d --- /dev/null +++ b/Semantics.SourceGenerators/Metadata/domains.json @@ -0,0 +1,246 @@ +{ + "domains": [ + { + "name": "Fundamental", + "description": "Basic physical quantities and constants that form the foundation of all other physics domains, including fundamental units like length, mass, time, and universal constants", + "constants": [ + { + "name": "AvogadroNumber", + "description": "Avogadro's number: 6.02214076 × 10²³ entities/mol (exact, SI defining constant)", + "value": "6.02214076e23" + }, + { + "name": "ElementaryCharge", + "description": "Elementary charge: 1.602176634 × 10⁻¹⁹ C (exact, SI defining constant)", + "value": "1.602176634e-19" + }, + { + "name": "SpeedOfLight", + "description": "Speed of light in vacuum: 299,792,458 m/s (exact, SI defining constant)", + "value": "299792458" + }, + { + "name": "PlanckConstant", + "description": "Planck constant: 6.62607015 × 10⁻³⁴ J·s (exact, SI defining constant)", + "value": "6.62607015e-34" + }, + { + "name": "BoltzmannConstant", + "description": "Boltzmann constant: 1.380649 × 10⁻²³ J/K (exact, SI defining constant)", + "value": "1.380649e-23" + }, + { + "name": "GravitationalConstant", + "description": "Gravitational constant: 6.67430 × 10⁻¹¹ m³/(kg⋅s²) (2018 CODATA)", + "value": "6.67430e-11" + }, + { + "name": "PermittivityOfFreeSpace", + "description": "Electric permittivity of free space: 8.8541878128 × 10⁻¹² F/m (exact, derived)", + "value": "8.8541878128e-12" + }, + { + "name": "PermeabilityOfFreeSpace", + "description": "Magnetic permeability of free space: 4π × 10⁻⁷ H/m (exact by definition)", + "value": "1.25663706212e-6" + }, + { + "name": "FineStructureConstant", + "description": "Fine structure constant: 7.2973525693 × 10⁻³ (dimensionless, 2018 CODATA)", + "value": "7.2973525693e-3" + } + ] + }, + { + "name": "ClassicalMechanics", + "description": "The physics of motion for macroscopic objects, covering forces, momentum, energy, rotational dynamics, and the motion of particles and rigid bodies", + "constants": [ + { + "name": "StandardGravity", + "description": "Standard gravitational acceleration: 9.80665 m/s² (exact by definition)", + "value": "9.80665" + }, + { + "name": "StandardAtmosphericPressure", + "description": "Standard atmospheric pressure: 101,325 Pa (exact by definition)", + "value": "101325" + } + ] + }, + { + "name": "FluidMechanics", + "description": "The study of fluids (liquids and gases) in motion and at rest, including fluid properties, flow dynamics, viscosity, pressure, and fluid-structure interactions", + "constants": [ + { + "name": "StandardAirDensity", + "description": "Standard air density at 15°C and 1 atm: 1.225 kg/m³ (ISO 2533)", + "value": "1.225" + }, + { + "name": "WaterSurfaceTension", + "description": "Water surface tension at 20°C: 0.0728 N/m (NIST)", + "value": "0.0728" + } + ] + }, + { + "name": "StructuralMechanics", + "description": "The analysis of structural behavior under various loads, including stress, strain, deformation, material properties, and the mechanical response of engineering structures" + }, + { + "name": "NuclearPhysics", + "description": "The study of atomic nuclei, radioactivity, nuclear reactions, decay processes, and radiation interactions with matter", + "constants": [ + { + "name": "AtomicMassUnit", + "description": "Atomic mass unit: 1.66053906660 × 10⁻²⁷ kg (2018 CODATA)", + "value": "1.66053906660e-27" + }, + { + "name": "NuclearMagneton", + "description": "Nuclear magneton: 5.0507837461 × 10⁻²⁷ J/T (2018 CODATA)", + "value": "5.0507837461e-27" + } + ] + }, + { + "name": "ParticlePhysics", + "description": "The physics of fundamental particles and their interactions, including elementary particles, high-energy phenomena, and quantum field theory applications" + }, + { + "name": "ComputationalPhysics", + "description": "Numerical methods and computational techniques for solving complex physical problems, including simulation parameters, algorithmic constants, and numerical precision requirements" + }, + { + "name": "AtmosphericPhysics", + "description": "The physics of atmospheric phenomena, including weather systems, atmospheric dynamics, meteorological quantities, and climate-related physical processes" + }, + { + "name": "Geophysics", + "description": "The physics of Earth systems, including seismic waves, gravitational fields, magnetic fields, geological processes, and Earth's internal structure" + }, + { + "name": "Astrophysics", + "description": "The physics of celestial objects and cosmic phenomena, including stellar physics, planetary motion, cosmological quantities, and space-based measurements" + }, + { + "name": "Electromagnetism", + "description": "Electric and magnetic phenomena and their interactions, including electric fields, magnetic fields, electromagnetic radiation, and electrical circuit properties" + }, + { + "name": "Thermodynamics", + "description": "The physics of heat, temperature, and energy transfer, including thermal properties, heat capacity, entropy, and thermodynamic processes", + "constants": [ + { + "name": "AbsoluteZeroInCelsius", + "description": "Absolute zero in Celsius: 273.15 K (exact by definition)", + "value": "273.15" + }, + { + "name": "WaterTriplePoint", + "description": "Water triple point: 273.16 K (exact by definition)", + "value": "273.16" + }, + { + "name": "StandardTemperature", + "description": "Standard temperature (STP): 273.15 K (0°C)", + "value": "273.15" + }, + { + "name": "WaterBoilingPoint", + "description": "Water boiling point at 1 atm: 373.15 K (100°C)", + "value": "373.15" + } + ] + }, + { + "name": "Optics", + "description": "The physics of light and optical phenomena, including electromagnetic radiation, photometry, illumination, optical properties, and light-matter interactions", + "constants": [ + { + "name": "LuminousEfficacy", + "description": "Luminous efficacy of monochromatic radiation at 540 THz: 683 lm/W (exact, SI defining constant)", + "value": "683.0" + } + ] + }, + { + "name": "AngularMechanics", + "description": "Rotational motion and angular quantities, including angular velocity, angular acceleration, torque, and moment of inertia", + "constants": [ + { + "name": "TwoPi", + "description": "2π - Full rotation in radians: 6.283185307179586", + "value": "6.283185307179586476925286766559005768394338798750211641949889184615632812572417997256069650684234135964735462226659258240820374631042607435096896808248" + }, + { + "name": "DegreesPerRadian", + "description": "Degrees per radian: 180/π ≈ 57.29577951308232", + "value": "57.29577951308232087679815481410517033240547246656432154916024386120284714832155263244096899585111094418897585567892854596978524038074810298080734906" + }, + { + "name": "RadiansPerDegree", + "description": "Radians per degree: π/180 ≈ 0.017453292519943295", + "value": "0.017453292519943295769236907684886127134428718885417254560971914401710091146034494436822415696345097379101040706699150667990539631694451077627806983" + } + ] + }, + { + "name": "Acoustics", + "description": "The physics of sound and vibration, including wave propagation, acoustic properties, sound intensity, frequency analysis, and audio-related measurements", + "constants": [ + { + "name": "ReferenceSoundPressure", + "description": "Reference sound pressure: 20 × 10⁻⁶ Pa (threshold of hearing)", + "value": "20e-6" + }, + { + "name": "ReferenceSoundIntensity", + "description": "Reference sound intensity: 1 × 10⁻¹² W/m² (threshold of hearing)", + "value": "1e-12" + }, + { + "name": "ReferenceSoundPower", + "description": "Reference sound power: 1 × 10⁻¹² W", + "value": "1e-12" + }, + { + "name": "SabineConstant", + "description": "Sabine reverberation constant: 0.161 s/m", + "value": "0.161" + } + ] + }, + { + "name": "Chemistry", + "description": "Chemical quantities and processes, including molecular properties, reaction kinetics, chemical concentrations, and the physical aspects of chemical systems", + "constants": [ + { + "name": "GasConstant", + "description": "Gas constant: 8.31446261815324 J/(mol·K) (exact, derived from Avogadro and Boltzmann constants)", + "value": "8.31446261815324" + }, + { + "name": "WaterIonProduct", + "description": "Water ion product (Kw) at 25°C: 1.0 × 10⁻¹⁴ (pKw = 14.0)", + "value": "14.0" + }, + { + "name": "MolarVolumeSTP", + "description": "Molar volume of ideal gas at STP: 22.413969545014137 L/mol (calculated from R*T/P at 273.15K, 101325Pa)", + "value": "22.413969545014137" + }, + { + "name": "Ln2", + "description": "Natural logarithm of 2: 0.6931471805599453", + "value": "0.6931471805599453094172321214581765680755001343602552541206800094933936219696947156058633269964186875420014810205706857336855202357581305570326707" + }, + { + "name": "NeutralPH", + "description": "Neutral pH value at 25°C: 7.0", + "value": "7.0" + } + ] + } + ] +} diff --git a/Semantics.SourceGenerators/Metadata/logarithmic.json b/Semantics.SourceGenerators/Metadata/logarithmic.json new file mode 100644 index 0000000..75fa21c --- /dev/null +++ b/Semantics.SourceGenerators/Metadata/logarithmic.json @@ -0,0 +1,156 @@ +{ + "logarithmicScales": [ + { + "name": "Decibels", + "description": "Represents a logarithmic level in decibels (dB).", + "remarks": "Decibels express ratios on a logarithmic scale. Amplitude/field quantities use dB = 20·log10(ratio); power quantities use dB = 10·log10(ratio). A level of 0 dB is unity.", + "displayFormat": "{0} dB", + "scalarFactory": "Create", + "arithmetic": true, + "conversions": [ + { + "linear": "Gain", + "multiplier": "20", + "fromName": "FromGain", + "toName": "ToAmplitude", + "fromSummary": "Creates a level from an amplitude gain using dB = 20·log10(gain).", + "toSummary": "Converts this level to a linear amplitude gain using gain = 10^(dB/20)." + }, + { + "linear": "Ratio", + "multiplier": "10", + "fromName": "FromPowerRatio", + "toName": "ToPower", + "fromSummary": "Creates a level from a linear power ratio using dB = 10·log10(ratio).", + "toSummary": "Converts this level to a linear power ratio using ratio = 10^(dB/10)." + } + ] + }, + { + "name": "Cents", + "description": "Represents a musical pitch interval in cents (1/100 of an equal-tempered semitone).", + "remarks": "An octave is 1200 cents: cents = 1200·log2(frequencyRatio).", + "displayFormat": "{0} ct", + "scalarFactory": "Create", + "arithmetic": true, + "conversions": [ + { + "linear": "Ratio", + "multiplier": "1200", + "logBase": "2", + "fromName": "FromFrequencyRatio", + "toName": "ToFrequencyRatio", + "fromSummary": "Creates an interval from a frequency ratio using cents = 1200·log2(ratio).", + "toSummary": "Converts this interval to a frequency ratio using ratio = 2^(cents/1200)." + } + ] + }, + { + "name": "Semitones", + "description": "Represents a musical pitch interval in equal-tempered semitones.", + "remarks": "An octave is 12 semitones: semitones = 12·log2(frequencyRatio).", + "displayFormat": "{0} st", + "scalarFactory": "Create", + "arithmetic": true, + "conversions": [ + { + "linear": "Ratio", + "multiplier": "12", + "logBase": "2", + "fromName": "FromFrequencyRatio", + "toName": "ToFrequencyRatio", + "fromSummary": "Creates an interval from a frequency ratio using semitones = 12·log2(ratio).", + "toSummary": "Converts this interval to a frequency ratio using ratio = 2^(semitones/12)." + } + ] + }, + { + "name": "SoundPressureLevel", + "description": "Represents a sound pressure level (SPL) in decibels relative to the 20 µPa threshold of hearing.", + "remarks": "SPL is a logarithmic field quantity: SPL = 20·log10(p / p₀) with p₀ = 20 µPa.", + "displayFormat": "{0} dB SPL", + "scalarFactory": "FromDecibels", + "arithmetic": true, + "conversions": [ + { + "linear": "SoundPressure", + "multiplier": "20", + "reference": { "constant": "ReferenceSoundPressure" }, + "fromSummary": "Creates a level from a linear sound pressure using SPL = 20·log10(p / p₀).", + "toSummary": "Converts this level to the equivalent linear sound pressure using p = p₀·10^(SPL/20)." + } + ] + }, + { + "name": "SoundIntensityLevel", + "description": "Represents a sound intensity level (SIL) in decibels relative to the 10⁻¹² W/m² threshold of hearing.", + "remarks": "SIL is a logarithmic power quantity: SIL = 10·log10(I / I₀) with I₀ = 10⁻¹² W/m².", + "displayFormat": "{0} dB SIL", + "scalarFactory": "FromDecibels", + "arithmetic": true, + "conversions": [ + { + "linear": "SoundIntensity", + "multiplier": "10", + "reference": { "constant": "ReferenceSoundIntensity" }, + "fromSummary": "Creates a level from a linear sound intensity using SIL = 10·log10(I / I₀).", + "toSummary": "Converts this level to the equivalent linear sound intensity using I = I₀·10^(SIL/10)." + } + ] + }, + { + "name": "SoundPowerLevel", + "description": "Represents a sound power level (SWL) in decibels relative to the 10⁻¹² W reference sound power.", + "remarks": "SWL is a logarithmic power quantity: SWL = 10·log10(P / P₀) with P₀ = 10⁻¹² W.", + "displayFormat": "{0} dB SWL", + "scalarFactory": "FromDecibels", + "arithmetic": true, + "conversions": [ + { + "linear": "SoundPower", + "multiplier": "10", + "reference": { "constant": "ReferenceSoundPower" }, + "fromSummary": "Creates a level from a linear sound power using SWL = 10·log10(P / P₀).", + "toSummary": "Converts this level to the equivalent linear sound power using P = P₀·10^(SWL/10)." + } + ] + }, + { + "name": "DirectionalityIndex", + "description": "Represents a directivity index (DI) in decibels — how much more intense a source or receiver is on-axis than its spherical average.", + "remarks": "DI = 10·log10(I_axis / I_average).", + "displayFormat": "{0} dB", + "scalarFactory": "FromDecibels", + "arithmetic": false, + "conversions": [ + { + "linear": "Ratio", + "multiplier": "10", + "fromName": "FromIntensityRatio", + "toName": "ToIntensityRatio", + "fromSummary": "Creates an index from the linear on-axis-to-average intensity ratio using DI = 10·log10(ratio).", + "toSummary": "Converts this index to the linear intensity ratio using ratio = 10^(DI/10)." + } + ] + }, + { + "name": "PH", + "description": "Represents acidity on the pH scale: pH = −log10([H⁺]) with the hydrogen-ion activity in mol/L.", + "remarks": "pH is a logarithmic scale over the linear Concentration quantity (stored in the SI base mol/m³; 1 mol/L = 1000 mol/m³).", + "displayFormat": "pH {0}", + "scalarFactory": "Create", + "arithmetic": false, + "conversions": [ + { + "linear": "Concentration", + "multiplier": "-1", + "reference": { "value": "1000" }, + "fromName": "FromHydrogenConcentration", + "toName": "ToHydrogenConcentration", + "fromSummary": "Creates a pH from a hydrogen-ion concentration using pH = −log10([H⁺] in mol/L).", + "toSummary": "Converts this pH to the equivalent hydrogen-ion concentration using [H⁺] = 10^(−pH) mol/L." + } + ] + } + ] +} diff --git a/Semantics.SourceGenerators/Metadata/magnitudes.json b/Semantics.SourceGenerators/Metadata/magnitudes.json new file mode 100644 index 0000000..e7523e4 --- /dev/null +++ b/Semantics.SourceGenerators/Metadata/magnitudes.json @@ -0,0 +1,109 @@ +{ + "magnitudes": [ + { + "name": "Yotta", + "symbol": "Y", + "exponent": 24 + }, + { + "name": "Zetta", + "symbol": "Z", + "exponent": 21 + }, + { + "name": "Exa", + "symbol": "E", + "exponent": 18 + }, + { + "name": "Peta", + "symbol": "P", + "exponent": 15 + }, + { + "name": "Tera", + "symbol": "T", + "exponent": 12 + }, + { + "name": "Giga", + "symbol": "G", + "exponent": 9 + }, + { + "name": "Mega", + "symbol": "M", + "exponent": 6 + }, + { + "name": "Kilo", + "symbol": "k", + "exponent": 3 + }, + { + "name": "Hecto", + "symbol": "h", + "exponent": 2 + }, + { + "name": "Deka", + "symbol": "da", + "exponent": 1 + }, + { + "name": "Unity", + "symbol": "", + "exponent": 0 + }, + { + "name": "Deci", + "symbol": "d", + "exponent": -1 + }, + { + "name": "Centi", + "symbol": "c", + "exponent": -2 + }, + { + "name": "Milli", + "symbol": "m", + "exponent": -3 + }, + { + "name": "Micro", + "symbol": "μ", + "exponent": -6 + }, + { + "name": "Nano", + "symbol": "n", + "exponent": -9 + }, + { + "name": "Pico", + "symbol": "p", + "exponent": -12 + }, + { + "name": "Femto", + "symbol": "f", + "exponent": -15 + }, + { + "name": "Atto", + "symbol": "a", + "exponent": -18 + }, + { + "name": "Zepto", + "symbol": "z", + "exponent": -21 + }, + { + "name": "Yocto", + "symbol": "y", + "exponent": -24 + } + ] +} diff --git a/Semantics.SourceGenerators/Metadata/precision.json b/Semantics.SourceGenerators/Metadata/precision.json new file mode 100644 index 0000000..af873e2 --- /dev/null +++ b/Semantics.SourceGenerators/Metadata/precision.json @@ -0,0 +1,6 @@ +{ + "storageTypes": [ + "float", + "double" + ] +} diff --git a/Semantics.SourceGenerators/Metadata/units.json b/Semantics.SourceGenerators/Metadata/units.json new file mode 100644 index 0000000..02607a6 --- /dev/null +++ b/Semantics.SourceGenerators/Metadata/units.json @@ -0,0 +1,1334 @@ +{ + "unitCategories": [ + { + "name": "Fundamental", + "description": "Fundamental SI units and basic derived units", + "units": [ + { + "name": "Meter", + "symbol": "m", + "description": "Meter - SI base unit of length.", + "system": "SIBase" + }, + { + "name": "Kilometer", + "symbol": "km", + "description": "Kilometer - 1000 meters.", + "system": "SIDerived", + "magnitude": "Kilo" + }, + { + "name": "Centimeter", + "symbol": "cm", + "description": "Centimeter - 0.01 meters.", + "system": "SIDerived", + "magnitude": "Centi" + }, + { + "name": "Millimeter", + "symbol": "mm", + "description": "Millimeter - 0.001 meters.", + "system": "SIDerived", + "magnitude": "Milli" + }, + { + "name": "Foot", + "symbol": "ft", + "description": "Foot - Imperial unit of length.", + "system": "Imperial", + "conversionFactor": "FeetToMeters" + }, + { + "name": "Inch", + "symbol": "in", + "description": "Inch - Imperial unit of length.", + "system": "Imperial", + "conversionFactor": "InchesToMeters" + }, + { + "name": "Micrometer", + "symbol": "μm", + "description": "Micrometer - 0.000001 meters.", + "system": "SIDerived", + "magnitude": "Micro" + }, + { + "name": "Nanometer", + "symbol": "nm", + "description": "Nanometer - 0.000000001 meters.", + "system": "SIDerived", + "magnitude": "Nano" + }, + { + "name": "Angstrom", + "symbol": "Å", + "description": "Angstrom - 10⁻¹⁰ meters, used for atomic scales.", + "system": "Other", + "conversionFactor": "AngstromToMeters" + }, + { + "name": "Yard", + "symbol": "yd", + "description": "Yard - Imperial unit of length.", + "system": "Imperial", + "conversionFactor": "YardToMeters" + }, + { + "name": "Mile", + "symbol": "mi", + "description": "Mile - Imperial unit of length.", + "system": "Imperial", + "conversionFactor": "MileToMeters" + }, + { + "name": "Kilogram", + "symbol": "kg", + "description": "Kilogram - SI base unit of mass.", + "system": "SIBase" + }, + { + "name": "Gram", + "symbol": "g", + "description": "Gram - 0.001 kilograms.", + "system": "SIDerived", + "magnitude": "Milli" + }, + { + "name": "Ton", + "symbol": "t", + "description": "Metric ton - 1000 kilograms.", + "system": "SIDerived", + "conversionFactor": "TonToKilograms" + }, + { + "name": "Pound", + "symbol": "lb", + "description": "Pound - Imperial unit of mass.", + "system": "Imperial", + "conversionFactor": "PoundToKilograms" + }, + { + "name": "Ounce", + "symbol": "oz", + "description": "Ounce - Imperial unit of mass.", + "system": "Imperial", + "conversionFactor": "OunceToKilograms" + }, + { + "name": "Second", + "symbol": "s", + "description": "Second - SI base unit of time.", + "system": "SIBase" + }, + { + "name": "Minute", + "symbol": "min", + "description": "Minute - 60 seconds.", + "system": "Other", + "conversionFactor": "MinuteToSeconds" + }, + { + "name": "Hour", + "symbol": "h", + "description": "Hour - 3600 seconds.", + "system": "Other", + "conversionFactor": "HourToSeconds" + }, + { + "name": "Day", + "symbol": "d", + "description": "Day - 86400 seconds.", + "system": "Other", + "conversionFactor": "DayToSeconds" + }, + { + "name": "Year", + "symbol": "yr", + "description": "Year - 365.25 days (31557600 seconds).", + "system": "Other", + "conversionFactor": "YearToSeconds" + }, + { + "name": "Millisecond", + "symbol": "ms", + "description": "Millisecond - 0.001 seconds.", + "system": "SIDerived", + "magnitude": "Milli" + }, + { + "name": "Microsecond", + "symbol": "μs", + "description": "Microsecond - 0.000001 seconds.", + "system": "SIDerived", + "magnitude": "Micro" + }, + { + "name": "SquareMeter", + "symbol": "m²", + "description": "Square meter - SI derived unit of area.", + "system": "SIDerived" + }, + { + "name": "SquareFoot", + "symbol": "ft²", + "description": "Square foot - Imperial unit of area.", + "system": "Imperial", + "conversionFactor": "SquareFootToSquareMeters" + }, + { + "name": "SquareInch", + "symbol": "in²", + "description": "Square inch - Imperial unit of area.", + "system": "Imperial", + "conversionFactor": "SquareInchToSquareMeters" + }, + { + "name": "CubicMeter", + "symbol": "m³", + "description": "Cubic meter - SI derived unit of volume.", + "system": "SIDerived" + }, + { + "name": "Liter", + "symbol": "L", + "description": "Liter - 0.001 cubic meters.", + "system": "SIDerived", + "conversionFactor": "LiterToCubicMeters" + }, + { + "name": "Milliliter", + "symbol": "mL", + "description": "Milliliter - 0.001 liters.", + "system": "SIDerived", + "magnitude": "Milli" + }, + { + "name": "Gallon", + "symbol": "gal", + "description": "US gallon - Imperial unit of volume.", + "system": "Imperial", + "conversionFactor": "GallonToCubicMeters" + }, + { + "name": "Dimensionless", + "symbol": "1", + "description": "Dimensionless - Pure number or ratio with no physical units.", + "system": "SIBase" + }, + { + "name": "Radian", + "symbol": "rad", + "description": "Radian - SI derived unit of plane angle.", + "system": "SIDerived" + }, + { + "name": "Degree", + "symbol": "°", + "description": "Degree - Common unit of plane angle.", + "system": "Other", + "conversionFactor": "DegreeToRadians" + }, + { + "name": "NauticalMile", + "symbol": "nmi", + "description": "Nautical mile - 1852 meters, used in navigation.", + "system": "Other", + "conversionFactor": "NauticalMileToMeters" + }, + { + "name": "Stone", + "symbol": "st", + "description": "Stone - Imperial unit of mass (14 pounds).", + "system": "Imperial", + "conversionFactor": "StoneToKilograms" + }, + { + "name": "ShortTon", + "symbol": "ton", + "description": "Short ton - US customary unit of mass (2000 pounds).", + "system": "USCustomary", + "conversionFactor": "ShortTonToKilograms" + }, + { + "name": "AtomicMassUnit", + "symbol": "u", + "description": "Atomic mass unit - 1/12 the mass of a carbon-12 atom.", + "system": "Atomic", + "conversionFactor": "AtomicMassUnitToKilograms" + }, + { + "name": "Week", + "symbol": "wk", + "description": "Week - 7 days.", + "system": "Other", + "conversionFactor": "WeekToSeconds" + }, + { + "name": "Nanosecond", + "symbol": "ns", + "description": "Nanosecond - 1e-9 seconds.", + "system": "SIDerived", + "magnitude": "Nano" + }, + { + "name": "SquareKilometer", + "symbol": "km²", + "description": "Square kilometer - 1e6 square meters.", + "system": "SIDerived", + "conversionFactor": "SquareKilometerToSquareMeters" + }, + { + "name": "SquareCentimeter", + "symbol": "cm²", + "description": "Square centimeter - 1e-4 square meters.", + "system": "SIDerived", + "conversionFactor": "SquareCentimeterToSquareMeters" + }, + { + "name": "SquareMile", + "symbol": "mi²", + "description": "Square mile - Imperial unit of area.", + "system": "Imperial", + "conversionFactor": "SquareMileToSquareMeters" + }, + { + "name": "Hectare", + "symbol": "ha", + "description": "Hectare - metric unit of area (10000 m²).", + "system": "Metric", + "conversionFactor": "HectareToSquareMeters" + }, + { + "name": "Acre", + "symbol": "ac", + "description": "Acre - Imperial unit of area.", + "system": "Imperial", + "conversionFactor": "AcreToSquareMeters" + }, + { + "name": "CubicCentimeter", + "symbol": "cm³", + "description": "Cubic centimeter - 1e-6 cubic meters.", + "system": "SIDerived", + "conversionFactor": "CubicCentimeterToCubicMeters" + }, + { + "name": "CubicFoot", + "symbol": "ft³", + "description": "Cubic foot - Imperial unit of volume.", + "system": "Imperial", + "conversionFactor": "CubicFootToCubicMeters" + }, + { + "name": "CubicInch", + "symbol": "in³", + "description": "Cubic inch - Imperial unit of volume.", + "system": "Imperial", + "conversionFactor": "CubicInchToCubicMeters" + }, + { + "name": "ImperialGallon", + "symbol": "imp gal", + "description": "Imperial gallon - British unit of volume.", + "system": "Imperial", + "conversionFactor": "ImperialGallonToCubicMeters" + }, + { + "name": "USQuart", + "symbol": "qt", + "description": "US liquid quart - US customary unit of volume.", + "system": "USCustomary", + "conversionFactor": "USQuartToCubicMeters" + }, + { + "name": "USPint", + "symbol": "pt", + "description": "US liquid pint - US customary unit of volume.", + "system": "USCustomary", + "conversionFactor": "USPintToCubicMeters" + }, + { + "name": "USFluidOunce", + "symbol": "fl oz", + "description": "US fluid ounce - US customary unit of volume.", + "system": "USCustomary", + "conversionFactor": "USFluidOunceToCubicMeters" + }, + { + "name": "Gradian", + "symbol": "grad", + "description": "Gradian - 1/400 of a full circle (π/200 rad).", + "system": "Other", + "conversionFactor": "GradianToRadians" + }, + { + "name": "Revolution", + "symbol": "rev", + "description": "Revolution - one full circle (2π rad).", + "system": "Other", + "conversionFactor": "RevolutionToRadians" + }, + { + "name": "Milliradian", + "symbol": "mrad", + "description": "Milliradian - 0.001 radians.", + "system": "SIDerived", + "magnitude": "Milli" + }, + { + "name": "Percent", + "symbol": "%", + "description": "Percent - one hundredth of unity.", + "system": "Other", + "conversionFactor": "PercentToRatio" + }, + { + "name": "PartPerMillion", + "symbol": "ppm", + "description": "Parts per million - 1e-6 dimensionless ratio.", + "system": "Other", + "conversionFactor": "PartPerMillionToRatio" + }, + { + "name": "PartPerBillion", + "symbol": "ppb", + "description": "Parts per billion - 1e-9 dimensionless ratio.", + "system": "Other", + "conversionFactor": "PartPerBillionToRatio" + }, + { + "name": "PercentByWeight", + "symbol": "% w/w", + "description": "Percent by weight - mass fraction expressed as a percentage.", + "system": "Other", + "conversionFactor": "PercentByWeightToRatio" + } + ] + }, + { + "name": "ClassicalMechanics", + "description": "Units of mechanical quantities (force, pressure, motion)", + "units": [ + { + "name": "Newton", + "symbol": "N", + "description": "Newton - SI derived unit of force.", + "system": "SIDerived" + }, + { + "name": "Pascal", + "symbol": "Pa", + "description": "Pascal - SI derived unit of pressure.", + "system": "SIDerived" + }, + { + "name": "MeterPerSecond", + "symbol": "m/s", + "description": "Meters per second - SI derived unit of velocity.", + "system": "SIDerived" + }, + { + "name": "MeterPerSecondSquared", + "symbol": "m/s²", + "description": "Meters per second squared - SI derived unit of acceleration.", + "system": "SIDerived" + }, + { + "name": "Bar", + "symbol": "bar", + "description": "Bar - Metric unit of pressure.", + "system": "SIDerived", + "conversionFactor": "BarToPascals" + }, + { + "name": "Atmosphere", + "symbol": "atm", + "description": "Standard atmosphere - Unit of pressure.", + "system": "Other", + "conversionFactor": "AtmosphereToPascals" + }, + { + "name": "Psi", + "symbol": "psi", + "description": "Pounds per square inch - Imperial unit of pressure.", + "system": "Imperial", + "conversionFactor": "PsiToPascals" + }, + { + "name": "KilometerPerHour", + "symbol": "km/h", + "description": "Kilometers per hour - Common unit of velocity.", + "system": "SIDerived", + "conversionFactor": "KilometerPerHourToMeterPerSecond" + }, + { + "name": "MilePerHour", + "symbol": "mph", + "description": "Miles per hour - Imperial unit of velocity.", + "system": "Imperial", + "conversionFactor": "MilePerHourToMeterPerSecond" + }, + { + "name": "Joule", + "symbol": "J", + "description": "Joule - SI derived unit of energy.", + "system": "SIDerived" + }, + { + "name": "Watt", + "symbol": "W", + "description": "Watt - SI derived unit of power.", + "system": "SIDerived" + }, + { + "name": "Calorie", + "symbol": "cal", + "description": "Calorie - Thermochemical calorie, energy unit.", + "system": "Other", + "conversionFactor": "CalorieToJoules" + }, + { + "name": "KilowattHour", + "symbol": "kWh", + "description": "Kilowatt-hour - Common unit of electrical energy.", + "system": "SIDerived", + "conversionFactor": "KilowattHourToJoules" + }, + { + "name": "Horsepower", + "symbol": "hp", + "description": "Mechanical horsepower - Imperial unit of power.", + "system": "Imperial", + "conversionFactor": "HorsepowerToWatts" + }, + { + "name": "NewtonSecond", + "symbol": "N⋅s", + "description": "Newton-second - SI derived unit of momentum.", + "system": "SIDerived" + }, + { + "name": "ElectronVolt", + "symbol": "eV", + "description": "Electron volt - Energy unit equal to electron charge times one volt.", + "system": "Other", + "conversionFactor": "ElectronVoltToJoules" + }, + { + "name": "NewtonMeter", + "symbol": "N⋅m", + "description": "Newton-meter - SI derived unit of torque.", + "system": "SIDerived" + }, + { + "name": "PoundFoot", + "symbol": "lb⋅ft", + "description": "Pound-foot - Imperial unit of torque.", + "system": "Imperial", + "conversionFactor": "PoundFootToNewtonMeters" + }, + { + "name": "KilogramMeterSquared", + "symbol": "kg⋅m²", + "description": "Kilogram-meter squared - SI derived unit of moment of inertia.", + "system": "SIDerived" + }, + { + "name": "KilogramMeterSquaredPerSecond", + "symbol": "kg⋅m²/s", + "description": "Kilogram-meter squared per second - SI derived unit of angular momentum.", + "system": "SIDerived" + }, + { + "name": "MeterPerSecondCubed", + "symbol": "m/s³", + "description": "Meters per second cubed - SI derived unit of jerk.", + "system": "SIDerived" + }, + { + "name": "MeterPerSecondQuartic", + "symbol": "m/s⁴", + "description": "Meters per second to the fourth - SI derived unit of snap.", + "system": "SIDerived" + }, + { + "name": "FootPerSecond", + "symbol": "ft/s", + "description": "Feet per second - Imperial unit of velocity.", + "system": "Imperial", + "conversionFactor": "FootPerSecondToMeterPerSecond" + }, + { + "name": "Knot", + "symbol": "kn", + "description": "Knot - one nautical mile per hour.", + "system": "Other", + "conversionFactor": "KnotToMeterPerSecond" + }, + { + "name": "StandardGravity", + "symbol": "g", + "description": "Standard gravity - acceleration of free fall at Earth's surface.", + "system": "Other", + "conversionFactor": "StandardGravityToMeterPerSecondSquared" + }, + { + "name": "Kilonewton", + "symbol": "kN", + "description": "Kilonewton - 1000 newtons.", + "system": "SIDerived", + "magnitude": "Kilo" + }, + { + "name": "Dyne", + "symbol": "dyn", + "description": "Dyne - CGS unit of force.", + "system": "CGS", + "conversionFactor": "DyneToNewtons" + }, + { + "name": "PoundForce", + "symbol": "lbf", + "description": "Pound-force - Imperial unit of force.", + "system": "Imperial", + "conversionFactor": "PoundForceToNewtons" + }, + { + "name": "Kilopascal", + "symbol": "kPa", + "description": "Kilopascal - 1000 pascals.", + "system": "SIDerived", + "magnitude": "Kilo" + }, + { + "name": "Torr", + "symbol": "Torr", + "description": "Torr - 1/760 of a standard atmosphere.", + "system": "Other", + "conversionFactor": "TorrToPascals" + }, + { + "name": "Kilojoule", + "symbol": "kJ", + "description": "Kilojoule - 1000 joules.", + "system": "SIDerived", + "magnitude": "Kilo" + }, + { + "name": "Kilocalorie", + "symbol": "kcal", + "description": "Kilocalorie - 1000 thermochemical calories.", + "system": "Other", + "conversionFactor": "KilocalorieToJoules" + }, + { + "name": "WattHour", + "symbol": "Wh", + "description": "Watt-hour - 3600 joules.", + "system": "SIDerived", + "conversionFactor": "WattHourToJoules" + }, + { + "name": "Erg", + "symbol": "erg", + "description": "Erg - CGS unit of energy.", + "system": "CGS", + "conversionFactor": "ErgToJoules" + }, + { + "name": "Btu", + "symbol": "BTU", + "description": "British thermal unit (IT) - Imperial unit of energy.", + "system": "Imperial", + "conversionFactor": "BtuToJoules" + }, + { + "name": "Kilowatt", + "symbol": "kW", + "description": "Kilowatt - 1000 watts.", + "system": "SIDerived", + "magnitude": "Kilo" + }, + { + "name": "Megawatt", + "symbol": "MW", + "description": "Megawatt - 1e6 watts.", + "system": "SIDerived", + "magnitude": "Mega" + }, + { + "name": "WattPerCubicMeter", + "symbol": "W/m³", + "description": "Watt per cubic meter - SI unit of volumetric power density.", + "system": "SIDerived" + } + ] + }, + { + "name": "Thermodynamics", + "description": "Units of temperature measurement", + "units": [ + { + "name": "Kelvin", + "symbol": "K", + "description": "Kelvin - SI base unit of thermodynamic temperature.", + "system": "SIBase" + }, + { + "name": "Celsius", + "symbol": "°C", + "description": "Celsius - Common temperature scale.", + "system": "SIDerived", + "offset": "CelsiusToKelvinOffset" + }, + { + "name": "Fahrenheit", + "symbol": "°F", + "description": "Fahrenheit - Imperial temperature scale.", + "system": "Imperial", + "conversionFactor": "FahrenheitScale", + "offset": "FahrenheitToKelvinOffset" + }, + { + "name": "JoulePerKelvin", + "symbol": "J/K", + "description": "Joule per kelvin - SI derived unit of entropy and heat capacity.", + "system": "SIDerived" + }, + { + "name": "JoulePerKilogramKelvin", + "symbol": "J/(kg·K)", + "description": "Joule per kilogram kelvin - SI derived unit of specific heat capacity.", + "system": "SIDerived" + }, + { + "name": "WattPerMeterKelvin", + "symbol": "W/(m·K)", + "description": "Watt per meter kelvin - SI derived unit of thermal conductivity.", + "system": "SIDerived" + }, + { + "name": "WattPerSquareMeterKelvin", + "symbol": "W/(m²·K)", + "description": "Watt per square meter kelvin - SI derived unit of heat transfer coefficient.", + "system": "SIDerived" + }, + { + "name": "PerKelvin", + "symbol": "K⁻¹", + "description": "Per kelvin - SI derived unit of thermal expansion coefficient.", + "system": "SIDerived" + }, + { + "name": "Rankine", + "symbol": "°R", + "description": "Rankine - absolute temperature scale with Fahrenheit-sized degrees.", + "system": "Imperial", + "conversionFactor": "FahrenheitScale" + }, + { + "name": "KelvinPerWatt", + "symbol": "K/W", + "description": "Kelvin per watt - SI unit of absolute thermal resistance.", + "system": "SIDerived" + } + ] + }, + { + "name": "Electromagnetism", + "description": "Units of electrical and electromagnetic quantities", + "units": [ + { + "name": "Ampere", + "symbol": "A", + "description": "Ampere - SI base unit of electric current.", + "system": "SIBase" + }, + { + "name": "Volt", + "symbol": "V", + "description": "Volt - SI derived unit of electric potential.", + "system": "SIDerived" + }, + { + "name": "VoltPerMeter", + "symbol": "V/m", + "description": "Volt per meter - SI derived unit of electric field strength.", + "system": "SIDerived" + }, + { + "name": "Ohm", + "symbol": "Ω", + "description": "Ohm - SI derived unit of electric resistance.", + "system": "SIDerived" + }, + { + "name": "Coulomb", + "symbol": "C", + "description": "Coulomb - SI derived unit of electric charge.", + "system": "SIDerived" + }, + { + "name": "Farad", + "symbol": "F", + "description": "Farad - SI derived unit of electric capacitance.", + "system": "SIDerived" + }, + { + "name": "Siemens", + "symbol": "S", + "description": "Siemens - SI derived unit of electric conductance.", + "system": "SIDerived" + }, + { + "name": "Tesla", + "symbol": "T", + "description": "Tesla - SI derived unit of magnetic flux density.", + "system": "SIDerived" + }, + { + "name": "Gauss", + "symbol": "G", + "description": "Gauss - CGS unit of magnetic flux density.", + "system": "CGS", + "conversionFactor": "GaussToTesla" + }, + { + "name": "Weber", + "symbol": "Wb", + "description": "Weber - SI derived unit of magnetic flux.", + "system": "SIDerived" + }, + { + "name": "Henry", + "symbol": "H", + "description": "Henry - SI derived unit of inductance.", + "system": "SIDerived" + }, + { + "name": "Milliampere", + "symbol": "mA", + "description": "Milliampere - 0.001 amperes.", + "system": "SIDerived", + "magnitude": "Milli" + }, + { + "name": "Kiloampere", + "symbol": "kA", + "description": "Kiloampere - 1000 amperes.", + "system": "SIDerived", + "magnitude": "Kilo" + }, + { + "name": "Kilovolt", + "symbol": "kV", + "description": "Kilovolt - 1000 volts.", + "system": "SIDerived", + "magnitude": "Kilo" + }, + { + "name": "Kilohm", + "symbol": "kΩ", + "description": "Kilohm - 1000 ohms.", + "system": "SIDerived", + "magnitude": "Kilo" + }, + { + "name": "Megohm", + "symbol": "MΩ", + "description": "Megohm - 1e6 ohms.", + "system": "SIDerived", + "magnitude": "Mega" + }, + { + "name": "Microfarad", + "symbol": "μF", + "description": "Microfarad - 1e-6 farads.", + "system": "SIDerived", + "magnitude": "Micro" + }, + { + "name": "Nanofarad", + "symbol": "nF", + "description": "Nanofarad - 1e-9 farads.", + "system": "SIDerived", + "magnitude": "Nano" + }, + { + "name": "Picofarad", + "symbol": "pF", + "description": "Picofarad - 1e-12 farads.", + "system": "SIDerived", + "magnitude": "Pico" + }, + { + "name": "AmpereHour", + "symbol": "Ah", + "description": "Ampere-hour - 3600 coulombs of electric charge.", + "system": "SIDerived", + "conversionFactor": "AmpereHourToCoulombs" + }, + { + "name": "VoltMeter", + "symbol": "V·m", + "description": "Volt meter - SI unit of electric flux.", + "system": "SIDerived" + }, + { + "name": "FaradPerMeter", + "symbol": "F/m", + "description": "Farad per meter - SI unit of permittivity.", + "system": "SIDerived" + }, + { + "name": "SiemensPerMeter", + "symbol": "S/m", + "description": "Siemens per meter - SI unit of electrical conductivity.", + "system": "SIDerived" + }, + { + "name": "VoltPerPascal", + "symbol": "V/Pa", + "description": "Volt per pascal - SI unit of transducer sensitivity.", + "system": "SIDerived" + } + ] + }, + { + "name": "AngularMechanics", + "description": "Units of rotational motion and angular quantities", + "units": [ + { + "name": "RadianPerSecond", + "symbol": "rad/s", + "description": "Radians per second - SI derived unit of angular velocity.", + "system": "SIDerived" + }, + { + "name": "RevolutionPerMinute", + "symbol": "rpm", + "description": "Revolutions per minute - Common unit of angular velocity.", + "system": "Other", + "conversionFactor": "RevolutionPerMinuteToRadianPerSecond" + }, + { + "name": "RadianPerSecondSquared", + "symbol": "rad/s²", + "description": "Radians per second squared - SI derived unit of angular acceleration.", + "system": "SIDerived" + }, + { + "name": "RadianPerSecondCubed", + "symbol": "rad/s³", + "description": "Radians per second cubed - SI derived unit of angular jerk.", + "system": "SIDerived" + } + ] + }, + { + "name": "Acoustics", + "description": "Units of wave and oscillation phenomena", + "units": [ + { + "name": "Hertz", + "symbol": "Hz", + "description": "Hertz - SI derived unit of frequency.", + "system": "SIDerived" + }, + { + "name": "Kilohertz", + "symbol": "kHz", + "description": "Kilohertz - 1000 hertz.", + "system": "SIDerived", + "magnitude": "Kilo" + }, + { + "name": "Megahertz", + "symbol": "MHz", + "description": "Megahertz - 1e6 hertz.", + "system": "SIDerived", + "magnitude": "Mega" + }, + { + "name": "Sone", + "symbol": "sone", + "description": "Sone - psychoacoustic unit of perceived loudness.", + "system": "Other" + }, + { + "name": "Acum", + "symbol": "acum", + "description": "Acum - psychoacoustic unit of perceived sharpness.", + "system": "Other" + } + ] + }, + { + "name": "Optics", + "description": "Units of optical and photometric quantities", + "units": [ + { + "name": "Candela", + "symbol": "cd", + "description": "Candela - SI base unit of luminous intensity.", + "system": "SIBase" + }, + { + "name": "Lumen", + "symbol": "lm", + "description": "Lumen - SI derived unit of luminous flux.", + "system": "SIDerived" + }, + { + "name": "Lux", + "symbol": "lx", + "description": "Lux - SI derived unit of illuminance.", + "system": "SIDerived" + }, + { + "name": "Diopter", + "symbol": "D", + "description": "Diopter - SI unit of optical power.", + "system": "SIDerived" + }, + { + "name": "Millicandela", + "symbol": "mcd", + "description": "Millicandela - 0.001 candelas.", + "system": "SIDerived", + "magnitude": "Milli" + }, + { + "name": "FootCandle", + "symbol": "fc", + "description": "Foot-candle - Imperial unit of illuminance (lumen per square foot).", + "system": "Imperial", + "conversionFactor": "FootCandleToLux" + }, + { + "name": "CandelaPerSquareMeter", + "symbol": "cd/m²", + "description": "Candela per square meter - SI unit of luminance.", + "system": "SIDerived" + }, + { + "name": "Nit", + "symbol": "nt", + "description": "Nit - common name for one candela per square meter.", + "system": "SIDerived" + }, + { + "name": "FootLambert", + "symbol": "fL", + "description": "Foot-lambert - Imperial unit of luminance (1/π candela per square foot).", + "system": "Imperial", + "conversionFactor": "FootLambertToCandelaPerSquareMeter" + } + ] + }, + { + "name": "NuclearPhysics", + "description": "Units of nuclear and radiation quantities", + "units": [ + { + "name": "Becquerel", + "symbol": "Bq", + "description": "Becquerel - SI derived unit of radioactive activity.", + "system": "SIDerived" + }, + { + "name": "Gray", + "symbol": "Gy", + "description": "Gray - SI derived unit of absorbed dose.", + "system": "SIDerived" + }, + { + "name": "Sievert", + "symbol": "Sv", + "description": "Sievert - SI derived unit of equivalent dose.", + "system": "SIDerived" + }, + { + "name": "Barn", + "symbol": "b", + "description": "Barn - Unit of nuclear cross section.", + "system": "Other", + "conversionFactor": "BarnToSquareMeters" + }, + { + "name": "CoulombPerKilogram", + "symbol": "C/kg", + "description": "Coulomb per kilogram - SI derived unit of radiation exposure.", + "system": "SIDerived" + }, + { + "name": "Curie", + "symbol": "Ci", + "description": "Curie - traditional unit of radioactive activity.", + "system": "Other", + "conversionFactor": "CurieToBecquerels" + }, + { + "name": "Rad", + "symbol": "rad", + "description": "Rad - traditional unit of absorbed dose (0.01 Gy).", + "system": "Other", + "conversionFactor": "RadToGrays" + }, + { + "name": "Rem", + "symbol": "rem", + "description": "Rem - traditional unit of equivalent dose (0.01 Sv).", + "system": "Other", + "conversionFactor": "RemToSieverts" + }, + { + "name": "Roentgen", + "symbol": "R", + "description": "Roentgen - traditional unit of ionizing radiation exposure.", + "system": "Other", + "conversionFactor": "RoentgenToCoulombsPerKilogram" + } + ] + }, + { + "name": "FluidMechanics", + "description": "Units of fluid mechanics quantities", + "units": [ + { + "name": "KilogramPerCubicMeter", + "symbol": "kg/m³", + "description": "Kilogram per cubic meter - SI derived unit of density.", + "system": "SIDerived" + }, + { + "name": "GramPerCubicCentimeter", + "symbol": "g/cm³", + "description": "Gram per cubic centimeter - 1000 kg/m³.", + "system": "SIDerived", + "conversionFactor": "GramPerCubicCentimeterToKilogramPerCubicMeter" + }, + { + "name": "GramPerLiter", + "symbol": "g/L", + "description": "Gram per liter - equal to one kilogram per cubic meter.", + "system": "SIDerived", + "conversionFactor": "GramPerLiterToKilogramPerCubicMeter" + } + ] + }, + { + "name": "Chemistry", + "description": "Units of chemical quantities", + "units": [ + { + "name": "Mole", + "symbol": "mol", + "description": "Mole - SI base unit of amount of substance.", + "system": "SIBase" + }, + { + "name": "MolePerCubicMeter", + "symbol": "mol/m³", + "description": "Mole per cubic meter - SI base unit of concentration.", + "system": "SIDerived" + }, + { + "name": "Molar", + "symbol": "M", + "description": "Molar - Moles per liter concentration.", + "system": "SIDerived", + "conversionFactor": "MolarToCubicMeter" + }, + { + "name": "Kilomole", + "symbol": "kmol", + "description": "Kilomole - 1000 moles.", + "system": "SIDerived", + "magnitude": "Kilo" + }, + { + "name": "Millimole", + "symbol": "mmol", + "description": "Millimole - 0.001 moles.", + "system": "SIDerived", + "magnitude": "Milli" + }, + { + "name": "Millimolar", + "symbol": "mM", + "description": "Millimolar - 0.001 moles per liter (1 mol/m³).", + "system": "SIDerived", + "conversionFactor": "MillimolarToMolePerCubicMeter" + }, + { + "name": "Micromolar", + "symbol": "μM", + "description": "Micromolar - 1e-6 moles per liter (0.001 mol/m³).", + "system": "SIDerived", + "conversionFactor": "MicromolarToMolePerCubicMeter" + } + ] + }, + { + "name": "FluidMechanics", + "description": "Units of fluid dynamics quantities", + "units": [ + { + "name": "SquareMeterPerSecond", + "symbol": "m²/s", + "description": "Square meter per second - SI derived unit of kinematic viscosity.", + "system": "SIDerived" + }, + { + "name": "Stokes", + "symbol": "St", + "description": "Stokes - CGS unit of kinematic viscosity.", + "system": "CGS", + "conversionFactor": "StokesToSquareMeterPerSecond" + }, + { + "name": "PascalSecond", + "symbol": "Pa·s", + "description": "Pascal second - SI derived unit of dynamic viscosity.", + "system": "SIDerived" + }, + { + "name": "Poise", + "symbol": "P", + "description": "Poise - CGS unit of dynamic viscosity.", + "system": "CGS", + "conversionFactor": "PoiseToPascalSecond" + }, + { + "name": "CubicMeterPerSecond", + "symbol": "m³/s", + "description": "Cubic meter per second - SI derived unit of volumetric flow rate.", + "system": "SIDerived" + }, + { + "name": "LiterPerSecond", + "symbol": "L/s", + "description": "Liter per second - Common unit of volumetric flow rate.", + "system": "SIDerived", + "conversionFactor": "LiterPerSecondToCubicMeterPerSecond" + }, + { + "name": "KilogramPerSecond", + "symbol": "kg/s", + "description": "Kilogram per second - SI derived unit of mass flow rate.", + "system": "SIDerived" + }, + { + "name": "NewtonPerMeter", + "symbol": "N/m", + "description": "Newton per meter - SI derived unit of surface tension.", + "system": "SIDerived" + }, + { + "name": "Centipoise", + "symbol": "cP", + "description": "Centipoise - 0.001 pascal seconds (viscosity of water at 20°C ≈ 1 cP).", + "system": "CGS", + "conversionFactor": "CentipoiseToPascalSecond" + }, + { + "name": "DynePerCentimeter", + "symbol": "dyn/cm", + "description": "Dyne per centimeter - CGS unit of surface tension.", + "system": "CGS", + "conversionFactor": "DynePerCentimeterToNewtonPerMeter" + } + ] + }, + { + "name": "Chemistry", + "description": "Units of chemical quantities", + "units": [ + { + "name": "KilogramPerMole", + "symbol": "kg/mol", + "description": "Kilogram per mole - SI derived unit of molar mass.", + "system": "SIDerived" + }, + { + "name": "GramPerMole", + "symbol": "g/mol", + "description": "Gram per mole - Common unit of molar mass.", + "system": "SIDerived", + "conversionFactor": "GramPerMoleToKilogramPerMole" + }, + { + "name": "Katal", + "symbol": "kat", + "description": "Katal - SI derived unit of catalytic activity.", + "system": "SIDerived" + }, + { + "name": "MolePerCubicMeterSecond", + "symbol": "mol/(m³·s)", + "description": "Mole per cubic meter second - SI derived unit of reaction rate.", + "system": "SIDerived" + }, + { + "name": "JoulePerMole", + "symbol": "J/mol", + "description": "Joule per mole - SI derived unit of molar energy.", + "system": "SIDerived" + }, + { + "name": "KilojoulePerMole", + "symbol": "kJ/mol", + "description": "Kilojoule per mole - Common unit of molar energy.", + "system": "SIDerived", + "conversionFactor": "KilojoulePerMoleToJoulePerMole" + }, + { + "name": "CaloriePerMole", + "symbol": "cal/mol", + "description": "Calorie per mole - thermochemical calorie per mole.", + "system": "Other", + "conversionFactor": "CaloriePerMoleToJoulePerMole" + }, + { + "name": "EnzymeUnit", + "symbol": "U", + "description": "Enzyme unit - one micromole of substrate per minute.", + "system": "Other", + "conversionFactor": "EnzymeUnitToKatals" + }, + { + "name": "Dalton", + "symbol": "Da", + "description": "Dalton - molar mass numerically equal to one gram per mole.", + "system": "Atomic", + "conversionFactor": "GramPerMoleToKilogramPerMole" + }, + { + "name": "PerSecond", + "symbol": "s⁻¹", + "description": "Per second - SI unit of a first-order rate constant.", + "system": "SIDerived" + } + ] + }, + { + "name": "AcousticsAndOptics", + "description": "Units of acoustic and optical quantities", + "units": [ + { + "name": "WattPerSquareMeter", + "symbol": "W/m²", + "description": "Watt per square meter - SI derived unit of irradiance and sound intensity.", + "system": "SIDerived" + }, + { + "name": "PascalSecondPerMeter", + "symbol": "Pa·s/m", + "description": "Pascal second per meter - SI derived unit of acoustic impedance.", + "system": "SIDerived" + } + ] + } + ] +} diff --git a/Semantics.SourceGenerators/Models/ConversionsMetadata.cs b/Semantics.SourceGenerators/Models/ConversionsMetadata.cs new file mode 100644 index 0000000..80e24a2 --- /dev/null +++ b/Semantics.SourceGenerators/Models/ConversionsMetadata.cs @@ -0,0 +1,35 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Models; + +using System.Collections.Generic; + +/// +/// Metadata structure for conversions generation. +/// +public class ConversionsMetadata +{ + public List Conversions { get; set; } = []; +} + +/// +/// Category of conversion factors. +/// +public class ConversionCategory +{ + public string Category { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public List Factors { get; set; } = []; +} + +/// +/// Definition of a single conversion factor for code generation. +/// +public class ConversionFactor +{ + public string Name { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public string Value { get; set; } = string.Empty; +} diff --git a/Semantics.SourceGenerators/Models/DimensionsMetadata.cs b/Semantics.SourceGenerators/Models/DimensionsMetadata.cs new file mode 100644 index 0000000..d83a9df --- /dev/null +++ b/Semantics.SourceGenerators/Models/DimensionsMetadata.cs @@ -0,0 +1,215 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Models; + +using System.Collections.Generic; + +/// +/// Root metadata structure for physical dimensions. +/// +public class DimensionsMetadata +{ + public List PhysicalDimensions { get; set; } = []; + + /// + /// Validates the deserialised metadata, returning a list of human-readable issues. + /// An empty list means the metadata is well-formed. + /// + /// + /// Per #60: catches malformed entries before they reach the generator emit + /// pass, so problems surface as Roslyn diagnostics rather than a mid-emit crash + /// or a silently dropped operator. Cross-dimension reference checks are still + /// performed in the generator (#56 / SEM001) because they need the full + /// dimension map. + /// + public List Validate() + { + List issues = []; + + if (PhysicalDimensions.Count == 0) + { + issues.Add("dimensions.json contains no physicalDimensions entries."); + return issues; + } + + HashSet seenDimensionNames = []; + HashSet seenTypeNames = []; + + foreach (PhysicalDimension dim in PhysicalDimensions) + { + string label = string.IsNullOrEmpty(dim.Name) ? "" : dim.Name; + + if (string.IsNullOrEmpty(dim.Name)) + { + issues.Add("A physicalDimensions entry is missing 'name'."); + } + else if (!seenDimensionNames.Add(dim.Name)) + { + issues.Add($"Dimension '{dim.Name}' is declared more than once."); + } + + if (string.IsNullOrEmpty(dim.Symbol)) + { + issues.Add($"Dimension '{label}' is missing 'symbol'."); + } + + if (dim.AvailableUnits.Count == 0) + { + issues.Add($"Dimension '{label}' has an empty 'availableUnits' list."); + } + else + { + foreach (string unit in dim.AvailableUnits) + { + if (string.IsNullOrWhiteSpace(unit)) + { + issues.Add($"Dimension '{label}' has a blank entry in 'availableUnits'."); + } + } + } + + VectorFormDefinition?[] forms = [ + dim.Quantities.Vector0, + dim.Quantities.Vector1, + dim.Quantities.Vector2, + dim.Quantities.Vector3, + dim.Quantities.Vector4, + ]; + + if (System.Array.TrueForAll(forms, f => f == null)) + { + issues.Add($"Dimension '{label}' declares no vector forms (vector0..vector4)."); + } + + for (int i = 0; i < forms.Length; i++) + { + VectorFormDefinition? form = forms[i]; + if (form == null) + { + continue; + } + + if (string.IsNullOrEmpty(form.Base)) + { + issues.Add($"Dimension '{label}' vector{i} is missing 'base'."); + } + else if (!seenTypeNames.Add(form.Base)) + { + issues.Add($"Type name '{form.Base}' (dimension '{label}' vector{i}) collides with another base or overload."); + } + + foreach (OverloadDefinition overload in form.Overloads) + { + if (string.IsNullOrEmpty(overload.Name)) + { + issues.Add($"Dimension '{label}' vector{i} has an overload missing 'name'."); + continue; + } + + if (!seenTypeNames.Add(overload.Name)) + { + issues.Add($"Overload type name '{overload.Name}' (dimension '{label}' vector{i}) collides with another base or overload."); + } + } + } + } + + return issues; + } +} + +/// +/// Definition of a physical dimension with vector form quantities and relationships. +/// Relationships reference dimension names and are resolved to concrete types at each vector form. +/// +public class PhysicalDimension +{ + public string Name { get; set; } = string.Empty; + public string Symbol { get; set; } = string.Empty; + public Dictionary DimensionalFormula { get; set; } = []; + public List AvailableUnits { get; set; } = []; + public VectorFormsMap Quantities { get; set; } = new(); + public List Integrals { get; set; } = []; + public List Derivatives { get; set; } = []; + public List DotProducts { get; set; } = []; + public List CrossProducts { get; set; } = []; +} + +/// +/// Map of vector forms for a physical dimension. +/// Each entry is nullable; absent entries mean that vector form is not available. +/// +public class VectorFormsMap +{ + public VectorFormDefinition? Vector0 { get; set; } + public VectorFormDefinition? Vector1 { get; set; } + public VectorFormDefinition? Vector2 { get; set; } + public VectorFormDefinition? Vector3 { get; set; } + public VectorFormDefinition? Vector4 { get; set; } +} + +/// +/// Definition of a single vector form within a dimension. +/// +public class VectorFormDefinition +{ + /// Gets or sets the base type name for this vector form. + public string Base { get; set; } = string.Empty; + + /// Gets or sets optional semantic overloads for this vector form. + public List Overloads { get; set; } = []; +} + +/// +/// Definition of a semantic overload (typed alias) for a base quantity type. +/// +public class OverloadDefinition +{ + public string Name { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public Dictionary Relationships { get; set; } = []; + + /// + /// Optional per-overload physical constraints. Currently only minExclusive is + /// honoured (per #51): when set on a V0 overload, the generated From{Unit} + /// factories use Vector0Guards.EnsurePositive instead of the default + /// EnsureNonNegative, so a strict-positive overload like Wavelength + /// rejects a zero input. The field is a string so it can hold a literal SI-base-unit + /// value (e.g. "0"); the generator emits T.CreateChecked(...) around it. + /// + public PhysicalConstraints? PhysicalConstraints { get; set; } +} + +/// +/// Per-overload physical constraints applied at construction time, on top of the structural +/// V0 non-negativity invariant. Only MinExclusive is implemented today; the other +/// fields are reserved for future per-overload bounds (#51). +/// +public class PhysicalConstraints +{ + /// + /// Strict-positive lower bound. When set (typically to "0"), the value must be + /// strictly greater than this number after conversion to the SI base unit. + /// + public string MinExclusive { get; set; } = string.Empty; +} + +/// +/// Definition of a mathematical relationship between dimensions. +/// +public class RelationshipDefinition +{ + public string Other { get; set; } = string.Empty; + public string Result { get; set; } = string.Empty; + + /// + /// Optional explicit list of vector forms (0..4) at which this relationship should + /// emit operators. When empty, the generator uses sensible defaults from the + /// relationship kind: integrals/derivatives default to all common forms, + /// dotProducts to V1+, crossProducts to V3 only. When set, missing forms + /// on either side surface as SEM003 diagnostics instead of being silently dropped. + /// + public List Forms { get; set; } = []; +} diff --git a/Semantics.SourceGenerators/Models/DomainsMetadata.cs b/Semantics.SourceGenerators/Models/DomainsMetadata.cs new file mode 100644 index 0000000..b3824cf --- /dev/null +++ b/Semantics.SourceGenerators/Models/DomainsMetadata.cs @@ -0,0 +1,35 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Models; + +using System.Collections.Generic; + +/// +/// Metadata structure for domains generation. +/// +public class DomainsMetadata +{ + public List Domains { get; set; } = []; +} + +/// +/// Domain of physical constants. +/// +public class Domain +{ + public string Name { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public List Constants { get; set; } = []; +} + +/// +/// Definition of a single physical constant for code generation. +/// +public class ConstantDefinition +{ + public string Name { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public string Value { get; set; } = string.Empty; +} diff --git a/Semantics.SourceGenerators/Models/LogarithmicMetadata.cs b/Semantics.SourceGenerators/Models/LogarithmicMetadata.cs new file mode 100644 index 0000000..22653f6 --- /dev/null +++ b/Semantics.SourceGenerators/Models/LogarithmicMetadata.cs @@ -0,0 +1,80 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Models; + +using System.Collections.Generic; + +/// +/// Metadata model for logarithmic-scale quantity generation (decibel levels, +/// pitch intervals, pH). Logarithmic scales don't obey linear arithmetic, so +/// they are generated as standalone record structs that convert to and from +/// their linear generated counterparts rather than as physical dimensions. +/// +public class LogarithmicMetadata +{ + public List LogarithmicScales { get; set; } = []; +} + +/// +/// Definition of a single logarithmic scale type. +/// +public class LogarithmicScaleDefinition +{ + public string Name { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public string? Remarks { get; set; } + + /// Display format with {0} as the value placeholder, e.g. "{0} dB SPL". + public string DisplayFormat { get; set; } = "{0}"; + + /// Name of the raw-scalar factory method (Create, FromDecibels, …). + public string ScalarFactory { get; set; } = "Create"; + + /// Whether to emit log-space addition and subtraction operators. + public bool Arithmetic { get; set; } + + public List Conversions { get; set; } = []; +} + +/// +/// A conversion between the logarithmic scale and a linear generated quantity: +/// scale = multiplier · log_base(linear / reference) and its inverse. +/// +public class LogarithmicConversionDefinition +{ + /// The linear generated quantity type name (e.g. SoundPressure). + public string Linear { get; set; } = string.Empty; + + /// The log-space multiplier (20 for field quantities, 10 for power quantities, 1200 for cents, …). + public string Multiplier { get; set; } = "10"; + + /// The logarithm base; defaults to 10. + public string LogBase { get; set; } = "10"; + + /// The reference value the linear quantity is divided by; defaults to 1. + public LogarithmicReferenceDefinition? Reference { get; set; } + + /// Factory method name override; defaults to From{Linear}. + public string? FromName { get; set; } + + /// Conversion method name override; defaults to To{Linear}. + public string? ToName { get; set; } + + public string? FromSummary { get; set; } + public string? ToSummary { get; set; } +} + +/// +/// The reference for a logarithmic conversion: either a named constant on +/// PhysicalConstants.Generic or a literal value. +/// +public class LogarithmicReferenceDefinition +{ + /// Name of a PhysicalConstants.Generic accessor (e.g. ReferenceSoundPressure). + public string? Constant { get; set; } + + /// A literal reference value (e.g. "1000"). + public string? Value { get; set; } +} diff --git a/Semantics.SourceGenerators/Models/MagnitudesMetadata.cs b/Semantics.SourceGenerators/Models/MagnitudesMetadata.cs new file mode 100644 index 0000000..635ed59 --- /dev/null +++ b/Semantics.SourceGenerators/Models/MagnitudesMetadata.cs @@ -0,0 +1,26 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Models; + +using System.Collections.Generic; + +/// +/// Unified metadata model for JSON-driven metric magnitudes generation +/// +public class MagnitudesMetadata +{ + public List Magnitudes { get; set; } = []; +} + +/// +/// Unified definition of a single metric magnitude for code generation. +/// Supports both simple extension generation and full metadata generation. +/// +public class MagnitudeDefinition +{ + public string Name { get; set; } = string.Empty; + public string Symbol { get; set; } = string.Empty; + public int Exponent { get; set; } +} diff --git a/Semantics.SourceGenerators/Models/PrecisionMetadata.cs b/Semantics.SourceGenerators/Models/PrecisionMetadata.cs new file mode 100644 index 0000000..1e96c60 --- /dev/null +++ b/Semantics.SourceGenerators/Models/PrecisionMetadata.cs @@ -0,0 +1,15 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Models; + +using System.Collections.Generic; + +/// +/// Metadata structure for precision types generation. +/// +public class PrecisionMetadata +{ + public List StorageTypes { get; set; } = []; +} diff --git a/Semantics.SourceGenerators/Models/UnitsMetadata.cs b/Semantics.SourceGenerators/Models/UnitsMetadata.cs new file mode 100644 index 0000000..91fe600 --- /dev/null +++ b/Semantics.SourceGenerators/Models/UnitsMetadata.cs @@ -0,0 +1,30 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Models; + +using System.Collections.Generic; + +public class UnitsMetadata +{ + public List UnitCategories { get; set; } = []; +} + +public class UnitCategory +{ + public string Name { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public List Units { get; set; } = []; +} + +public class UnitDefinition +{ + public string Name { get; set; } = string.Empty; + public string Symbol { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public string System { get; set; } = string.Empty; + public string Magnitude { get; set; } = "1"; + public string ConversionFactor { get; set; } = "1"; + public string Offset { get; set; } = "0"; +} diff --git a/Semantics.SourceGenerators/Semantics.SourceGenerators.csproj b/Semantics.SourceGenerators/Semantics.SourceGenerators.csproj new file mode 100644 index 0000000..fd6c85a --- /dev/null +++ b/Semantics.SourceGenerators/Semantics.SourceGenerators.csproj @@ -0,0 +1,31 @@ + + + + + netstandard2.0 + latest + true + $(NoWarn);CA1002;CA1304;CA1305;CA1307;CA1311;CA1805;RS1035;RS1041;RS1042 + true + false + false + enable + true + $(GetTargetPathDependsOn);GetDependencyTargetPaths + + + + + + + + + + + + + + + + + diff --git a/Semantics.SourceGenerators/Templates/ClassTemplate.cs b/Semantics.SourceGenerators/Templates/ClassTemplate.cs new file mode 100644 index 0000000..3974cb1 --- /dev/null +++ b/Semantics.SourceGenerators/Templates/ClassTemplate.cs @@ -0,0 +1,154 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Templates; + +using System.Collections.Generic; +using System.Linq; +using ktsu.CodeBlocker; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +internal class ClassTemplate : TemplateBase +{ + + public string BaseClass { get; set; } = string.Empty; + public List Interfaces { get; set; } = []; + public List Constraints { get; set; } = []; + public List Members { get; set; } = []; + public List NestedClasses { get; set; } = []; + + public override void WriteTo(CodeBlocker codeBlocker) + { + // specify class/struct/ record etc as keywords + base.WriteTo(codeBlocker); + + codeBlocker.Write($"{Name}"); + + WriteBaseClassAndInterfacesTo(codeBlocker); + WriteConstraintsTo(codeBlocker); + WriteMembersTo(codeBlocker); + } + + private void WriteBaseClassAndInterfacesTo(CodeBlocker codeBlocker) + { + bool hasBaseClass = !string.IsNullOrEmpty(BaseClass); + bool hasInterfaces = Interfaces.Count > 0; + bool hasBaseClassOrInterfaces = hasBaseClass || hasInterfaces; + if (hasBaseClassOrInterfaces) + { + codeBlocker.Write($" : "); + } + + List baseAndInterfaces = []; + if (hasBaseClass) + { + baseAndInterfaces.Add(BaseClass); + } + + baseAndInterfaces.AddRange(Interfaces); + + codeBlocker.Write(string.Join(", ", baseAndInterfaces)); + + if (hasBaseClassOrInterfaces) + { + codeBlocker.NewLine(); + } + } + + public void WriteConstraintsTo(CodeBlocker codeBlocker) + { + foreach (string constraint in Constraints) + { + codeBlocker.WriteLine($"\t{constraint}"); + } + } + + public void WriteMembersTo(CodeBlocker codeBlocker) + { + if (Members.Count == 0 && NestedClasses.Count == 0) + { + codeBlocker.WriteLine("{ }"); + return; + } + + IEnumerable sortedMembers = Members.OrderBy(MemberTemplate.MemberSortOrder); + + using (new ScopeWithTrailingSemicolon(codeBlocker)) + { + foreach (MemberTemplate member in sortedMembers) + { + member.WriteTo(codeBlocker); + codeBlocker.NewLine(); + } + + foreach (ClassTemplate nestedClass in NestedClasses) + { + codeBlocker.NewLine(); + nestedClass.WriteTo(codeBlocker); + } + } + } +} + +internal static class ClassTemplateExtensions +{ + public static CodeBlocker AddClass(this CodeBlocker codeBlocker, ClassTemplate classTemplate) + { + classTemplate.WriteTo(codeBlocker); + + return codeBlocker; + } + + public static CodeBlocker AddClassName(this CodeBlocker codeBlocker, ClassTemplate classTemplate) + { + codeBlocker.Write(classTemplate.Name); + return codeBlocker; + } + + public static CodeBlocker AddInheritance(this CodeBlocker codeBlocker, ClassTemplate classTemplate) + { + bool hasBaseClass = !string.IsNullOrEmpty(classTemplate.BaseClass); + bool hasInterfaces = classTemplate.Interfaces.Count > 0; + bool hasBaseClassOrInterfaces = hasBaseClass || hasInterfaces; + + if (hasBaseClassOrInterfaces) + { + codeBlocker.Write($" : "); + } + + List baseAndInterfaces = []; + if (hasBaseClass) + { + baseAndInterfaces.Add(classTemplate.BaseClass); + } + + baseAndInterfaces.AddRange(classTemplate.Interfaces); + + codeBlocker.Write(string.Join(", ", baseAndInterfaces)); + + if (hasBaseClassOrInterfaces) + { + codeBlocker.NewLine(); + } + + if (string.IsNullOrEmpty(classTemplate.BaseClass) && classTemplate.Interfaces.Count == 0) + { + return codeBlocker; + } + codeBlocker.Write(" : "); + if (!string.IsNullOrEmpty(classTemplate.BaseClass)) + { + codeBlocker.Write(classTemplate.BaseClass); + } + if (classTemplate.Interfaces.Count > 0) + { + if (!string.IsNullOrEmpty(classTemplate.BaseClass)) + { + codeBlocker.Write(", "); + } + codeBlocker.Write(string.Join(", ", classTemplate.Interfaces)); + } + return codeBlocker; + } +} diff --git a/Semantics.SourceGenerators/Templates/ConstructorTemplate.cs b/Semantics.SourceGenerators/Templates/ConstructorTemplate.cs new file mode 100644 index 0000000..1a9d539 --- /dev/null +++ b/Semantics.SourceGenerators/Templates/ConstructorTemplate.cs @@ -0,0 +1,84 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Templates; + +using System; +using System.Collections.Generic; +using ktsu.CodeBlocker; + +internal class ConstructorTemplate : MemberTemplate +{ + public List Parameters { get; set; } = []; + public List BaseParameters { get; set; } = []; + public Action? BodyFactory { get; set; } = (sw) => { }; + public override void WriteTo(CodeBlocker codeBlocker) + { + base.WriteTo(codeBlocker); + WriteParametersTo(codeBlocker); + WriteBaseParametersTo(codeBlocker); + WriteBodyTo(codeBlocker); + } + + private void WriteParametersTo(CodeBlocker codeBlocker) + { + List parameterStrings = []; + foreach (ParameterTemplate parameterTemplate in Parameters) + { + CodeBlocker parameterStringWriter = CodeBlocker.Create(); + parameterTemplate.WriteTo(parameterStringWriter); + parameterStrings.Add(parameterStringWriter.ToString()); + } + + codeBlocker.Write("("); + codeBlocker.Write(string.Join(", ", parameterStrings)); + codeBlocker.Write(")"); + } + + private void WriteBaseParametersTo(CodeBlocker codeBlocker) + { + if (BaseParameters.Count == 0) + { + return; // No base parameters to write + } + + codeBlocker.NewLine(); + codeBlocker.Write(": base"); + codeBlocker.Write("("); + codeBlocker.Write(string.Join(", ", BaseParameters)); + codeBlocker.Write(")"); + } + + private void WriteBodyTo(CodeBlocker codeBlocker) + { + // If there is no body, we just append a semicolon. Abstract methods for example. + if (BodyFactory is null) + { + codeBlocker.Write(";"); + return; + } + + CodeBlocker bodyStringWriter = CodeBlocker.Create(); + BodyFactory(bodyStringWriter); + string bodyString = bodyStringWriter.ToString(); + string[] bodyLines = bodyString.Split([Environment.NewLine], StringSplitOptions.None); + int bodyLineCount = bodyLines.Length; + + // If the body is empty, we just append an empty block. Virtual base methods for example. + if (bodyString.Length == 0) + { + codeBlocker.WriteLine(" { }"); + return; + } + + if (bodyLineCount > 1) + { + // If the body has multiple lines, we need to write a new line before the opening brace. + codeBlocker.NewLine(); + } + + // BodyFactory should provide the braces, or the expression body + codeBlocker.Write(bodyString); + } +} diff --git a/Semantics.SourceGenerators/Templates/FieldTemplate.cs b/Semantics.SourceGenerators/Templates/FieldTemplate.cs new file mode 100644 index 0000000..5eae17b --- /dev/null +++ b/Semantics.SourceGenerators/Templates/FieldTemplate.cs @@ -0,0 +1,29 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Templates; + +using ktsu.CodeBlocker; + +internal class FieldTemplate : MemberTemplate +{ + public override void WriteTo(CodeBlocker codeBlocker) + { + base.WriteTo(codeBlocker); + + if (!string.IsNullOrEmpty(DefaultValue)) + { + codeBlocker.Write(" = "); + if (DefaultValueIsQuoted) + { + codeBlocker.Write($"\"{DefaultValue}\""); + } + else + { + codeBlocker.Write(DefaultValue); + } + } + codeBlocker.WriteLine(";"); + } +} diff --git a/Semantics.SourceGenerators/Templates/MemberTemplate.cs b/Semantics.SourceGenerators/Templates/MemberTemplate.cs new file mode 100644 index 0000000..6773081 --- /dev/null +++ b/Semantics.SourceGenerators/Templates/MemberTemplate.cs @@ -0,0 +1,28 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Templates; + +using ktsu.CodeBlocker; + +internal abstract class MemberTemplate : TemplateBase +{ + public override void WriteTo(CodeBlocker codeBlocker) + { + base.WriteTo(codeBlocker); + string typeAndName = $"{Type} {Name}".Trim(); + codeBlocker.Write(typeAndName); + } + + internal static int MemberSortOrder(MemberTemplate memberTemplate) + { + return memberTemplate switch + { + FieldTemplate => 0, + PropertyTemplate => 1, + MethodTemplate => 2, + _ => 3 + }; + } +} diff --git a/Semantics.SourceGenerators/Templates/MethodTemplate.cs b/Semantics.SourceGenerators/Templates/MethodTemplate.cs new file mode 100644 index 0000000..f6c0e28 --- /dev/null +++ b/Semantics.SourceGenerators/Templates/MethodTemplate.cs @@ -0,0 +1,68 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Templates; + +using System; +using System.Collections.Generic; +using ktsu.CodeBlocker; + +internal class MethodTemplate : MemberTemplate +{ + public List Parameters { get; set; } = []; + public Action? BodyFactory { get; set; } + public override void WriteTo(CodeBlocker codeBlocker) + { + base.WriteTo(codeBlocker); + WriteParametersTo(codeBlocker); + WriteBodyTo(codeBlocker); + } + + private void WriteParametersTo(CodeBlocker codeBlocker) + { + List parameterStrings = []; + foreach (ParameterTemplate parameterTemplate in Parameters) + { + CodeBlocker parameterStringWriter = CodeBlocker.Create(); + parameterTemplate.WriteTo(parameterStringWriter); + parameterStrings.Add(parameterStringWriter.ToString()); + } + + codeBlocker.Write("("); + codeBlocker.Write(string.Join(", ", parameterStrings)); + codeBlocker.Write(")"); + } + + private void WriteBodyTo(CodeBlocker codeBlocker) + { + // If there is no body, we just append a semicolon. Abstract methods for example. + if (BodyFactory is null) + { + codeBlocker.Write(";"); + return; + } + + CodeBlocker bodyStringWriter = CodeBlocker.Create(); + BodyFactory(bodyStringWriter); + string bodyString = bodyStringWriter.ToString(); + string[] bodyLines = bodyString.Split([Environment.NewLine], StringSplitOptions.None); + int bodyLineCount = bodyLines.Length; + + // If the body is empty, we just append an empty block. Virtual base methods for example. + if (bodyLineCount == 0) + { + codeBlocker.WriteLine(" { }"); + return; + } + + if (bodyLineCount > 1) + { + // If the body has multiple lines, we need to write a new line before the opening brace. + codeBlocker.NewLine(); + } + + // BodyFactory should provide the braces, or the expression body + codeBlocker.Write(bodyString); + } +} diff --git a/Semantics.SourceGenerators/Templates/ParameterTemplate.cs b/Semantics.SourceGenerators/Templates/ParameterTemplate.cs new file mode 100644 index 0000000..d8ecc47 --- /dev/null +++ b/Semantics.SourceGenerators/Templates/ParameterTemplate.cs @@ -0,0 +1,29 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Templates; + +using ktsu.CodeBlocker; + +internal class ParameterTemplate : TemplateBase +{ + public override void WriteTo(CodeBlocker codeBlocker) + { + base.WriteTo(codeBlocker); + + codeBlocker.Write($"{Type} {Name}"); + if (!string.IsNullOrEmpty(DefaultValue)) + { + codeBlocker.Write($" = "); + if (DefaultValueIsQuoted) + { + codeBlocker.Write($"\"{DefaultValue}\""); + } + else + { + codeBlocker.Write(DefaultValue); + } + } + } +} diff --git a/Semantics.SourceGenerators/Templates/PropertyTemplate.cs b/Semantics.SourceGenerators/Templates/PropertyTemplate.cs new file mode 100644 index 0000000..012c213 --- /dev/null +++ b/Semantics.SourceGenerators/Templates/PropertyTemplate.cs @@ -0,0 +1,73 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Templates; + +using System; +using ktsu.CodeBlocker; + +internal class PropertyTemplate : MemberTemplate +{ + public static Action AutoGet = (sw) => sw.Write("get;"); + public static Action AutoSet = (sw) => sw.Write("set;"); + public static Action AutoInit = (sw) => sw.Write("init;"); + + public Action? GetterFactory { get; set; } + public Action? SetterFactory { get; set; } + public string? SetterBody { get; set; } + public override void WriteTo(CodeBlocker codeBlocker) + { + base.WriteTo(codeBlocker); + + if (GetterFactory is null && SetterFactory is null) + { + // If both are null, we assume it's an abstract property and terminate with a semicolon. + codeBlocker.WriteLine(";"); + } + else if ((GetterFactory == AutoGet || GetterFactory is null) && (SetterFactory is null || SetterFactory == AutoSet || SetterFactory == AutoInit)) + { + // both properties are auto or null, we can use the shorthand syntax. + codeBlocker.Write(" { "); + GetterFactory?.Invoke(codeBlocker); + if (GetterFactory is not null && SetterFactory is not null) + { + codeBlocker.Write(" "); + } + SetterFactory?.Invoke(codeBlocker); + codeBlocker.WriteLine(" }"); + return; + } + else + { + // either one or both properties are custom, we need to write them out in full. + codeBlocker.NewLine(); + codeBlocker.WriteLine("{"); + codeBlocker.NewLine(); + + if (GetterFactory == AutoGet) + { + // If the getter is auto, we can use the shorthand syntax. + GetterFactory(codeBlocker); + codeBlocker.NewLine(); + } + else if (GetterFactory is not null) + { + GetterFactory(codeBlocker); + } + + if (SetterFactory == AutoSet || SetterFactory == AutoInit) + { + // If the setter is auto, we can use the shorthand syntax. + SetterFactory(codeBlocker); + codeBlocker.NewLine(); + } + else if (SetterFactory is not null) + { + SetterFactory(codeBlocker); + } + + codeBlocker.WriteLine("}"); + } + } +} diff --git a/Semantics.SourceGenerators/Templates/SourceFileTemplate.cs b/Semantics.SourceGenerators/Templates/SourceFileTemplate.cs new file mode 100644 index 0000000..664b575 --- /dev/null +++ b/Semantics.SourceGenerators/Templates/SourceFileTemplate.cs @@ -0,0 +1,65 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Templates; +using System.Collections.Generic; +using System.Linq; +using ktsu.CodeBlocker; + +internal class SourceFileTemplate : TemplateBase +{ + public string FileName { get; set; } = string.Empty; + public string Namespace { get; set; } = string.Empty; + public List Usings { get; set; } = []; + public List Classes { get; set; } = []; +} + +internal static class SourceFileTemplateExtensions +{ + public static CodeBlocker AddSourceFile(this CodeBlocker codeBlocker, SourceFileTemplate template) + { + codeBlocker.AddTemplate(template); + codeBlocker.AddNamespace(template.Namespace); + codeBlocker.AddUsings(template.Usings); + codeBlocker.AddClasses(template.Classes); + return codeBlocker; + } + + public static CodeBlocker AddNamespace(this CodeBlocker codeBlocker, string namespaceName) + { + if (!string.IsNullOrEmpty(namespaceName)) + { + codeBlocker.WriteLine($"namespace {namespaceName};"); + codeBlocker.NewLine(); + } + + return codeBlocker; + } + + public static CodeBlocker AddUsings(this CodeBlocker codeBlocker, IEnumerable usings) + { + foreach (string usingDirective in usings) + { + codeBlocker.WriteLine($"using {usingDirective};"); + } + + if (usings.Any()) + { + codeBlocker.NewLine(); + } + + return codeBlocker; + } + + public static CodeBlocker AddClasses(this CodeBlocker codeBlocker, IEnumerable classes) + { + foreach (ClassTemplate classTemplate in classes) + { + codeBlocker.AddClass(classTemplate); + codeBlocker.NewLine(); + } + + return codeBlocker; + } +} diff --git a/Semantics.SourceGenerators/Templates/TemplateBase.cs b/Semantics.SourceGenerators/Templates/TemplateBase.cs new file mode 100644 index 0000000..43edf9d --- /dev/null +++ b/Semantics.SourceGenerators/Templates/TemplateBase.cs @@ -0,0 +1,64 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace Semantics.SourceGenerators.Templates; +using System.Collections.Generic; +using System.Linq; +using ktsu.CodeBlocker; + +internal abstract class TemplateBase +{ + public string Name { get; set; } = string.Empty; + public string Type { get; set; } = string.Empty; + public string DefaultValue { get; set; } = string.Empty; + public bool DefaultValueIsQuoted { get; set; } = false; + public List Attributes { get; set; } = []; + public List Keywords { get; set; } = []; + public List Comments { get; set; } = []; + + public virtual void WriteTo(CodeBlocker codeBlocker) + { + codeBlocker.AddComments(Comments); + codeBlocker.AddAttributes(Attributes); + codeBlocker.AddKeywords(Keywords); + } +} + +internal static class TemplateBaseExtensions +{ + public static CodeBlocker AddTemplate(this CodeBlocker codeBlocker, TemplateBase template) + { + codeBlocker.AddComments(template.Comments); + codeBlocker.AddAttributes(template.Attributes); + codeBlocker.AddKeywords(template.Keywords); + return codeBlocker; + } + + public static CodeBlocker AddComments(this CodeBlocker codeBlocker, IEnumerable comments) + { + foreach (string comment in comments) + { + codeBlocker.WriteLine(comment); + } + return codeBlocker; + } + + public static CodeBlocker AddAttributes(this CodeBlocker codeBlocker, IEnumerable attributes) + { + foreach (string attribute in attributes) + { + codeBlocker.Write($"[{attribute}] "); + } + return codeBlocker; + } + + public static CodeBlocker AddKeywords(this CodeBlocker codeBlocker, IEnumerable keywords) + { + if (keywords.Any()) + { + codeBlocker.Write(string.Join(" ", keywords) + " "); + } + return codeBlocker; + } +} diff --git a/Semantics.Strings/Semantics.Strings.csproj b/Semantics.Strings/Semantics.Strings.csproj index f59bcd8..4b57fd6 100644 --- a/Semantics.Strings/Semantics.Strings.csproj +++ b/Semantics.Strings/Semantics.Strings.csproj @@ -1,19 +1,17 @@ - - net10.0;net9.0;net8.0;net7.0;net6.0;net5.0;netstandard2.0;netstandard2.1; - $(NoWarn);CA1866;CA2249;IDE0057; - - - + net10.0;net9.0;net8.0;netstandard2.0;netstandard2.1 + $(NoWarn);CA1716;CA1866;CA2249;IDE0057 + - - + + + diff --git a/Semantics.Strings/Validation/Rules/LengthValidationRule.cs b/Semantics.Strings/Validation/Rules/LengthValidationRule.cs new file mode 100644 index 0000000..e15ba41 --- /dev/null +++ b/Semantics.Strings/Validation/Rules/LengthValidationRule.cs @@ -0,0 +1,27 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Strings; + +/// +/// Validation rule that checks string length constraints. +/// +/// Minimum allowed length (inclusive) +/// Maximum allowed length (inclusive) +public sealed class LengthValidationRule(int minLength, int maxLength) : ValidationRuleBase +{ + private readonly int _minLength = minLength; + private readonly int _maxLength = maxLength; + + /// + public override string Name => "Length"; + + /// + public override bool Validate(ISemanticString semanticString) + => semanticString.Length >= _minLength && semanticString.Length <= _maxLength; + + /// + public override string GetErrorMessage(ISemanticString semanticString) + => $"String length {semanticString.Length} is not between {_minLength} and {_maxLength}"; +} diff --git a/Semantics.Strings/Validation/Rules/PatternValidationRule.cs b/Semantics.Strings/Validation/Rules/PatternValidationRule.cs new file mode 100644 index 0000000..85be363 --- /dev/null +++ b/Semantics.Strings/Validation/Rules/PatternValidationRule.cs @@ -0,0 +1,27 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Strings; + +/// +/// Validation rule that checks for required patterns using regular expressions. +/// +/// The regular expression pattern to match +/// Regular expression options +public sealed class PatternValidationRule(string pattern, System.Text.RegularExpressions.RegexOptions options = System.Text.RegularExpressions.RegexOptions.None) : ValidationRuleBase +{ + private readonly string _pattern = pattern; + private readonly System.Text.RegularExpressions.Regex _regex = new(pattern, options); + + /// + public override string Name => "Pattern"; + + /// + public override bool Validate(ISemanticString semanticString) + => _regex.IsMatch(semanticString.ToString()); + + /// + public override string GetErrorMessage(ISemanticString semanticString) + => $"String '{semanticString}' does not match required pattern: {_pattern}"; +} diff --git a/Semantics.Strings/Validation/Attributes/SemanticStringValidationAttribute.cs b/Semantics.Strings/Validation/SemanticStringValidationAttribute.cs similarity index 100% rename from Semantics.Strings/Validation/Attributes/SemanticStringValidationAttribute.cs rename to Semantics.Strings/Validation/SemanticStringValidationAttribute.cs diff --git a/Semantics.Test/AcousticDirectionalityIndexTests.cs b/Semantics.Test/AcousticDirectionalityIndexTests.cs deleted file mode 100644 index deed0b4..0000000 --- a/Semantics.Test/AcousticDirectionalityIndexTests.cs +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public class AcousticDirectionalityIndexTests -{ - [TestMethod] - public void FromDirectivityFactor_And_ToDirectivityFactor_ShouldBeConsistent() - { - DirectionalityIndex diFromQ1 = DirectionalityIndex.FromDirectivityFactor(1.0); - Assert.AreEqual(0.0, diFromQ1.Value, 1e-12); - - DirectionalityIndex diFromQ10 = DirectionalityIndex.FromDirectivityFactor(10.0); - Assert.AreEqual(10.0, diFromQ10.Value, 1e-12); - - double qBack = diFromQ10.ToDirectivityFactor(); - Assert.AreEqual(10.0, qBack, 1e-12); - } - - [TestMethod] - public void FromDecibels_And_OnAxisGain_ShouldWork() - { - DirectionalityIndex di = DirectionalityIndex.FromDecibels(6.0); - Assert.AreEqual(6.0, di.Value, 1e-12); - Assert.AreEqual(6.0, di.OnAxisGain(), 1e-12); - } - - [TestMethod] - public void GetDirectivityPattern_Thresholds_ShouldMatch() - { - Assert.AreEqual( - "Omnidirectional (no directivity)", - DirectionalityIndex.FromDecibels(0.5).GetDirectivityPattern()); - - Assert.AreEqual( - "Slightly directional", - DirectionalityIndex.FromDecibels(1.0).GetDirectivityPattern()); - - Assert.AreEqual( - "Moderately directional", - DirectionalityIndex.FromDecibels(3.0).GetDirectivityPattern()); - - Assert.AreEqual( - "Highly directional", - DirectionalityIndex.FromDecibels(7.0).GetDirectivityPattern()); - - Assert.AreEqual( - "Very directional", - DirectionalityIndex.FromDecibels(10.0).GetDirectivityPattern()); - - Assert.AreEqual( - "Extremely directional (beam-like)", - DirectionalityIndex.FromDecibels(15.0).GetDirectivityPattern()); - } - - [TestMethod] - public void EstimateBeamwidth_ShouldApproximateExpected() - { - // DI = 0 dB => Q = 1 => beamwidth ≈ 58.3° - DirectionalityIndex di0 = DirectionalityIndex.FromDecibels(0.0); - Assert.AreEqual(58.3, di0.EstimateBeamwidth(), 1e-6); - - // DI = 6 dB => Q = 10^0.6 ≈ 3.981 => beamwidth ≈ 58.3 / sqrt(3.981) ≈ 29.22° - DirectionalityIndex di6 = DirectionalityIndex.FromDecibels(6.0); - Assert.AreEqual(29.22, di6.EstimateBeamwidth(), 1e-2); - } - - [TestMethod] - public void EstimateFrontToBackRatio_ShouldRespectCap() - { - DirectionalityIndex di10 = DirectionalityIndex.FromDecibels(10.0); - Assert.AreEqual(15.0, di10.EstimateFrontToBackRatio(), 1e-12); - - DirectionalityIndex di25 = DirectionalityIndex.FromDecibels(25.0); - Assert.AreEqual(30.0, di25.EstimateFrontToBackRatio(), 1e-12); - } - - [TestMethod] - public void GetTypicalApplication_Thresholds_ShouldMatch() - { - Assert.AreEqual( - "Ambient sound sources, subwoofers", - DirectionalityIndex.FromDecibels(1.0).GetTypicalApplication()); - - Assert.AreEqual( - "Monitor speakers, near-field applications", - DirectionalityIndex.FromDecibels(2.0).GetTypicalApplication()); - - Assert.AreEqual( - "Home audio, bookshelf speakers", - DirectionalityIndex.FromDecibels(4.0).GetTypicalApplication()); - - Assert.AreEqual( - "Studio monitors, PA speakers", - DirectionalityIndex.FromDecibels(6.0).GetTypicalApplication()); - - Assert.AreEqual( - "Horn-loaded speakers, line arrays", - DirectionalityIndex.FromDecibels(8.0).GetTypicalApplication()); - - Assert.AreEqual( - "Highly directional arrays, sound reinforcement", - DirectionalityIndex.FromDecibels(10.0).GetTypicalApplication()); - } - - [TestMethod] - public void CoverageAngle_ShouldComputeReasonableAngles() - { - // DI = 6 dB, level = -3 dB => adjusted DI = 3 dB => Q ≈ 1.995 => angle ≈ 90° - DirectionalityIndex di6 = DirectionalityIndex.FromDecibels(6.0); - double angleMinus3 = di6.CoverageAngle(-3.0); - Assert.AreEqual(90.0, angleMinus3, 1.0); - - // DI = 6 dB, level = -6 dB => adjusted DI = 0 dB => Q = 1 => angle = 0° - double angleMinus6 = di6.CoverageAngle(-6.0); - Assert.AreEqual(0.0, angleMinus6, 1e-9); - } -} diff --git a/Semantics.Test/AcousticImpedanceTests.cs b/Semantics.Test/AcousticImpedanceTests.cs deleted file mode 100644 index 336b452..0000000 --- a/Semantics.Test/AcousticImpedanceTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public sealed class AcousticImpedanceTests -{ - private const double Tolerance = 1e-10; - - [TestMethod] - public void FromRayls_And_FromPascalSecondsPerMeter_ShouldCreateSameValue() - { - AcousticImpedance a = AcousticImpedance.FromRayls(415.0); - AcousticImpedance b = AcousticImpedance.FromPascalSecondsPerMeter(415.0); - - Assert.AreEqual(a.Value, b.Value, Tolerance); - } - - [TestMethod] - public void Multiply_Density_And_SoundSpeed_ShouldMatch_Z_Equals_RhoC() - { - Density rho = Density.FromKilogramsPerCubicMeter(1.225); - SoundSpeed c = SoundSpeed.FromMetersPerSecond(343.0); - - AcousticImpedance z = AcousticImpedance.Multiply(rho, c); - AcousticImpedance z2 = AcousticImpedance.FromDensityAndSoundSpeed(rho, c); - - Assert.AreEqual(rho.Value * c.Value, z.Value, Tolerance); - Assert.AreEqual(z2.Value, z.Value, Tolerance); - } - - [TestMethod] - public void Divide_SoundPressure_By_Velocity_ShouldCreateImpedance() - { - SoundPressure p = SoundPressure.Create(2.0); - Velocity u = Velocity.Create(0.004819); - - AcousticImpedance z = AcousticImpedance.Divide(p, u); - - Assert.AreEqual(p.Value / u.Value, z.Value, 1e-12); - } - - [TestMethod] - public void CalculateSoundSpeed_And_Density_ShouldInvertRelations() - { - Density rho = Density.FromKilogramsPerCubicMeter(1.225); - SoundSpeed c = SoundSpeed.FromMetersPerSecond(343.0); - AcousticImpedance z = AcousticImpedance.FromDensityAndSoundSpeed(rho, c); - - SoundSpeed computedC = AcousticImpedance.CalculateSoundSpeed(z, rho); - Density computedRho = AcousticImpedance.CalculateDensity(z, c); - - Assert.AreEqual(c.Value, computedC.Value, Tolerance); - Assert.AreEqual(rho.Value, computedRho.Value, Tolerance); - } - - [TestMethod] - public void ForStandardAir_ShouldReturnReasonableValue() - { - AcousticImpedance zAir = AcousticImpedance.ForStandardAir(); - Assert.IsTrue(zAir.Value is > 400 and < 450, "Standard air acoustic impedance should be between 400 and 450 Rayls"); - } - - [TestMethod] - public void Dimension_ShouldBe_AcousticImpedance() - { - AcousticImpedance z = AcousticImpedance.Create(1.0); - Assert.AreEqual(PhysicalDimensions.AcousticImpedance, z.Dimension); - } -} - diff --git a/Semantics.Test/AcousticOperatorTests.cs b/Semantics.Test/AcousticOperatorTests.cs index ab18589..abbfe1d 100644 --- a/Semantics.Test/AcousticOperatorTests.cs +++ b/Semantics.Test/AcousticOperatorTests.cs @@ -4,6 +4,7 @@ namespace ktsu.Semantics.Test; +using ktsu.Semantics.Quantities; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -12,13 +13,36 @@ public sealed class AcousticOperatorTests [TestMethod] public void Frequency_From_Speed_DividedBy_Wavelength() { - SoundSpeed speed = SoundSpeed.FromMetersPerSecond(343.0); + // Semantic overloads widen implicitly to their base, so the + // Speed / Length => Frequency operator applies to acoustic types. + Speed speed = SoundSpeed.FromMeterPerSecond(343.0); Wavelength wavelength = Wavelength.Create(0.343); - Frequency f1 = speed / wavelength; // operator on Wavelength + Frequency f1 = speed / wavelength; Frequency f2 = Frequency.Create(speed.Value / wavelength.Value); Assert.AreEqual(f2.Value, f1.Value, 1e-12); } -} + [TestMethod] + public void Wavelength_From_Speed_DividedBy_Frequency() + { + Speed speed = SoundSpeed.FromMeterPerSecond(343.0); + Frequency frequency = Frequency.FromHertz(1000.0); + + Length wavelength = speed / frequency; + + Assert.AreEqual(0.343, wavelength.Value, 1e-12); + } + + [TestMethod] + public void Speed_From_Frequency_MultipliedBy_Wavelength() + { + Frequency frequency = Frequency.FromHertz(1000.0); + Wavelength wavelength = Wavelength.Create(0.343); + + Speed speed = frequency * wavelength; + + Assert.AreEqual(343.0, speed.Value, 1e-12); + } +} diff --git a/Semantics.Test/AdvancedAttributeValidationTests.cs b/Semantics.Test/AdvancedAttributeValidationTests.cs index 3341dfc..f21027f 100644 --- a/Semantics.Test/AdvancedAttributeValidationTests.cs +++ b/Semantics.Test/AdvancedAttributeValidationTests.cs @@ -4,8 +4,8 @@ namespace ktsu.Semantics.Test; -using System; using ktsu.Semantics.Strings; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] diff --git a/Semantics.Test/AdvancedErrorScenarioTests.cs b/Semantics.Test/AdvancedErrorScenarioTests.cs deleted file mode 100644 index 95e4137..0000000 --- a/Semantics.Test/AdvancedErrorScenarioTests.cs +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using System.Collections.Concurrent; -using System.Text; -using ktsu.Semantics.Paths; -using ktsu.Semantics.Strings; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public static class AdvancedErrorScenarioTests -{ - [TestClass] - public class SemanticQuantityErrorTests - { - [TestMethod] - public void SemanticQuantity_DivideByZero_ShouldThrow() - { - Length length = Length.FromMeters(10.0); - Length zeroLength = Length.FromMeters(0.0); - Assert.ThrowsExactly(() => _ = length / zeroLength); - } - - [TestMethod] - public void SemanticQuantity_NullArgument_ShouldThrow() - { - Length length = Length.FromMeters(10.0); - Length? nullLength = null; - Assert.ThrowsExactly(() => _ = length * nullLength!); - } - - [TestMethod] - public void SemanticQuantity_InfinityHandling_ShouldWork() - { - Length length = Length.FromMeters(double.PositiveInfinity); - Assert.IsTrue(double.IsPositiveInfinity(length.Value), "Positive infinity value should be preserved"); - } - - [TestMethod] - public void SemanticQuantity_NaNHandling_ShouldWork() - { - Length length = Length.FromMeters(double.NaN); - Assert.IsTrue(double.IsNaN(length.Value), "NaN value should be preserved"); - } - - [TestMethod] - public void SemanticQuantity_MaxMinValues_ShouldWork() - { - Length maxLength = Length.FromMeters(double.MaxValue); - Length minLength = Length.FromMeters(double.MinValue); - Assert.AreEqual(double.MaxValue, maxLength.Value); - Assert.AreEqual(double.MinValue, minLength.Value); - } - - [TestMethod] - public void SemanticQuantity_Overflow_ShouldHandle() - { - Length length = Length.FromMeters(double.MaxValue); - Length doubled = length * 2.0; - Assert.IsTrue(double.IsPositiveInfinity(doubled.Value), "Overflow should result in positive infinity"); - } - - [TestMethod] - public void SemanticQuantity_Underflow_ShouldHandle() - { - Length length = Length.FromMeters(double.MinValue); - Length doubled = length * 2.0; - Assert.IsTrue(double.IsNegativeInfinity(doubled.Value), "Underflow should result in negative infinity"); - } - - [TestMethod] - public void SemanticQuantity_Precision_ShouldMaintain() - { - Length length = Length.FromMeters(1.23456789); - Assert.AreEqual(1.23456789, length.Value); - } - } - - [TestClass] - public class SemanticStringAdvancedErrorTests - { - [RegexMatch(@"^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used via generic type references")] - private sealed partial record PasswordString : SemanticString { } - - [TestMethod] - public void PasswordString_ValidPassword_ShouldWork() - { - PasswordString password = SemanticString.Create("ValidP@ss123"); - Assert.IsNotNull(password); - Assert.AreEqual("ValidP@ss123", password.WeakString); - } - - [TestMethod] - public void PasswordString_TooShort_ShouldThrow() - { - Assert.ThrowsExactly(() => SemanticString.Create("Short1!")); - } - - [TestMethod] - public void PasswordString_NoUppercase_ShouldThrow() - { - Assert.ThrowsExactly(() => SemanticString.Create("lowercase1!")); - } - - [TestMethod] - public void PasswordString_NoSpecialChar_ShouldThrow() - { - Assert.ThrowsExactly(() => SemanticString.Create("NoSpecial1")); - } - - [TestMethod] - public void SemanticString_UnicodeHandling_ShouldWork() - { - ChineseString chineseString = SemanticString.Create("你好世界"); - Assert.IsNotNull(chineseString); - Assert.AreEqual("你好世界", chineseString.WeakString); - } - - [ChineseText] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used via generic type references")] - private sealed partial record ChineseString : SemanticString { } - - [AttributeUsage(AttributeTargets.Class)] - private sealed class ChineseTextAttribute : SemanticStringValidationAttribute - { - public override bool Validate(ISemanticString semanticString) - { - return semanticString.WeakString.All(c => c is >= (char)0x4E00 and <= (char)0x9FFF); - } - } - - [TestMethod] - public void SemanticString_MaxLengthStress_ShouldWork() - { - LongTestString longString = SemanticString.Create(new string('a', 10000)); - Assert.IsNotNull(longString); - Assert.AreEqual(10000, longString.Length); - } - - [Contains("a")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used via generic type references")] - private sealed partial record LongTestString : SemanticString { } - - [TestMethod] - public void SemanticString_CultureInvariance_ShouldWork() - { - CaseTestString testString = SemanticString.Create("Test"); - Assert.IsNotNull(testString); - Assert.AreEqual("Test", testString.WeakString); - - // Should fail with different case - Assert.ThrowsExactly(() => - SemanticString.Create("test")); - } - - [StartsWith("Test")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used via generic type references")] - private sealed partial record CaseTestString : SemanticString { } - - [TestMethod] - public void SemanticString_MemoryLeakPrevention_ShouldWork() - { - TestString testString = SemanticString.Create("test"); - Assert.IsNotNull(testString); - Assert.AreEqual("test", testString.WeakString); - } - - [StartsWith("test")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used via generic type references")] - private sealed partial record TestString : SemanticString { } - } - - [TestClass] - public class ValidationStrategyErrorTests - { - private sealed class FailingValidationStrategy : IValidationStrategy - { - public static bool Validate(object? value) => throw new InvalidOperationException("Validation failed"); - public bool Validate(ISemanticString semanticString, Type type) => throw new NotImplementedException(); - } - - private sealed class AlwaysFailStrategy : IValidationStrategy - { - public static bool Validate(object? _) => false; - public bool Validate(ISemanticString semanticString, Type type) => throw new NotImplementedException(); - } - - [TestMethod] - public void ValidationStrategy_ThrowingValidator_ShouldPropagate() - { - FailingValidationStrategy strategy = new(); - Assert.ThrowsExactly(() => FailingValidationStrategy.Validate("test")); - } - - [TestMethod] - public void ValidationStrategy_AlwaysFails_ShouldReturnFalse() - { - AlwaysFailStrategy strategy = new(); - bool result = AlwaysFailStrategy.Validate("test"); - Assert.IsFalse(result, "AlwaysFail strategy should return false"); - } - - [TestMethod] - public void ValidationStrategy_NullInput_ShouldHandle() - { - AlwaysFailStrategy strategy = new(); - bool result = AlwaysFailStrategy.Validate(null!); - Assert.IsFalse(result, "AlwaysFail strategy should return false for null input"); - } - } - - [TestClass] - public class PathValidationErrorTests - { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used via generic type references")] - private sealed partial record TestDirectoryPath : SemanticDirectoryPath { } - - [TestMethod] - public void SemanticPath_InvalidCharacters_ShouldThrow() - { - char[] invalidChars = Path.GetInvalidPathChars(); - Assert.ThrowsExactly(() => - { - if (invalidChars.Length > 0) - { - string invalidPath = $"test{invalidChars[0]}path"; - SemanticString.Create(invalidPath); - } - else - { - // If no invalid characters, throw manually for test - throw new ArgumentException("No invalid characters to test"); - } - }); - } - - [TestMethod] - public void SemanticPath_MaxPathLength_ShouldHandle() - { - try - { - // Test very long path (Windows has 260 char limit traditionally) - string longPath = new('a', 300); - TestDirectoryPath path = SemanticString.Create(longPath); - Assert.IsNotNull(path); - } - catch (ArgumentException) - { - // This is acceptable - path might be too long - Assert.IsTrue(true, "ArgumentException is acceptable for paths exceeding maximum length"); - } - } - - [TestMethod] - public void SemanticPath_EmptyString_ShouldHandle() - { - try - { - TestDirectoryPath emptyPath = SemanticString.Create(""); - Assert.IsNotNull(emptyPath); - } - catch (ArgumentException) - { - // This might be expected behavior - Assert.IsTrue(true, "ArgumentException is acceptable for empty path strings"); - } - } - - [TestMethod] - public void SemanticPath_WhitespaceOnly_ShouldHandle() - { - try - { - TestDirectoryPath whitespacePath = SemanticString.Create(" "); - Assert.IsNotNull(whitespacePath); - } - catch (ArgumentException) - { - // This might be expected behavior - Assert.IsTrue(true, "ArgumentException is acceptable for whitespace-only path strings"); - } - } - } - - [TestClass] - public class ConcurrencyErrorTests - { - [TestMethod] - public void SemanticString_ThreadSafety_ShouldWork() - { - const int threadCount = 20; - const int operationsPerThread = 1000; - Task[] tasks = new Task[threadCount]; - ConcurrentBag exceptions = []; - ConcurrentBag results = []; - - for (int i = 0; i < threadCount; i++) - { - int threadIndex = i; - tasks[i] = Task.Run(() => - { - try - { - for (int j = 0; j < operationsPerThread; j++) - { - ThreadTestString str = SemanticString.Create($"thread{threadIndex}operation{j}"); - results.Add(str.WeakString); - } - } - catch (ArgumentException ex) - { - exceptions.Add(ex); - } - catch (InvalidOperationException ex) - { - exceptions.Add(ex); - } - }, TestContext.CancellationTokenSource.Token); - } - - Task.WaitAll(tasks, TestContext.CancellationTokenSource.Token); - - Assert.IsEmpty(exceptions, $"Exceptions occurred: {string.Join(", ", exceptions.Select(e => e.Message))}"); - Assert.HasCount(threadCount * operationsPerThread, results); - } - - [StartsWith("thread")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Used via generic type references")] - private sealed partial record ThreadTestString : SemanticString { } - - [TestMethod] - public void SemanticQuantity_ThreadSafety_ShouldWork() - { - const int threadCount = 20; - const int operationsPerThread = 1000; - Task[] tasks = new Task[threadCount]; - ConcurrentBag exceptions = []; - ConcurrentBag results = []; - - for (int i = 0; i < threadCount; i++) - { - int threadIndex = i; - tasks[i] = Task.Run(() => - { - try - { - for (int j = 0; j < operationsPerThread; j++) - { - Length length = Length.FromMeters(threadIndex + j); - Length doubled = length * 2.0; - results.Add(doubled.Value); - } - } - catch (ArgumentException ex) - { - exceptions.Add(ex); - } - catch (InvalidOperationException ex) - { - exceptions.Add(ex); - } - catch (ArithmeticException ex) - { - exceptions.Add(ex); - } - }, TestContext.CancellationTokenSource.Token); - } - - Task.WaitAll(tasks, TestContext.CancellationTokenSource.Token); - - Assert.IsEmpty(exceptions, $"Exceptions occurred: {string.Join(", ", exceptions.Select(e => e.Message))}"); - Assert.HasCount(threadCount * operationsPerThread, results); - } - - public TestContext TestContext { get; set; } - } - - [TestClass] - public class ResourceManagementTests - { - [TestMethod] - public void PooledStringBuilder_ExceptionSafety_ShouldReturnToPool() - { - StringBuilder? sb = null; - try - { - sb = PooledStringBuilder.Get(); - sb.Append("test"); - throw new InvalidOperationException("Test exception"); - } - catch (InvalidOperationException) - { - // Expected exception - } - finally - { - if (sb != null) - { - PooledStringBuilder.Return(sb); - } - } - - // Verify the StringBuilder was returned to pool - StringBuilder newSb = PooledStringBuilder.Get(); - Assert.AreEqual(0, newSb.Length, "StringBuilder should have been cleared"); - } - - [TestMethod] - public void PooledStringBuilder_MultipleFinally_ShouldBeSafe() - { - StringBuilder sb = PooledStringBuilder.Get(); - try - { - sb.Append("test"); - } - finally - { - PooledStringBuilder.Return(sb); - PooledStringBuilder.Return(sb); // Double return should be safe - } - - Assert.IsTrue(true, "Double return should not cause issues"); - } - } -} diff --git a/Semantics.Test/AdvancedIntegrationTests.cs b/Semantics.Test/AdvancedIntegrationTests.cs deleted file mode 100644 index 626c1ef..0000000 --- a/Semantics.Test/AdvancedIntegrationTests.cs +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -/// -/// Advanced integration tests for multi-domain physics relationships. -/// Tests cross-domain consistency using available quantities and constants. -/// -[TestClass] -public class AdvancedIntegrationTests -{ - private const double Tolerance = 1e-10; - - #region Multi-Domain Physics Tests - - /// - /// Tests mechanical relationships using Newton's laws. - /// - [TestMethod] - public void MechanicalRelations_NewtonianMechanics() - { - // Arrange - Mass mass = Mass.FromKilograms(10.0); - Acceleration acceleration = Acceleration.FromMetersPerSecondSquared(9.8); - Velocity velocity = Velocity.FromMetersPerSecond(5.0); - - // Act - Calculate force using F = ma - Force force = mass * acceleration; - Momentum momentum = mass * velocity; - - // Assert - Assert.AreEqual(98.0, force.Value, Tolerance, "F = ma = 10 * 9.8 = 98N"); - Assert.AreEqual(50.0, momentum.Value, Tolerance, "p = mv = 10 * 5 = 50 kg⋅m/s"); - } - - /// - /// Tests electrical relationships using Ohm's law. - /// - [TestMethod] - public void ElectricalRelations_OhmsLaw() - { - // Arrange - ElectricPotential voltage = ElectricPotential.FromVolts(12.0); - ElectricCurrent current = ElectricCurrent.FromAmperes(2.0); - ElectricResistance resistance = ElectricResistance.FromOhms(6.0); - - // Act & Assert - Test Ohm's law: V = IR - ElectricPotential calculatedVoltage = current * resistance; - Assert.AreEqual(voltage.Value, calculatedVoltage.Value, Tolerance, "V = IR should hold"); - - // Test power calculation: P = VI - Power power = voltage * current; - Assert.AreEqual(24.0, power.Value, Tolerance, "P = VI = 12 * 2 = 24W"); - } - - /// - /// Tests thermal relationships and unit conversions. - /// - [TestMethod] - public void ThermalRelations_TemperatureConversions() - { - // Arrange - Temperature temperature1 = Temperature.FromKelvin(300.0); - Temperature temperature2 = Temperature.FromKelvin(350.0); - - // Act - Temperature deltaT = temperature2 - temperature1; - double tempCelsius = temperature1.ToCelsius(); - double tempFahrenheit = temperature1.ToFahrenheit(); - - // Assert - Assert.AreEqual(50.0, deltaT.Value, Tolerance, "Temperature difference should be 50K"); - Assert.AreEqual(26.85, tempCelsius, 0.1, "300K should be approximately 26.85°C"); - Assert.AreEqual(80.33, tempFahrenheit, 0.1, "300K should be approximately 80.33°F"); - } - - /// - /// Tests chemical concentration calculations. - /// - [TestMethod] - public void ChemicalRelations_ConcentrationCalculations() - { - // Arrange - AmountOfSubstance amount = AmountOfSubstance.FromMoles(2.0); - Volume volume = Volume.FromCubicMeters(0.001); // 1 liter - - // Act - Concentration concentration = amount / volume; - - // Assert - Assert.AreEqual(2000.0, concentration.Value, Tolerance, "Concentration = amount/volume = 2000 mol/m³"); - } - - /// - /// Tests acoustic frequency relationships. - /// - [TestMethod] - public void AcousticRelations_FrequencyWavelength() - { - // Arrange - Frequency frequency = Frequency.FromHertz(1000.0); - double speedOfSound = 343.0; // m/s at room temperature - - // Act - Calculate wavelength: λ = c/f - double wavelength = speedOfSound / frequency.Value; - - // Assert - Assert.AreEqual(0.343, wavelength, Tolerance, "λ = c/f = 343/1000 = 0.343m"); - } - - /// - /// Tests fluid dynamics Reynolds number calculation. - /// - [TestMethod] - public void FluidDynamicsRelations_ReynoldsNumber() - { - // Arrange - Density density = Density.FromKilogramsPerCubicMeter(1000.0); // Water - Velocity velocity = Velocity.FromMetersPerSecond(2.0); - Length length = Length.FromMeters(1.0); - DynamicViscosity viscosity = DynamicViscosity.Create(0.001); // Water at 20°C - - // Act - ReynoldsNumber reynolds = ReynoldsNumber.FromFluidProperties(density, velocity, length, viscosity); - - // Assert - Assert.AreEqual(2000000.0, reynolds.Value, 1.0, "Re = ρvL/μ = 1000*2*1/0.001 = 2,000,000"); - } - - /// - /// Tests optical luminous intensity. - /// - [TestMethod] - public void OpticalRelations_LuminousIntensity() - { - // Arrange - LuminousIntensity intensity = LuminousIntensity.FromCandelas(100.0); - - // Assert - Assert.AreEqual(100.0, intensity.Value, Tolerance, "Luminous intensity should be 100 cd"); - } - - /// - /// Tests nuclear radioactive activity. - /// - [TestMethod] - public void NuclearRelations_RadioactiveActivity() - { - // Arrange - RadioactiveActivity activity = RadioactiveActivity.FromBecquerels(1000.0); - - // Assert - Assert.AreEqual(1000.0, activity.Value, Tolerance, "Activity should be 1000 Bq"); - Assert.IsGreaterThan(0, activity.Value, "Activity should be positive"); - } - - #endregion - - #region Cross-Domain Integration Tests - - /// - /// Tests thermal-mechanical integration using pressure-temperature relationships. - /// - [TestMethod] - public void ThermalMechanicalIntegration_PressureTemperature() - { - // Arrange - Temperature temperature = Temperature.FromKelvin(300.0); - Pressure pressure = Pressure.FromPascals(PhysicalConstants.Generic.StandardAtmosphericPressure()); - - // Act - Test basic relationships - double temperatureRatio = temperature.Value / PhysicalConstants.Generic.StandardTemperature(); - double pressureRatio = pressure.Value / PhysicalConstants.Generic.StandardAtmosphericPressure(); - - // Assert - Assert.AreEqual(1.098, temperatureRatio, 0.001, "Temperature ratio at 300K vs STP"); - Assert.AreEqual(1.0, pressureRatio, Tolerance, "Pressure ratio at standard conditions"); - } - - /// - /// Tests electrical-thermal integration using Joule heating. - /// - [TestMethod] - public void ElectricalThermalIntegration_JouleHeating() - { - // Arrange - ElectricCurrent current = ElectricCurrent.FromAmperes(10.0); - ElectricResistance resistance = ElectricResistance.FromOhms(5.0); - Time time = Time.FromSeconds(60.0); - - // Act - Calculate power and energy - ElectricPotential voltage = current * resistance; - Power power = voltage * current; - Energy energy = Energy.Create(power.Value * time.Value); - - // Assert - Assert.AreEqual(50.0, voltage.Value, Tolerance, "V = IR = 10 * 5 = 50V"); - Assert.AreEqual(500.0, power.Value, Tolerance, "P = VI = 50 * 10 = 500W"); - Assert.AreEqual(30000.0, energy.Value, Tolerance, "E = Pt = 500 * 60 = 30000J"); - } - - /// - /// Tests chemical-thermal integration using gas constants. - /// - [TestMethod] - public void ChemicalThermalIntegration_GasLaws() - { - // Arrange - AmountOfSubstance amount = AmountOfSubstance.FromMoles(1.0); - Temperature temperature = Temperature.FromKelvin(PhysicalConstants.Generic.StandardTemperature()); - double gasConstant = PhysicalConstants.Generic.GasConstant(); - - // Act - Calculate RT for ideal gas - double RT = gasConstant * temperature.Value; - - // Assert - Use more appropriate tolerance for floating point multiplication - Assert.AreEqual(2271.098, RT, 0.01, "RT = 8.314 * 273.15 = 2271.098 J/mol"); - } - - /// - /// Tests acoustic-mechanical integration using sound pressure and velocity. - /// - [TestMethod] - public void AcousticMechanicalIntegration_SoundWaves() - { - // Arrange - Frequency frequency = Frequency.FromHertz(1000.0); - Velocity velocity = Velocity.FromMetersPerSecond(343.0); // Speed of sound - - // Act - Calculate wavelength - double wavelength = velocity.Value / frequency.Value; - - // Assert - Assert.AreEqual(0.343, wavelength, Tolerance, "λ = v/f = 343/1000 = 0.343m"); - } - - #endregion -} diff --git a/Semantics.Test/AdvancedUtilityTests.cs b/Semantics.Test/AdvancedUtilityTests.cs index 11e1ba7..221a05a 100644 --- a/Semantics.Test/AdvancedUtilityTests.cs +++ b/Semantics.Test/AdvancedUtilityTests.cs @@ -4,9 +4,9 @@ namespace ktsu.Semantics.Test; +using ktsu.Semantics.Paths; using System.Collections.Concurrent; using System.Text; -using ktsu.Semantics.Paths; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] diff --git a/Semantics.Test/AttributeValidationTests.cs b/Semantics.Test/AttributeValidationTests.cs index 04a8438..426f3dc 100644 --- a/Semantics.Test/AttributeValidationTests.cs +++ b/Semantics.Test/AttributeValidationTests.cs @@ -4,8 +4,8 @@ namespace ktsu.Semantics.Test; -using System; using ktsu.Semantics.Strings; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] diff --git a/Semantics.Test/AudioEngineeringTests.cs b/Semantics.Test/AudioEngineeringTests.cs index b471c45..6cc92fa 100644 --- a/Semantics.Test/AudioEngineeringTests.cs +++ b/Semantics.Test/AudioEngineeringTests.cs @@ -4,6 +4,8 @@ namespace ktsu.Semantics.Test; +using ktsu.Semantics.Quantities; +using ktsu.Semantics.Quantities.Units; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] @@ -74,9 +76,47 @@ public void Cents_OctaveIsFrequencyRatioOfTwo() [TestMethod] public void RatioPercent_RoundTrip() { - Assert.AreEqual(50.0, Ratio.Create(0.5).ToPercent().Value, Tolerance); - Assert.AreEqual(0.5, Percent.Create(50.0).ToRatio().Value, Tolerance); - Assert.AreEqual(0.25, Percent.FromFraction(0.25).ToFraction(), Tolerance); + // Percent is a unit of the Dimensionless dimension, not a separate type. + Assert.AreEqual(0.5, Ratio.FromPercent(50.0).Value, Tolerance); + Assert.AreEqual(50.0, Ratio.Create(0.5).In(Units.Percent), Tolerance); + Assert.AreEqual(0.25, Ratio.FromPercent(25.0).Value, Tolerance); + } + + [TestMethod] + public void Gain_Is_A_Generated_Ratio_Overload() + { + // Gain widens implicitly to the generated Ratio and is V0 (non-negative + // through the guarded From{Unit} factories). + Gain gain = Gain.Create(2.0); + Ratio asRatio = gain; + Assert.AreEqual(2.0, asRatio.Value, Tolerance); + Assert.ThrowsExactly(() => Gain.FromDimensionless(-1.0)); + } + + [TestMethod] + public void Gain_Cascading_Multiplies() + { + Gain total = Gain.Create(2.0) * Gain.Create(0.5); + Assert.AreEqual(1.0, total.Value, Tolerance); + Assert.AreEqual(Gain.Unity.Value, total.Value, Tolerance); + } + + [TestMethod] + public void Decibels_FromPowerRatio_Takes_The_Generated_Ratio() + { + Decibels db = Decibels.FromPowerRatio(Ratio.Create(100.0)); + Assert.AreEqual(20.0, db.Value, Tolerance); + } + + [TestMethod] + public void Ratio_Arithmetic_Comes_From_The_Generator() + { + // The audio-specific Ratio struct is gone; the generated Dimensionless + // Ratio supplies the arithmetic. + Ratio mix = Ratio.Create(0.5) * Ratio.Create(0.5); + Assert.AreEqual(0.25, mix.Value, Tolerance); + // Same-type division yields the raw storage ratio, as for every quantity. + Assert.AreEqual(2.0, Ratio.Create(1.0) / Ratio.Create(0.5), Tolerance); } [TestMethod] diff --git a/Semantics.Test/ElectricalQuantitiesTests.cs b/Semantics.Test/ElectricalQuantitiesTests.cs deleted file mode 100644 index 62d8c28..0000000 --- a/Semantics.Test/ElectricalQuantitiesTests.cs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public class ElectricalQuantitiesTests -{ - [TestMethod] - public void ElectricCurrent_BasicOperations_ShouldWork() - { - // Test creation - ElectricCurrent current1 = ElectricCurrent.FromAmperes(5.0); - ElectricCurrent current2 = ElectricCurrent.FromAmperes(3.0); - - // Test arithmetic operations - ElectricCurrent sum = current1 + current2; - ElectricCurrent difference = current1 - current2; - ElectricCurrent scaled = current1 * 2.0; - ElectricCurrent divided = current1 / 2.0; - - Assert.AreEqual(8.0, sum.Value, 1e-10); - Assert.AreEqual(2.0, difference.Value, 1e-10); - Assert.AreEqual(10.0, scaled.Value, 1e-10); - Assert.AreEqual(2.5, divided.Value, 1e-10); - } - - [TestMethod] - public void ElectricPotential_BasicOperations_ShouldWork() - { - // Test creation from different units - ElectricPotential voltage1 = ElectricPotential.FromVolts(12.0); - ElectricPotential voltage2 = ElectricPotential.FromVolts(5.0); - - // Test arithmetic operations - ElectricPotential sum = voltage1 + voltage2; - ElectricPotential difference = voltage1 - voltage2; - - Assert.AreEqual(17.0, sum.Value, 1e-10); - Assert.AreEqual(7.0, difference.Value, 1e-10); - } - - [TestMethod] - public void ElectricResistance_OhmsLawCalculations_ShouldWork() - { - // Test Ohm's law relationships - ElectricPotential voltage = ElectricPotential.FromVolts(12.0); - ElectricCurrent current = ElectricCurrent.FromAmperes(2.0); - ElectricResistance resistance = ElectricResistance.FromOhms(6.0); - - // V = I * R - ElectricPotential calculatedVoltage = current * resistance; - Assert.AreEqual(voltage.Value, calculatedVoltage.Value, 1e-10); - - // R = V / I - ElectricResistance calculatedResistance = ElectricResistance.FromOhms(voltage.Value / current.Value); - Assert.AreEqual(resistance.Value, calculatedResistance.Value, 1e-10); - } - - [TestMethod] - public void ElectricCapacitance_BasicOperations_ShouldWork() - { - ElectricCapacitance capacitance1 = ElectricCapacitance.FromFarads(1e-6); - ElectricCapacitance capacitance2 = ElectricCapacitance.FromFarads(0.5e-6); - - // Test series and parallel capacitance - double seriesCapacitance = 1.0 / ((1.0 / capacitance1.Value) + (1.0 / capacitance2.Value)); - double parallelCapacitance = capacitance1.Value + capacitance2.Value; - - Assert.AreEqual(3.33333e-7, seriesCapacitance, 1e-12); - Assert.AreEqual(1.5e-6, parallelCapacitance, 1e-15); - } - - [TestMethod] - public void ElectricCharge_BasicOperations_ShouldWork() - { - ElectricCharge charge1 = ElectricCharge.FromCoulombs(5.0); - ElectricCharge charge2 = ElectricCharge.FromCoulombs(3.0); - - ElectricCharge sum = charge1 + charge2; - ElectricCharge difference = charge1 - charge2; - - Assert.AreEqual(8.0, sum.Value, 1e-10); - Assert.AreEqual(2.0, difference.Value, 1e-10); - } - - [TestMethod] - public void ElectricField_BasicOperations_ShouldWork() - { - ElectricField field1 = ElectricField.FromVoltsPerMeter(100.0); - ElectricField field2 = ElectricField.FromVoltsPerMeter(50.0); - - ElectricField sum = field1 + field2; - ElectricField difference = field1 - field2; - - Assert.AreEqual(150.0, sum.Value, 1e-10); - Assert.AreEqual(50.0, difference.Value, 1e-10); - } - - [TestMethod] - public void ElectricConductivity_BasicOperations_ShouldWork() - { - ElectricConductivity conductivity = ElectricConductivity.FromSiemensPerMeter(1e6); - double resistivity = 1.0 / conductivity.Value; - - Assert.AreEqual(1e-6, resistivity, 1e-15); - } - - [TestMethod] - public void ElectricFlux_BasicOperations_ShouldWork() - { - ElectricFlux flux1 = ElectricFlux.Create(2.0); - ElectricFlux flux2 = ElectricFlux.Create(1.0); - - ElectricFlux sum = flux1 + flux2; - ElectricFlux difference = flux1 - flux2; - - Assert.AreEqual(3.0, sum.Value, 1e-10); - Assert.AreEqual(1.0, difference.Value, 1e-10); - } - - [TestMethod] - public void Permittivity_BasicOperations_ShouldWork() - { - Permittivity permittivity = Permittivity.FromFaradsPerMeter(8.854e-12); - - // Test relative permittivity calculation (vacuum permittivity) - Assert.AreEqual(8.854e-12, permittivity.Value, 1e-17); - } - - [TestMethod] - public void ElectricPowerDensity_BasicOperations_ShouldWork() - { - ElectricPowerDensity powerDensity1 = ElectricPowerDensity.Create(1000.0); - ElectricPowerDensity powerDensity2 = ElectricPowerDensity.Create(500.0); - - ElectricPowerDensity sum = powerDensity1 + powerDensity2; - ElectricPowerDensity difference = powerDensity1 - powerDensity2; - - Assert.AreEqual(1500.0, sum.Value, 1e-10); - Assert.AreEqual(500.0, difference.Value, 1e-10); - } - - [TestMethod] - public void ImpedanceAC_BasicOperations_ShouldWork() - { - ImpedanceAC impedance1 = ImpedanceAC.FromOhms(50.0); - ImpedanceAC impedance2 = ImpedanceAC.FromOhms(25.0); - - // Test series and parallel impedance - ImpedanceAC seriesImpedance = impedance1 + impedance2; - double parallelImpedance = 1.0 / ((1.0 / impedance1.Value) + (1.0 / impedance2.Value)); - - Assert.AreEqual(75.0, seriesImpedance.Value, 1e-10); - Assert.AreEqual(16.666667, parallelImpedance, 1e-6); - } - - [TestMethod] - public void ElectricalQuantities_ZeroValues_ShouldWork() - { - ElectricCurrent zeroCurrent = ElectricCurrent.FromAmperes(0.0); - ElectricPotential zeroVoltage = ElectricPotential.FromVolts(0.0); - ElectricResistance zeroResistance = ElectricResistance.FromOhms(0.0); - - Assert.AreEqual(0.0, zeroCurrent.Value); - Assert.AreEqual(0.0, zeroVoltage.Value); - Assert.AreEqual(0.0, zeroResistance.Value); - } - - [TestMethod] - public void ElectricalQuantities_NegativeValues_ShouldWork() - { - ElectricCurrent negativeCurrent = ElectricCurrent.FromAmperes(-5.0); - ElectricPotential negativeVoltage = ElectricPotential.FromVolts(-12.0); - - Assert.AreEqual(-5.0, negativeCurrent.Value); - Assert.AreEqual(-12.0, negativeVoltage.Value); - - // Test negation operator - ElectricCurrent positiveFromNegation = -negativeCurrent; - Assert.AreEqual(5.0, positiveFromNegation.Value); - } - - [TestMethod] - public void ElectricalQuantities_ToString_ShouldWork() - { - ElectricCurrent current = ElectricCurrent.FromAmperes(2.5); - ElectricPotential voltage = ElectricPotential.FromVolts(12.0); - - Assert.IsNotNull(current.ToString()); - Assert.IsNotNull(voltage.ToString()); - Assert.Contains("2.5", current.ToString()); - Assert.Contains("12", voltage.ToString()); - } -} diff --git a/Semantics.Test/ErrorHandlingTests.cs b/Semantics.Test/ErrorHandlingTests.cs index eaf1842..c6f6bbb 100644 --- a/Semantics.Test/ErrorHandlingTests.cs +++ b/Semantics.Test/ErrorHandlingTests.cs @@ -4,10 +4,11 @@ namespace ktsu.Semantics.Test; +using ktsu.Semantics.Strings; +using ktsu.Semantics.Paths; +using ktsu.Semantics.Quantities; using System; using System.IO; -using ktsu.Semantics.Paths; -using ktsu.Semantics.Strings; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] diff --git a/Semantics.Test/IntegrationTests.cs b/Semantics.Test/IntegrationTests.cs deleted file mode 100644 index 31ed66b..0000000 --- a/Semantics.Test/IntegrationTests.cs +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -/// -/// Integration tests demonstrating quantities from multiple physics domains working together. -/// -[TestClass] -public class IntegrationTests -{ - - /// - /// Tests ideal gas law using thermal, chemical, and mechanical quantities. - /// PV = nRT demonstration. - /// - [TestMethod] - public void IdealGasLaw_CombinesThreeDomainsCorrectly() - { - // Arrange - Standard conditions - Pressure pressure = Pressure.FromPascals(PhysicalConstants.Generic.StandardAtmosphericPressure()); // 1 atm (Mechanical) - Volume volume = Volume.FromCubicMeters(0.0224); // 22.4 L (Mechanical) - Temperature temperature = Temperature.FromKelvin(PhysicalConstants.Generic.StandardTemperature()); // 0°C (Thermal) - double gasConstant = PhysicalConstants.Generic.GasConstant(); - - // Act - Calculate amount of substance using ideal gas law: n = PV/RT - double pressureValue = pressure.Value; - double volumeValue = volume.Value; - double temperatureValue = temperature.Value; - double calculatedMoles = pressureValue * volumeValue / (gasConstant * temperatureValue); - AmountOfSubstance expectedMoles = AmountOfSubstance.FromMoles(1.0); // Should be ~1 mole at STP - - // Assert - Assert.AreEqual(expectedMoles.Value, calculatedMoles, 0.01, - "Ideal gas law should predict approximately 1 mole at STP"); - } - - /// - /// Tests electromagnetic energy calculations combining electrical and optical domains. - /// E = hf for photons and electrical power relationships. - /// - [TestMethod] - public void ElectromagneticEnergy_CombinesElectricalAndOpticalDomains() - { - // Arrange - Green light LED - Frequency frequency = Frequency.FromHertz(5.45e14); // Green light ~550nm (Acoustic) - ElectricPotential voltage = ElectricPotential.FromVolts(3.3); // LED forward voltage (Electrical) - ElectricCurrent current = ElectricCurrent.FromAmperes(0.02); // 20mA LED current (Electrical) - - // Act - Calculate photon energy and electrical power - Energy photonEnergy = Frequency.GetPhotonEnergy(frequency); // E = hf - Power electricalPower = voltage * current; // P = VI - - // Assert - Verify reasonable values - Assert.IsGreaterThan(0, photonEnergy.Value, "Photon energy should be positive"); - Assert.AreEqual(0.066, electricalPower.Value, 0.001, "Electrical power should be 66mW"); - - // Green light photon should be around 2.25 eV = 3.6e-19 J - Assert.IsTrue(photonEnergy.Value is > 3e-19 and < 4e-19, - "Green light photon energy should be around 3.6e-19 J"); - } - - /// - /// Tests acoustic-mechanical relationships using sound in different media. - /// Combines acoustic, mechanical, and fluid dynamics domains. - /// - [TestMethod] - public void SoundPropagation_CombinesAcousticMechanicalAndFluidDomains() - { - // Arrange - Sound in air - Frequency frequency = Frequency.FromHertz(1000.0); // 1 kHz tone (Acoustic) - Density airDensity = Density.FromKilogramsPerCubicMeter(1.225); // Air density (Mechanical) - SoundSpeed soundSpeed = SoundSpeed.FromMetersPerSecond(343.0); // Sound speed in air (Acoustic) - - // Act - Calculate acoustic properties - Wavelength wavelength = soundSpeed / frequency; // λ = v/f - AcousticImpedance acousticImpedance = AcousticImpedance.FromDensityAndSoundSpeed(airDensity, soundSpeed); // Z = ρc - - // Assert - Assert.AreEqual(0.343, wavelength.Value, 0.001, "1 kHz wavelength in air should be ~34.3 cm"); - Assert.IsTrue(acousticImpedance.Value is > 400 and < 450, - "Air acoustic impedance should be around 415 kg/(m²·s)"); - } - - /// - /// Tests nuclear decay and energy release combining nuclear and thermal domains. - /// - [TestMethod] - public void NuclearDecay_CombinesNuclearAndThermalDomains() - { - // Arrange - Radioactive source - RadioactiveActivity initialActivity = RadioactiveActivity.FromBecquerels(1000.0); // 1000 Bq (Nuclear) - Time halfLife = Time.FromSeconds(3600.0); // 1 hour half-life (Mechanical) - Time time = Time.FromSeconds(7200.0); // 2 hours elapsed (Mechanical) - - // Act - Calculate decay (simplified calculation for 2 half-lives) - double halfLives = time.Value / halfLife.Value; // 2 half-lives - double remainingFraction = Math.Pow(0.5, halfLives); - RadioactiveActivity remainingActivity = RadioactiveActivity.FromBecquerels(initialActivity.Value * remainingFraction); - double decayedAtoms = initialActivity.Value - remainingActivity.Value; // Simplified decay count - - // Convert to thermal energy (simplified - each decay releases ~1 MeV = 1.6e-13 J) - Energy energyPerDecay = Energy.FromJoules(1.6e-13); - Energy totalThermalEnergy = Energy.Create(decayedAtoms * energyPerDecay.Value); - - // Assert - Assert.AreEqual(250.0, remainingActivity.Value, 1.0, - "Activity should be 1/4 original after 2 half-lives"); - Assert.IsGreaterThan(0, totalThermalEnergy.Value, "Thermal energy should be released from decay"); - } - - /// - /// Tests fluid dynamics and thermodynamics in heat transfer. - /// Combines fluid dynamics, thermal, and mechanical domains. - /// - [TestMethod] - public void HeatTransferInFlowingFluid_CombinesMultipleDomains() - { - // Arrange - Hot water flowing through pipe - Temperature fluidTemp = Temperature.FromCelsius(80.0); // Hot water (Thermal) - Temperature ambientTemp = Temperature.FromCelsius(20.0); // Ambient temperature (Thermal) - VolumetricFlowRate flowRate = VolumetricFlowRate.FromLitersPerSecond(2.0); // 2 L/s flow (Fluid Dynamics) - Density waterDensity = Density.FromKilogramsPerCubicMeter(1000.0); // Water density (Mechanical) - SpecificHeat specificHeat = SpecificHeat.FromJoulesPerKilogramKelvin(4186.0); // Water specific heat (Thermal) - - // Act - Calculate mass flow rate and thermal power manually - double massFlowRateValue = waterDensity.Value * flowRate.Value; // ρ * V̇ = ṁ - Temperature tempDifference = fluidTemp - ambientTemp; - Power thermalPower = Power.Create(massFlowRateValue * specificHeat.Value * tempDifference.Value); - - // Assert - Assert.AreEqual(2.0, massFlowRateValue, 0.01, "Mass flow rate should be 2 kg/s for water"); - Assert.IsTrue(thermalPower.Value is > 500000 and < 600000, - "Thermal power should be around 500 kW"); - } - - /// - /// Tests chemical reaction kinetics with thermal effects. - /// Combines chemical and thermal domains. - /// - [TestMethod] - public void ChemicalReactionKinetics_CombinesChemicalAndThermalDomains() - { - // Arrange - Arrhenius equation parameters - double preExponentialFactor = 1e12; // A factor in s⁻¹ - ActivationEnergy activationEnergy = ActivationEnergy.Create(50000.0); // 50 kJ/mol (Chemical) - Temperature lowTemp = Temperature.FromCelsius(25.0); // Room temperature (Thermal) - Temperature highTemp = Temperature.FromCelsius(100.0); // Elevated temperature (Thermal) - - // Act - Calculate rate constants at different temperatures - RateConstant lowTempRate = RateConstant.FromArrheniusEquation(preExponentialFactor, activationEnergy, lowTemp); - RateConstant highTempRate = RateConstant.FromArrheniusEquation(preExponentialFactor, activationEnergy, highTemp); - - // Assert - Higher temperature should give higher rate constant - Assert.IsGreaterThan(lowTempRate.Value, -highTempRate.Value, "Rate constant should increase with temperature"); - - // Rate should increase significantly (exponentially) with temperature - double rateRatio = highTempRate.Value / lowTempRate.Value; - Assert.IsGreaterThan(5.0, -rateRatio, "Rate constant should increase substantially with 75°C temperature increase"); - } - - /// - /// Tests mechanical energy conversion with electrical generation. - /// Combines mechanical and electrical domains. - /// - [TestMethod] - public void MechanicalToElectricalConversion_CombinesMechanicalAndElectricalDomains() - { - // Arrange - Generator parameters - Torque torque = Torque.FromNewtonMeters(100.0); // Generator torque (Mechanical) - AngularVelocity angularVelocity = AngularVelocity.FromRadiansPerSecond(150.0); // ~1430 RPM (Mechanical) - double efficiency = 0.85; // 85% efficiency - - // Act - Calculate mechanical and electrical power manually - double mechanicalPowerValue = torque.Value * angularVelocity.Value; // P = τω - Power mechanicalPower = Power.Create(mechanicalPowerValue); - Power electricalPower = Power.Create(mechanicalPower.Value * efficiency); - - // For a 3-phase generator at 400V line-to-line - ElectricPotential voltage = ElectricPotential.FromVolts(400.0); // Line voltage (Electrical) - double currentValue = electricalPower.Value / (voltage.Value * Math.Sqrt(3)); // 3-phase current (simplified) - - // Assert - Assert.AreEqual(15000.0, mechanicalPower.Value, 0.1, "Mechanical power should be 15 kW"); - Assert.AreEqual(12750.0, electricalPower.Value, 1.0, "Electrical power should be 12.75 kW"); - Assert.IsTrue(currentValue is > 18 and < 19, - "3-phase current should be around 18.4 A"); - } - - /// - /// Tests optical fiber transmission with electrical and optical domains. - /// - [TestMethod] - public void OpticalFiberTransmission_CombinesOpticalAndElectricalDomains() - { - // Arrange - Fiber optic system - Power laserPower = Power.FromWatts(0.001); // 1 mW laser (Electrical) - double fiberLengthKm = 10.0; // 10 km fiber (simplified) - double attenuationPerKm = 0.2; // 0.2 dB/km attenuation - RefractiveIndex coreRefractiveIndex = RefractiveIndex.Create(1.46); // Silica core (Optical) - RefractiveIndex claddingRefractiveIndex = RefractiveIndex.Create(1.45); // Silica cladding (Optical) - - // Act - Calculate numerical aperture and received power - double numericalAperture = Math.Sqrt(Math.Pow(coreRefractiveIndex.Value, 2) - - Math.Pow(claddingRefractiveIndex.Value, 2)); - - // Calculate power loss due to attenuation - double totalAttenuation = attenuationPerKm * fiberLengthKm; - double powerLossFactor = Math.Pow(10, -totalAttenuation / 10); // Convert dB to linear - Power receivedPower = Power.Create(laserPower.Value * powerLossFactor); - - // Assert - Assert.IsTrue(numericalAperture is > 0.1 and < 0.2, - "Numerical aperture should be around 0.17"); - Assert.IsLessThan(laserPower.Value, -receivedPower.Value, "Received power should be less than transmitted power"); - Assert.IsGreaterThan(0.0006, -receivedPower.Value, "Should receive more than 0.6 mW after 10 km with 0.2 dB/km loss"); - } -} diff --git a/Semantics.Test/MechanicalQuantitiesTests.cs b/Semantics.Test/MechanicalQuantitiesTests.cs deleted file mode 100644 index 890fbae..0000000 --- a/Semantics.Test/MechanicalQuantitiesTests.cs +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -/// -/// Tests for mechanical quantities with physical constants integration. -/// -[TestClass] -public class MechanicalQuantitiesTests -{ - private const double Tolerance = 1e-10; - - /// - /// Tests Newton's second law: F = ma - /// - [TestMethod] - public void Force_NewtonsSecondLaw_CalculatesCorrectly() - { - // Arrange - Mass mass = Mass.Create(10.0); // 10 kg - Acceleration acceleration = Acceleration.Create(5.0); // 5 m/s² - - // Act - Force force = Force.FromMassAndAcceleration(mass, acceleration); - double forceValue = force.In(Units.Newton); - - // Assert - Assert.AreEqual(50.0, forceValue, Tolerance, "F = ma should give 10 kg × 5 m/s² = 50 N"); - } - - /// - /// Tests weight calculation using standard gravity. - /// - [TestMethod] - public void Force_WeightCalculation_UsesStandardGravity() - { - // Arrange - Mass mass = Mass.Create(1.0); // 1 kg - - // Act - Force weight = Force.FromWeight(mass); - double weightValue = weight.In(Units.Newton); - - // Expected: 1 kg × 9.80665 m/s² = 9.80665 N - double expectedWeight = PhysicalConstants.Mechanical.StandardGravity; - - // Assert - Assert.AreEqual(expectedWeight, weightValue, Tolerance, - "Weight should use standard gravity constant"); - } - - /// - /// Tests acceleration calculation from force and mass. - /// - [TestMethod] - public void Force_AccelerationCalculation_InversesCorrectly() - { - // Arrange - Force force = Force.Create(100.0); // 100 N - Mass mass = Mass.Create(20.0); // 20 kg - - // Act - Acceleration acceleration = force.GetAcceleration(mass); - double accelerationValue = acceleration.In(Units.MetersPerSecondSquared); - - // Assert - Assert.AreEqual(5.0, accelerationValue, Tolerance, "a = F/m should give 100 N / 20 kg = 5 m/s²"); - } - - /// - /// Tests kinetic energy calculation: KE = ½mv² - /// - [TestMethod] - public void Energy_KineticEnergy_CalculatesCorrectly() - { - // Arrange - Mass mass = Mass.Create(2.0); // 2 kg - Velocity velocity = Velocity.Create(10.0); // 10 m/s - - // Act - Energy kineticEnergy = Energy.FromKineticEnergy(mass, velocity); - double energyValue = kineticEnergy.In(Units.Joule); - - // Expected: ½ × 2 kg × (10 m/s)² = 100 J - Assert.AreEqual(100.0, energyValue, Tolerance, "KE = ½mv² should give ½ × 2 × 100 = 100 J"); - } - - /// - /// Tests potential energy calculation: PE = mgh - /// - [TestMethod] - public void Energy_PotentialEnergy_UsesStandardGravity() - { - // Arrange - Mass mass = Mass.Create(5.0); // 5 kg - Length height = Length.Create(10.0); // 10 m - - // Act - Energy potentialEnergy = Energy.FromPotentialEnergy(mass, height); - double energyValue = potentialEnergy.In(Units.Joule); - - // Expected: 5 kg × 9.80665 m/s² × 10 m = 490.3325 J - double expectedEnergy = 5.0 * PhysicalConstants.Mechanical.StandardGravity * 10.0; - - // Assert - Assert.AreEqual(expectedEnergy, energyValue, Tolerance, - "PE = mgh should use standard gravity constant"); - } - - /// - /// Tests work-energy theorem: W = F·d - /// - [TestMethod] - public void Energy_WorkDone_CalculatesCorrectly() - { - // Arrange - Force force = Force.Create(50.0); // 50 N - Length distance = Length.Create(4.0); // 4 m - - // Act - Energy work = Energy.FromWork(force, distance); - double workValue = work.In(Units.Joule); - - // Assert - Assert.AreEqual(200.0, workValue, Tolerance, "W = F·d should give 50 N × 4 m = 200 J"); - } - - /// - /// Tests velocity calculation from kinetic energy. - /// - [TestMethod] - public void Energy_VelocityFromKineticEnergy_InversesCorrectly() - { - // Arrange - Energy kineticEnergy = Energy.Create(72.0); // 72 J - Mass mass = Mass.Create(2.0); // 2 kg - - // Act - Velocity velocity = kineticEnergy.GetVelocityFromKineticEnergy(mass); - double velocityValue = velocity.In(Units.MetersPerSecond); - - // Expected: v = √(2E/m) = √(2×72/2) = √72 = 6√2 ≈ 8.485 m/s - double expectedVelocity = Math.Sqrt(72.0); - - // Assert - Assert.AreEqual(expectedVelocity, velocityValue, Tolerance, - "v = √(2E/m) should give √72 ≈ 8.485 m/s"); - } - - /// - /// Tests pressure calculation from force and area: P = F/A - /// - [TestMethod] - public void Pressure_ForceAndArea_CalculatesCorrectly() - { - // Arrange - Force force = Force.Create(1000.0); // 1000 N - Area area = Area.Create(2.0); // 2 m² - - // Act - Use the operator instead of the removed method - Pressure pressure = force / area; - double pressureValue = pressure.In(Units.Pascal); - - // Assert - Assert.AreEqual(500.0, pressureValue, Tolerance, "P = F/A should give 1000 N / 2 m² = 500 Pa"); - } - - /// - /// Tests standard atmospheric pressure constant. - /// - [TestMethod] - public void Pressure_StandardAtmospheric_UsesCorrectConstant() - { - // Act - Pressure atmosphericPressure = Pressure.StandardAtmospheric(); - double pressureValue = atmosphericPressure.In(Units.Pascal); - - // Assert - Assert.AreEqual(PhysicalConstants.Mechanical.StandardAtmosphericPressure, pressureValue, Tolerance, - "Standard atmospheric pressure should use the correct constant"); - } - - /// - /// Tests force calculation from pressure and area. - /// - [TestMethod] - public void Pressure_ForceCalculation_InversesCorrectly() - { - // Arrange - Pressure pressure = Pressure.Create(2000.0); // 2000 Pa - Area area = Area.Create(0.5); // 0.5 m² - - // Act - Force force = pressure.GetForce(area); - double forceValue = force.In(Units.Newton); - - // Assert - Assert.AreEqual(1000.0, forceValue, Tolerance, "F = P×A should give 2000 Pa × 0.5 m² = 1000 N"); - } - - /// - /// Tests hydrostatic pressure calculation: P = ρgh - /// - [TestMethod] - public void Pressure_HydrostaticPressure_CalculatesCorrectly() - { - // Arrange - Water density and 10m depth - Density waterDensity = Density.Create(1000.0); // 1000 kg/m³ - Length depth = Length.Create(10.0); // 10 m - - // Act - Pressure hydrostaticPressure = Pressure.FromHydrostaticPressure(waterDensity, depth); - double pressureValue = hydrostaticPressure.In(Units.Pascal); - - // Expected: 1000 kg/m³ × 9.80665 m/s² × 10 m = 98066.5 Pa - double expectedPressure = 1000.0 * PhysicalConstants.Mechanical.StandardGravity * 10.0; - - // Assert - Assert.AreEqual(expectedPressure, pressureValue, Tolerance, - "Hydrostatic pressure should use standard gravity constant"); - } - - /// - /// Tests that physical constants are consistent across calculations. - /// - [TestMethod] - public void PhysicalConstants_MechanicalConstants_AreConsistent() - { - // Test that weight calculation and Units.StandardGravity use the same value - Mass oneMass = Mass.Create(1.0); - Force weight = Force.FromWeight(oneMass); - double weightInNewtons = weight.In(Units.Newton); - - // Should equal the standard gravity constant - Assert.AreEqual(PhysicalConstants.Mechanical.StandardGravity, weightInNewtons, Tolerance, - "Weight calculation should be consistent with standard gravity constant"); - - // Test atmospheric pressure consistency - Pressure standardPressure = Pressure.StandardAtmospheric(); - double pressureInPascals = standardPressure.In(Units.Pascal); - - Assert.AreEqual(PhysicalConstants.Mechanical.StandardAtmosphericPressure, pressureInPascals, Tolerance, - "Standard atmospheric pressure should be consistent"); - } -} diff --git a/Semantics.Test/NuclearQuantitiesTests.cs b/Semantics.Test/NuclearQuantitiesTests.cs deleted file mode 100644 index 4224837..0000000 --- a/Semantics.Test/NuclearQuantitiesTests.cs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using ktsu.Semantics; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public sealed class NuclearQuantitiesTests -{ - [TestMethod] - public void AbsorbedDose_Conversions_And_Basics() - { - AbsorbedDose fromRads = AbsorbedDose.FromRads(100.0); - Assert.AreEqual(1.0, fromRads.Value, 1e-12); - - AbsorbedDose fromMicro = AbsorbedDose.FromMicrograys(1_000_000.0); - Assert.AreEqual(1.0, fromMicro.Value, 1e-12); - - Energy energy = Energy.FromJoules(10.0); - Mass mass = Mass.FromKilograms(2.0); - AbsorbedDose fromEoverM = AbsorbedDose.FromEnergyAndMass(energy, mass); - Assert.AreEqual(5.0, fromEoverM.Value, 1e-12); - - Assert.ThrowsExactly(() => AbsorbedDose.FromEnergyAndMass(energy, Mass.FromKilograms(0.0))); - } - - [TestMethod] - public void AbsorbedDose_Calculations_And_Exceptions() - { - AbsorbedDose dose = AbsorbedDose.FromGrays(2.0); - Mass mass = Mass.FromKilograms(3.0); - Energy energy = dose.CalculateEnergyAbsorbed(mass); - Assert.AreEqual(6.0, energy.Value, 1e-12); - - EquivalentDose ed = dose.CalculateEquivalentDose(5.0); - Assert.AreEqual(10.0, ed.Value, 1e-12); - - Time t2 = Time.FromSeconds(2.0); - double rate = AbsorbedDose.FromGrays(4.0).CalculateDoseRate(t2); - Assert.AreEqual(2.0, rate, 1e-12); - Assert.ThrowsExactly(() => AbsorbedDose.FromGrays(1.0).CalculateDoseRate(Time.FromSeconds(0.0))); - - Mass massFromEnergy = AbsorbedDose.FromGrays(5.0).CalculateMass(Energy.FromJoules(10.0)); - Assert.AreEqual(2.0, massFromEnergy.Value, 1e-12); - Assert.ThrowsExactly(() => AbsorbedDose.FromGrays(0.0).CalculateMass(Energy.FromJoules(1.0))); - - Assert.IsTrue(AbsorbedDose.FromGrays(5.0).IsPotentiallyLethal()); - Assert.IsFalse(AbsorbedDose.FromGrays(3.0).IsPotentiallyLethal()); - Assert.IsTrue(AbsorbedDose.FromGrays(0.3).CausesRadiationSickness()); - Assert.IsFalse(AbsorbedDose.FromGrays(0.2).CausesRadiationSickness()); - } - - [TestMethod] - public void EquivalentDose_Conversions_And_Calculations() - { - EquivalentDose fromRems = EquivalentDose.FromRems(100.0); - Assert.AreEqual(1.0, fromRems.Value, 1e-12); - - EquivalentDose fromMicro = EquivalentDose.FromMicrosieverts(1_000_000.0); - Assert.AreEqual(1.0, fromMicro.Value, 1e-12); - - EquivalentDose fromDwr = EquivalentDose.FromAbsorbedDoseAndWeightingFactor(AbsorbedDose.FromGrays(2.0), 10.0); - Assert.AreEqual(20.0, fromDwr.Value, 1e-12); - - AbsorbedDose backToD = EquivalentDose.FromSieverts(10.0).CalculateAbsorbedDose(5.0); - Assert.AreEqual(2.0, backToD.Value, 1e-12); - Assert.ThrowsExactly(() => EquivalentDose.FromSieverts(1.0).CalculateAbsorbedDose(0.0)); - - double rate = EquivalentDose.FromSieverts(4.0).CalculateDoseRate(Time.FromSeconds(2.0)); - Assert.AreEqual(2.0, rate, 1e-12); - Assert.ThrowsExactly(() => EquivalentDose.FromSieverts(1.0).CalculateDoseRate(Time.FromSeconds(0.0))); - - Assert.IsTrue(EquivalentDose.FromSieverts(0.03).ExceedsAnnualWorkerLimit()); - Assert.IsFalse(EquivalentDose.FromSieverts(0.01).ExceedsAnnualWorkerLimit()); - Assert.IsTrue(EquivalentDose.FromSieverts(0.002).ExceedsPublicLimit()); - Assert.IsFalse(EquivalentDose.FromSieverts(0.0005).ExceedsPublicLimit()); - - EquivalentDose eff = EquivalentDose.FromSieverts(10.0).CalculateEffectiveDoseContribution(0.12); - Assert.AreEqual(1.2, eff.Value, 1e-12); - } - - [TestMethod] - public void Exposure_Conversions_And_Calculations() - { - Exposure fromR = Exposure.FromRoentgens(1.0); - Assert.AreEqual(2.58e-4, fromR.Value, 1e-12); - - Exposure frommR = Exposure.FromMilliroentgens(1000.0); - Assert.AreEqual(2.58e-4, frommR.Value, 1e-12); - - ElectricCharge q = ElectricCharge.Create(1.0); - Mass m = Mass.FromKilograms(2.0); - Exposure x = Exposure.FromChargeAndMass(q, m); - Assert.AreEqual(0.5, x.Value, 1e-12); - Assert.ThrowsExactly(() => Exposure.FromChargeAndMass(q, Mass.FromKilograms(0.0))); - - ElectricCharge calcQ = Exposure.FromCoulombsPerKilogram(0.2).CalculateCharge(Mass.FromKilograms(2.0)); - Assert.AreEqual(0.4, calcQ.Value, 1e-12); - - Assert.ThrowsExactly(() => Exposure.FromCoulombsPerKilogram(0.0).CalculateMass(ElectricCharge.Create(1.0))); - Mass calcM = Exposure.FromCoulombsPerKilogram(0.5).CalculateMass(ElectricCharge.Create(1.0)); - Assert.AreEqual(2.0, calcM.Value, 1e-12); - - double rate = Exposure.FromCoulombsPerKilogram(2.0).CalculateExposureRate(Time.FromSeconds(4.0)); - Assert.AreEqual(0.5, rate, 1e-12); - Assert.ThrowsExactly(() => Exposure.FromCoulombsPerKilogram(1.0).CalculateExposureRate(Time.FromSeconds(0.0))); - - AbsorbedDose doseInAir = Exposure.FromRoentgens(1.0).ToAbsorbedDoseInAir(); - Assert.AreEqual(0.00876, doseInAir.Value, 1e-12); - } -} diff --git a/Semantics.Test/PathValidationAttributeTests.cs b/Semantics.Test/PathValidationAttributeTests.cs index 69229ac..5cfc7ed 100644 --- a/Semantics.Test/PathValidationAttributeTests.cs +++ b/Semantics.Test/PathValidationAttributeTests.cs @@ -4,9 +4,9 @@ namespace ktsu.Semantics.Test; -using System; -using ktsu.Semantics.Paths; using ktsu.Semantics.Strings; +using ktsu.Semantics.Paths; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] diff --git a/Semantics.Test/PerformanceBenchmarks.cs b/Semantics.Test/PerformanceBenchmarks.cs deleted file mode 100644 index 03a124d..0000000 --- a/Semantics.Test/PerformanceBenchmarks.cs +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using System.Diagnostics; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -/// -/// Performance benchmarks for common physics quantity operations. -/// These tests measure performance characteristics but don't fail on timing. -/// -[TestClass] -public class PerformanceBenchmarks -{ - private const int IterationCount = 100000; - private const int WarmupIterations = 1000; - - /// - /// Benchmarks unit conversion performance across different domains. - /// - [TestMethod] - public void BenchmarkUnitConversions() - { - // Warmup - for (int i = 0; i < WarmupIterations; i++) - { - Temperature temp = Temperature.FromCelsius(25.0); - double _ = temp.ToFahrenheit(); - } - - Stopwatch stopwatch = Stopwatch.StartNew(); - - // Benchmark different unit conversions - for (int i = 0; i < IterationCount; i++) - { - // Temperature conversions - Temperature temp = Temperature.FromCelsius(25.0 + (i * 0.001)); - double fahrenheit = temp.ToFahrenheit(); - double kelvin = temp.ToKelvin(); - - // Length conversions - Length length = Length.FromMeters(1.0 + (i * 0.0001)); - double feet = length.Value * 3.28084; // Convert to feet - double inches = length.Value * 39.3701; // Convert to inches - - // Energy conversions - Energy energy = Energy.FromJoules(1000.0 + (i * 0.01)); - double calories = energy.Value / 4184.0; // Convert to calories - double kwh = energy.Value / 3600000.0; // Convert to kWh - } - - stopwatch.Stop(); - double operationsPerSecond = IterationCount * 6 / stopwatch.Elapsed.TotalSeconds; // 6 conversions per iteration - - Console.WriteLine($"Unit conversions: {operationsPerSecond:F0} operations/second"); - Assert.IsGreaterThan(100000, operationsPerSecond, "Unit conversions should be fast"); - } - - /// - /// Benchmarks arithmetic operations between quantities. - /// - [TestMethod] - public void BenchmarkArithmeticOperations() - { - // Warmup - for (int i = 0; i < WarmupIterations; i++) - { - Force force1 = Force.FromNewtons(100.0); - Force force2 = Force.FromNewtons(50.0); - Force _ = force1 + force2; - } - - Stopwatch stopwatch = Stopwatch.StartNew(); - - for (int i = 0; i < IterationCount; i++) - { - // Force arithmetic - Force force1 = Force.FromNewtons(100.0 + (i * 0.001)); - Force force2 = Force.FromNewtons(50.0 + (i * 0.0005)); - Force forceSum = force1 + force2; - Force forceDiff = force1 - force2; - - // Energy arithmetic - Energy energy1 = Energy.FromJoules(1000.0 + (i * 0.01)); - Energy energy2 = Energy.FromJoules(500.0 + (i * 0.005)); - Energy energySum = energy1 + energy2; - Energy energyDiff = energy1 - energy2; - - // Temperature arithmetic - Temperature temp1 = Temperature.FromKelvin(300.0 + (i * 0.001)); - Temperature temp2 = Temperature.FromKelvin(273.15); - Temperature tempDiff = temp1 - temp2; - } - - stopwatch.Stop(); - double operationsPerSecond = IterationCount * 7 / stopwatch.Elapsed.TotalSeconds; - - Console.WriteLine($"Arithmetic operations: {operationsPerSecond:F0} operations/second"); - Assert.IsGreaterThan(500000, operationsPerSecond, "Arithmetic operations should be very fast"); - } - - /// - /// Benchmarks physics relationship calculations. - /// - [TestMethod] - public void BenchmarkPhysicsRelationships() - { - // Warmup - for (int i = 0; i < WarmupIterations; i++) - { - Mass mass = Mass.FromKilograms(10.0); - Acceleration acceleration = Acceleration.FromMetersPerSecondSquared(9.8); - Force _ = mass * acceleration; - } - - Stopwatch stopwatch = Stopwatch.StartNew(); - - for (int i = 0; i < IterationCount; i++) - { - // F = ma - Mass mass = Mass.FromKilograms(10.0 + (i * 0.0001)); - Acceleration acceleration = Acceleration.FromMetersPerSecondSquared(9.8 + (i * 0.00001)); - Force force = mass * acceleration; - - // P = VI - ElectricPotential voltage = ElectricPotential.FromVolts(12.0 + (i * 0.0001)); - ElectricCurrent current = ElectricCurrent.FromAmperes(2.0 + (i * 0.00001)); - Power power = voltage * current; - - // E = mc² (simplified) - Energy energy = Energy.FromKineticEnergy(mass, Velocity.FromMetersPerSecond(10.0)); - - // λ = v/f - SoundSpeed soundSpeed = SoundSpeed.FromMetersPerSecond(343.0); - Frequency frequency = Frequency.FromHertz(1000.0 + (i * 0.1)); - Wavelength wavelength = soundSpeed / frequency; - } - - stopwatch.Stop(); - double operationsPerSecond = IterationCount * 4 / stopwatch.Elapsed.TotalSeconds; - - Console.WriteLine($"Physics relationships: {operationsPerSecond:F0} operations/second"); - Assert.IsGreaterThan(200000, operationsPerSecond, "Physics relationships should be efficient"); - } - - /// - /// Benchmarks quantity creation from different input types. - /// - [TestMethod] - public void BenchmarkQuantityCreation() - { - // Warmup - for (int i = 0; i < WarmupIterations; i++) - { - Temperature _ = Temperature.FromCelsius(25.0); - } - - Stopwatch stopwatch = Stopwatch.StartNew(); - - for (int i = 0; i < IterationCount; i++) - { - // Different creation methods - Temperature temp1 = Temperature.FromCelsius(25.0 + (i * 0.001)); - Temperature temp2 = Temperature.FromKelvin(298.15 + (i * 0.001)); - Temperature temp3 = Temperature.FromFahrenheit(77.0 + (i * 0.002)); - - Force force1 = Force.FromNewtons(100.0 + (i * 0.001)); - Force force2 = Force.FromNewtons((22.48 + (i * 0.0002)) * 4.448); // Convert pounds to newtons - - Energy energy1 = Energy.FromJoules(1000.0 + (i * 0.01)); - Energy energy2 = Energy.FromJoules((239.0 + (i * 0.002)) * 4184.0); // Convert calories to joules - Energy energy3 = Energy.FromJoules((0.000278 + (i * 0.000001)) * 3600000.0); // Convert kWh to joules - } - - stopwatch.Stop(); - double operationsPerSecond = IterationCount * 8 / stopwatch.Elapsed.TotalSeconds; - - Console.WriteLine($"Quantity creation: {operationsPerSecond:F0} operations/second"); - Assert.IsGreaterThan(300000, operationsPerSecond, "Quantity creation should be fast"); - } - - /// - /// Benchmarks constant access performance. - /// - [TestMethod] - public void BenchmarkConstantAccess() - { - // Warmup - for (int i = 0; i < WarmupIterations; i++) - { - double _ = PhysicalConstants.Generic.SpeedOfLight(); - } - - Stopwatch stopwatch = Stopwatch.StartNew(); - - for (int i = 0; i < IterationCount; i++) - { - // Access various constants - double c = PhysicalConstants.Generic.SpeedOfLight(); - double g = PhysicalConstants.Generic.StandardGravity(); - double R = PhysicalConstants.Generic.GasConstant(); - double Na = PhysicalConstants.Generic.AvogadroNumber(); - double h = PhysicalConstants.Generic.PlanckConstant(); - - // Use the values to prevent compiler warnings - _ = c + g + R + Na + h; - } - - stopwatch.Stop(); - double operationsPerSecond = IterationCount * 5 / stopwatch.Elapsed.TotalSeconds; - - Console.WriteLine($"Constant access: {operationsPerSecond:F0} operations/second"); - Assert.IsGreaterThan(1000000, operationsPerSecond, "Constant access should be very fast"); - } - - /// - /// Benchmarks cross-domain calculations (ideal gas law). - /// - [TestMethod] - public void BenchmarkCrossDomainCalculations() - { - // Warmup - for (int i = 0; i < WarmupIterations; i++) - { - Pressure p = Pressure.FromPascals(PhysicalConstants.Generic.StandardAtmosphericPressure()); - Volume v = Volume.FromCubicMeters(0.0224); - Temperature t = Temperature.FromKelvin(PhysicalConstants.Generic.StandardTemperature()); - double r = PhysicalConstants.Generic.GasConstant(); - double _ = p.Value * v.Value / (r * t.Value); - } - - Stopwatch stopwatch = Stopwatch.StartNew(); - - for (int i = 0; i < IterationCount; i++) - { - // Ideal gas law calculation combining multiple domains - Pressure pressure = Pressure.FromPascals(PhysicalConstants.Generic.StandardAtmosphericPressure() + (i * 0.1)); // Mechanical - Volume volume = Volume.FromCubicMeters(0.0224 + (i * 0.000001)); // Mechanical - Temperature temperature = Temperature.FromKelvin(PhysicalConstants.Generic.StandardTemperature() + (i * 0.001)); // Thermal - double gasConstant = PhysicalConstants.Generic.GasConstant(); // Chemical - - // PV = nRT calculation - double moles = pressure.Value * volume.Value / (gasConstant * temperature.Value); - - // Convert to amount of substance - AmountOfSubstance amount = AmountOfSubstance.FromMoles(moles); - } - - stopwatch.Stop(); - double operationsPerSecond = IterationCount / stopwatch.Elapsed.TotalSeconds; - - Console.WriteLine($"Cross-domain calculations: {operationsPerSecond:F0} operations/second"); - Assert.IsGreaterThan(100000, operationsPerSecond, "Cross-domain calculations should be efficient"); - } -} diff --git a/Semantics.Test/PerformanceRegressionTests.cs b/Semantics.Test/PerformanceRegressionTests.cs deleted file mode 100644 index ea56a56..0000000 --- a/Semantics.Test/PerformanceRegressionTests.cs +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using System.Diagnostics; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -/// -/// Performance regression tests to monitor library performance over time. -/// These tests establish performance baselines and detect regressions. -/// -[TestClass] -public class PerformanceRegressionTests -{ - private const int LargeIterationCount = 1000000; - private const int MediumIterationCount = 100000; - private const int WarmupIterations = 10000; - - /// - /// Performance baseline for basic quantity creation across all domains. - /// Target: > 1M operations/second per domain (CI-friendly). - /// - [TestMethod] - public void PerformanceBaseline_QuantityCreation() - { - Dictionary results = []; - - // Test each domain's basic quantity creation - Dictionary domainTests = new() - { - ["Mechanical"] = () => { Force _ = Force.FromNewtons(100.0); }, - ["Electrical"] = () => { ElectricCurrent _ = ElectricCurrent.FromAmperes(5.0); }, - ["Thermal"] = () => { Temperature _ = Temperature.FromKelvin(300.0); }, - ["Chemical"] = () => { AmountOfSubstance _ = AmountOfSubstance.FromMoles(1.0); }, - ["Acoustic"] = () => { Frequency _ = Frequency.FromHertz(1000.0); }, - ["Optical"] = () => { LuminousIntensity _ = LuminousIntensity.FromCandelas(100.0); }, - ["Nuclear"] = () => { RadioactiveActivity _ = RadioactiveActivity.FromBecquerels(1000.0); }, - ["FluidDynamics"] = () => { ReynoldsNumber _ = ReynoldsNumber.Create(10000.0); } - }; - - foreach (KeyValuePair domain in domainTests) - { - // Warmup - for (int i = 0; i < WarmupIterations; i++) - { - domain.Value(); - } - - // Benchmark - Stopwatch stopwatch = Stopwatch.StartNew(); - for (int i = 0; i < LargeIterationCount; i++) - { - domain.Value(); - } - stopwatch.Stop(); - - double operationsPerSecond = LargeIterationCount / stopwatch.Elapsed.TotalSeconds; - results[domain.Key] = operationsPerSecond; - - Console.WriteLine($"{domain.Key} creation: {operationsPerSecond:F0} ops/sec"); - - // Performance regression test - should be > 1M ops/sec (CI-friendly target) - Assert.IsGreaterThan(1000000, -operationsPerSecond, $"{domain.Key} quantity creation performance regression: {operationsPerSecond:F0} ops/sec < 1M ops/sec"); - } - } - - /// - /// Performance baseline for unit conversions. - /// Target: > 3M conversions/second (CI-friendly). - /// - [TestMethod] - public void PerformanceBaseline_UnitConversions() - { - // Pre-create objects outside the measurement to avoid allocation overhead - Temperature temp = Temperature.FromKelvin(300.0); - Mass mass = Mass.FromKilograms(1.0); - - var conversions = new[] - { - new { name = "Temperature K→C", action = new Action(() => { - double _ = temp.ToCelsius(); - })}, - new { name = "Temperature K→F", action = new Action(() => { - double _ = temp.ToFahrenheit(); - })}, - new { name = "Mass kg→lb", action = new Action(() => { - double _ = mass.Value * 2.20462; // Convert to pounds - })} - }; - - foreach (var conversion in conversions) - { - double performance = MeasurePerformance(conversion.action, MediumIterationCount); - Console.WriteLine($"{conversion.name}: {performance:F0} ops/sec"); - - Assert.IsGreaterThan(3000000, -performance, $"Conversion performance regression: {conversion.name} = {performance:F0} ops/sec < 3M ops/sec"); - } - } - - /// - /// Performance baseline for arithmetic operations between quantities. - /// Target: > 1M operations/second for basic arithmetic (CI-friendly). - /// - [TestMethod] - public void PerformanceBaseline_ArithmeticOperations() - { - // Pre-create objects outside the measurement to avoid allocation overhead - Force f1 = Force.FromNewtons(100.0); - Force f2 = Force.FromNewtons(50.0); - Energy e1 = Energy.FromJoules(1000.0); - Energy e2 = Energy.FromJoules(300.0); - Temperature t1 = Temperature.FromKelvin(350.0); - Temperature t2 = Temperature.FromKelvin(300.0); - Mass mass = Mass.FromKilograms(10.0); - Length distance = Length.FromMeters(100.0); - - var arithmeticTests = new[] - { - new { name = "Force Addition", action = new Action(() => { - Force _ = f1 + f2; - })}, - new { name = "Energy Subtraction", action = new Action(() => { - Energy _ = e1 - e2; - })}, - new { name = "Temperature Difference", action = new Action(() => { - Temperature _ = t1 - t2; - })}, - new { name = "Scalar Multiplication", action = new Action(() => { - Mass _ = mass * 2.5; - })}, - new { name = "Scalar Division", action = new Action(() => { - Length _ = distance / 3.0; - })} - }; - - foreach (var test in arithmeticTests) - { - double performance = MeasurePerformance(test.action, LargeIterationCount); - Console.WriteLine($"{test.name}: {performance:F0} ops/sec"); - - Assert.IsGreaterThan(1000000, -performance, $"Arithmetic operation performance regression: {test.name} = {performance:F0} ops/sec < 1M ops/sec"); - } - } - - /// - /// Performance baseline for physics relationship calculations. - /// Target: > 800K operations/second for complex physics calculations (CI-friendly). - /// - [TestMethod] - public void PerformanceBaseline_PhysicsRelationships() - { - // Pre-create objects outside the measurement to avoid allocation overhead - Mass mass1 = Mass.FromKilograms(10.0); - Acceleration acceleration = Acceleration.FromMetersPerSecondSquared(9.8); - ElectricCurrent current1 = ElectricCurrent.FromAmperes(5.0); - ElectricResistance resistance = ElectricResistance.FromOhms(10.0); - Mass mass2 = Mass.FromKilograms(2.0); - Velocity velocity = Velocity.FromMetersPerSecond(15.0); - ElectricPotential voltage = ElectricPotential.FromVolts(12.0); - ElectricCurrent current2 = ElectricCurrent.FromAmperes(3.0); - Mass mass3 = Mass.FromKilograms(5.0); - Volume volume = Volume.FromCubicMeters(0.002); - - var physicsTests = new[] - { - new { name = "Newton's 2nd Law", action = new Action(() => { - Force _ = mass1 * acceleration; // F = ma - })}, - new { name = "Ohm's Law", action = new Action(() => { - ElectricPotential _ = current1 * resistance; // V = IR - })}, - new { name = "Momentum Calculation", action = new Action(() => { - Momentum _ = mass2 * velocity; // p = mv - })}, - new { name = "Power Calculation", action = new Action(() => { - Power _ = voltage * current2; // P = VI - })}, - new { name = "Density Calculation", action = new Action(() => { - Density _ = mass3 / volume; // ρ = m/V - })} - }; - - foreach (var test in physicsTests) - { - double performance = MeasurePerformance(test.action, MediumIterationCount); - Console.WriteLine($"{test.name}: {performance:F0} ops/sec"); - - Assert.IsGreaterThan(800000, -performance, $"Physics relationship performance regression: {test.name} = {performance:F0} ops/sec < 800K ops/sec"); - } - } - - /// - /// Performance baseline for physical constant access. - /// Target: > 15M operations/second for constant access (CI-friendly). - /// - [TestMethod] - public void PerformanceBaseline_ConstantAccess() - { - var constantTests = new[] - { - new { name = "Speed of Light", action = new Action(() => { - double _ = PhysicalConstants.Generic.SpeedOfLight(); - })}, - new { name = "Gas Constant", action = new Action(() => { - double _ = PhysicalConstants.Generic.GasConstant(); - })}, - new { name = "Standard Gravity", action = new Action(() => { - double _ = PhysicalConstants.Generic.StandardGravity(); - })}, - new { name = "Avogadro Number", action = new Action(() => { - double _ = PhysicalConstants.Generic.AvogadroNumber(); - })} - }; - - foreach (var test in constantTests) - { - double performance = MeasurePerformance(test.action, LargeIterationCount); - Console.WriteLine($"{test.name}: {performance:F0} ops/sec"); - - Assert.IsGreaterThan(15000000, -performance, $"Constant access performance regression: {test.name} = {performance:F0} ops/sec < 15M ops/sec"); - } - } - - /// - /// Performance baseline for cross-domain calculations. - /// Target: > 500K operations/second for multi-domain scenarios (CI-friendly). - /// - [TestMethod] - public void PerformanceBaseline_CrossDomainCalculations() - { - // Pre-create objects outside the measurement to avoid allocation overhead - Temperature temperature1 = Temperature.FromKelvin(300.0); - Pressure pressure = Pressure.FromPascals(PhysicalConstants.Generic.StandardAtmosphericPressure()); - double standardTemp = PhysicalConstants.Generic.StandardTemperature(); - double standardPress = PhysicalConstants.Generic.StandardAtmosphericPressure(); - - ElectricCurrent current = ElectricCurrent.FromAmperes(10.0); - ElectricResistance resistance = ElectricResistance.FromOhms(5.0); - Time time = Time.FromSeconds(1.0); - - // Pre-calculate electrical-thermal intermediate values to avoid allocation overhead - ElectricPotential voltage = current * resistance; - Power power = voltage * current; - - AmountOfSubstance amount = AmountOfSubstance.FromMoles(1.0); - Temperature temperature2 = Temperature.FromKelvin(298.15); - double gasConstant = PhysicalConstants.Generic.GasConstant(); - - var crossDomainTests = new[] - { - new { name = "Thermal-Mechanical", action = new Action(() => { - double tempRatio = temperature1.Value / standardTemp; - double pressRatio = pressure.Value / standardPress; - double _ = tempRatio * pressRatio; - })}, - new { name = "Electrical-Thermal", action = new Action(() => { - Energy _ = Energy.Create(power.Value * time.Value); - })}, - new { name = "Chemical-Thermal", action = new Action(() => { - double _ = gasConstant * temperature2.Value * amount.Value; - })} - }; - - foreach (var test in crossDomainTests) - { - double performance = MeasurePerformance(test.action, MediumIterationCount); - Console.WriteLine($"{test.name}: {performance:F0} ops/sec"); - - Assert.IsGreaterThan(500000, -performance, $"Cross-domain performance regression: {test.name} = {performance:F0} ops/sec < 500K ops/sec"); - } - } - - private static double MeasurePerformance(Action action, int iterations) - { - // Warmup - for (int i = 0; i < WarmupIterations; i++) - { - action(); - } - - // Measure - Stopwatch stopwatch = Stopwatch.StartNew(); - for (int i = 0; i < iterations; i++) - { - action(); - } - stopwatch.Stop(); - - return iterations / stopwatch.Elapsed.TotalSeconds; - } -} diff --git a/Semantics.Test/PhysicalConstantsTests.cs b/Semantics.Test/PhysicalConstantsTests.cs deleted file mode 100644 index 89bb862..0000000 --- a/Semantics.Test/PhysicalConstantsTests.cs +++ /dev/null @@ -1,1334 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public class PhysicalConstantsTests -{ - private const double Tolerance = 1e-10; - private const double RelaxedTolerance = 1e-8; // For constants with experimental uncertainty - private const double LooseTolerance = 1e-6; // For complex derived relationships - - [TestMethod] - public void MolarVolumeSTP_MatchesCalculatedValue() - { - double gasConstant = PhysicalConstants.Fundamental.GasConstant; - double temperature = PhysicalConstants.Temperature.StandardTemperature; - double pressure = PhysicalConstants.Mechanical.StandardAtmosphericPressure; - - double calculatedMolarVolume = gasConstant * temperature / pressure; - double calculatedMolarVolumeInLiters = calculatedMolarVolume * 1000; - double storedMolarVolume = PhysicalConstants.Chemical.MolarVolumeSTP; - - Assert.AreEqual(calculatedMolarVolumeInLiters, storedMolarVolume, RelaxedTolerance); - } - - [TestMethod] - public void GasConstant_MatchesCalculatedFromAvogadroAndBoltzmann() - { - double avogadroNumber = PhysicalConstants.Fundamental.AvogadroNumber; - double boltzmannConstant = PhysicalConstants.Fundamental.BoltzmannConstant; - - double calculatedGasConstant = avogadroNumber * boltzmannConstant; - double storedGasConstant = PhysicalConstants.Fundamental.GasConstant; - - Assert.AreEqual(calculatedGasConstant, storedGasConstant, Tolerance); - } - - [TestMethod] - public void Ln2_MatchesCalculatedValue() - { - double calculatedLn2 = Math.Log(2.0); - double storedLn2 = PhysicalConstants.Chemical.Ln2; - - Assert.AreEqual(calculatedLn2, storedLn2, Tolerance); - } - - [TestMethod] - public void Ln10_MatchesCalculatedValue() - { - double calculatedLn10 = Math.Log(10.0); - double storedLn10 = PhysicalConstants.Mathematical.Ln10; - - Assert.AreEqual(calculatedLn10, storedLn10, Tolerance, - "Natural logarithm of 10 should match calculated value"); - } - - [TestMethod] - public void Log10E_MatchesCalculatedValue() - { - double calculatedLog10E = Math.Log10(Math.E); - double storedLog10E = PhysicalConstants.Mathematical.Log10E; - - Assert.AreEqual(calculatedLog10E, storedLog10E, Tolerance, - "Base-10 logarithm of e should match calculated value"); - } - - [TestMethod] - public void Ln10_And_Log10E_AreReciprocals() - { - double ln10 = PhysicalConstants.Mathematical.Ln10; - double log10E = PhysicalConstants.Mathematical.Log10E; - - double product = ln10 * log10E; - Assert.AreEqual(1.0, product, Tolerance, - "Ln(10) * Log10(e) should equal 1"); - } - - [TestMethod] - public void FractionConstants_MatchCalculatedValues() - { - // Test mathematical fraction constants - Assert.AreEqual(0.5, PhysicalConstants.Mathematical.OneHalf, Tolerance, - "One half should equal 0.5"); - - Assert.AreEqual(2.0 / 3.0, PhysicalConstants.Mathematical.TwoThirds, Tolerance, - "Two thirds should equal 2/3"); - - Assert.AreEqual(1.5, PhysicalConstants.Mathematical.ThreeHalves, Tolerance, - "Three halves should equal 1.5"); - - Assert.AreEqual(4.0 / 3.0, PhysicalConstants.Mathematical.FourThirds, Tolerance, - "Four thirds should equal 4/3"); - } - - [TestMethod] - public void TemperatureConversionFactors_AreConsistent() - { - // Test Celsius to Fahrenheit conversion factors - double celsiusToFahrenheitSlope = PhysicalConstants.Conversion.CelsiusToFahrenheitSlope; - double fahrenheitToCelsiusSlope = PhysicalConstants.Conversion.FahrenheitToCelsiusSlope; - - Assert.AreEqual(9.0 / 5.0, celsiusToFahrenheitSlope, Tolerance, - "Celsius to Fahrenheit slope should be 9/5"); - - Assert.AreEqual(5.0 / 9.0, fahrenheitToCelsiusSlope, Tolerance, - "Fahrenheit to Celsius slope should be 5/9"); - - // Test that they are reciprocals - double product = celsiusToFahrenheitSlope * fahrenheitToCelsiusSlope; - Assert.AreEqual(1.0, product, Tolerance, - "Temperature conversion slopes should be reciprocals"); - } - - [TestMethod] - public void EnergyConversions_AreConsistent() - { - // Test kilowatt-hour to joule conversion - double kwHToJ = PhysicalConstants.Conversion.KilowattHourToJoule; - double expectedKwHToJ = 1000.0 * 3600.0; // 1 kW = 1000 W, 1 hour = 3600 s - Assert.AreEqual(expectedKwHToJ, kwHToJ, Tolerance, - "Kilowatt-hour to joule conversion should be 1000 * 3600"); - - // Test that BTU conversion is reasonable (around 1055 J/BTU) - double btuToJ = PhysicalConstants.Conversion.BtuToJoule; - Assert.IsTrue(btuToJ is > 1000 and < 1100, - "BTU to joule conversion should be approximately 1055 J/BTU"); - } - - [TestMethod] - public void PoundForceConversion_MatchesCalculatedValue() - { - // Pound-force = pound-mass * standard gravity - double poundMassToKg = PhysicalConstants.Mechanical.PoundMassToKilogram; - double standardGravity = PhysicalConstants.Mechanical.StandardGravity; - - double calculatedPoundForce = poundMassToKg * standardGravity; - - // This should match the conversion factor used in Units.cs for pound-force - // We can't directly access the unit conversion, but we can verify the calculation - Assert.IsTrue(calculatedPoundForce is > 4.4 and < 4.5, - "Pound-force should be approximately 4.448 N"); - } - - [TestMethod] - public void NuclearMagneton_RelationshipToFundamentalConstants() - { - // Nuclear magneton = (e * h) / (4 * π * proton mass) - // We can't calculate exactly without proton mass, but can verify order of magnitude - double nuclearMagneton = PhysicalConstants.Nuclear.NuclearMagneton; - - // Nuclear magneton should be much smaller than Bohr magneton - // Order of magnitude check: should be around 5e-27 J/T - Assert.IsTrue(nuclearMagneton is > 1e-28 and < 1e-26, - "Nuclear magneton should be on the order of 5e-27 J/T"); - } - - [TestMethod] - public void AtomicMassUnit_OrderOfMagnitudeCheck() - { - // Atomic mass unit should be approximately 1/12 of carbon-12 atom mass - double atomicMassUnit = PhysicalConstants.Nuclear.AtomicMassUnit; - double avogadroNumber = PhysicalConstants.Fundamental.AvogadroNumber; - - // 1 amu * Avogadro's number should be approximately 1 g/mol - double calculatedMolarMass = atomicMassUnit * avogadroNumber * 1000; // Convert to g/mol - - Assert.IsTrue(calculatedMolarMass is > 0.9 and < 1.1, - "1 amu * Avogadro's number should be approximately 1 g/mol"); - } - - [TestMethod] - public void SoundReferenceValues_AreConsistent() - { - // Test that sound reference values are related - double refPressure = PhysicalConstants.Acoustic.ReferenceSoundPressure; - double refIntensity = PhysicalConstants.Acoustic.ReferenceSoundIntensity; - double refPower = PhysicalConstants.Acoustic.ReferenceSoundPower; - - // All should be threshold of hearing values - Assert.AreEqual(20e-6, refPressure, Tolerance, - "Reference sound pressure should be 20 μPa"); - - Assert.AreEqual(1e-12, refIntensity, Tolerance, - "Reference sound intensity should be 1 pW/m²"); - - Assert.AreEqual(1e-12, refPower, Tolerance, - "Reference sound power should be 1 pW"); - } - - [TestMethod] - public void WaterConstants_ArePhysicallyReasonable() - { - // Water boiling point should be 100°C above absolute zero - double waterBoiling = PhysicalConstants.Temperature.WaterBoilingPoint; - double absoluteZero = PhysicalConstants.Temperature.AbsoluteZeroInCelsius; - double boilingCelsius = waterBoiling - absoluteZero; - - Assert.AreEqual(100.0, boilingCelsius, Tolerance, - "Water should boil at 100°C above absolute zero"); - - // Water triple point should be slightly above absolute zero - double triplePoint = PhysicalConstants.Temperature.WaterTriplePoint; - double triplePointCelsius = triplePoint - absoluteZero; - - Assert.AreEqual(0.01, triplePointCelsius, Tolerance, - "Water triple point should be 0.01°C"); - } - - [TestMethod] - public void GenericHelpers_ReturnCorrectValues() - { - Assert.AreEqual(PhysicalConstants.Fundamental.AvogadroNumber, - PhysicalConstants.Generic.AvogadroNumber(), Tolerance); - - Assert.AreEqual(PhysicalConstants.Fundamental.GasConstant, - PhysicalConstants.Generic.GasConstant(), Tolerance); - - Assert.AreEqual(PhysicalConstants.Fundamental.SpeedOfLight, - PhysicalConstants.Generic.SpeedOfLight(), Tolerance); - - // Test additional Generic helper methods - Assert.AreEqual(PhysicalConstants.Nuclear.AtomicMassUnit, - PhysicalConstants.Generic.AtomicMassUnit(), Tolerance); - - Assert.AreEqual(PhysicalConstants.Nuclear.NuclearMagneton, - PhysicalConstants.Generic.NuclearMagneton(), Tolerance); - } - - [TestMethod] - public void GenericHelpers_FloatType_ReturnCorrectValues() - { - // Test with float type - Assert.AreEqual((float)PhysicalConstants.Fundamental.AvogadroNumber, - PhysicalConstants.Generic.AvogadroNumber(), (float)RelaxedTolerance); - - Assert.AreEqual((float)PhysicalConstants.Fundamental.GasConstant, - PhysicalConstants.Generic.GasConstant(), (float)RelaxedTolerance); - } - - [TestMethod] - public void GenericHelpers_DecimalType_ReturnCorrectValues() - { - // Test with decimal type (where precision allows) - Assert.AreEqual((decimal)PhysicalConstants.Temperature.AbsoluteZeroInCelsius, - PhysicalConstants.Generic.AbsoluteZeroInCelsius()); - - Assert.AreEqual((decimal)PhysicalConstants.Mechanical.StandardGravity, - PhysicalConstants.Generic.StandardGravity()); - } - - [TestMethod] - public void ElectromagneticConstants_DerivedFromFundamentals() - { - // Impedance of free space: Z₀ = μ₀c = √(μ₀/ε₀) - // Where μ₀ = 4π × 10⁻⁷ H/m (exact in old SI) - // And ε₀ = 1/(μ₀c²) (derived) - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double mu0 = 4 * Math.PI * 1e-7; // Permeability of free space - double epsilon0 = 1.0 / (mu0 * c * c); // Permittivity of free space - double impedanceOfFreeSpace = Math.Sqrt(mu0 / epsilon0); - - // Should equal approximately 377 ohms - Assert.IsTrue(impedanceOfFreeSpace is > 376 and < 378, - "Impedance of free space should be approximately 377 Ω"); - } - - [TestMethod] - public void ElectronVolt_DerivedFromElementaryCharge() - { - // 1 eV = e × 1 V, where e is elementary charge - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double electronVoltInJoules = elementaryCharge; // 1 eV in joules - - // Should be approximately 1.602 × 10⁻¹⁹ J - Assert.IsTrue(electronVoltInJoules is > 1.6e-19 and < 1.61e-19, - "1 eV should be approximately 1.602 × 10⁻¹⁹ J"); - } - - [TestMethod] - public void BohrRadius_DerivedFromFundamentalConstants() - { - // Bohr radius: a₀ = 4πε₀ℏ²/(mₑe²) = ℏ²/(mₑce²α) - // Where α is fine structure constant ≈ 1/137 - // Bohr radius should be approximately 5.29 × 10⁻¹¹ m - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double fineStructureConstant = 1.0 / 137.036; // Approximate value - - // Estimate Bohr radius using simplified relationship - // a₀ ≈ ℏc/(mₑc²α) where mₑc² ≈ 0.511 MeV - double electronRestMassEnergy = 0.511e6 * elementaryCharge; // Convert MeV to joules - double hbar = PhysicalConstants.Fundamental.PlanckConstant / (2 * Math.PI); - double estimatedBohrRadius = hbar * c / (electronRestMassEnergy * fineStructureConstant); - - // Should be approximately 5.3 × 10⁻¹¹ m - Assert.IsTrue(estimatedBohrRadius is > 5e-11 and < 6e-11, - "Bohr radius should be approximately 5.3 × 10⁻¹¹ m"); - } - - [TestMethod] - public void ClassicalElectronRadius_OrderOfMagnitude() - { - // Classical electron radius: rₑ = e²/(4πε₀mₑc²) - // Should be approximately 2.82 × 10⁻¹⁵ m - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double mu0 = 4 * Math.PI * 1e-7; - double epsilon0 = 1.0 / (mu0 * c * c); - double electronRestMassEnergy = 0.511e6 * elementaryCharge; // 0.511 MeV in joules - - double classicalElectronRadius = elementaryCharge * elementaryCharge / - (4 * Math.PI * epsilon0 * electronRestMassEnergy); - - // Should be approximately 2.8 × 10⁻¹⁵ m - Assert.IsTrue(classicalElectronRadius is > 2e-15 and < 3e-15, - "Classical electron radius should be approximately 2.8 × 10⁻¹⁵ m"); - } - - [TestMethod] - public void StefanBoltzmannConstant_DerivedFromFundamentals() - { - // Stefan-Boltzmann constant: σ = (2π⁵k⁴)/(15h³c²) - double k = PhysicalConstants.Fundamental.BoltzmannConstant; - double h = PhysicalConstants.Fundamental.PlanckConstant; - double c = PhysicalConstants.Fundamental.SpeedOfLight; - - double stefanBoltzmannConstant = 2 * Math.Pow(Math.PI, 5) * Math.Pow(k, 4) / - (15 * Math.Pow(h, 3) * c * c); - - // Should be approximately 5.67 × 10⁻⁸ W/(m²·K⁴) - Assert.IsTrue(stefanBoltzmannConstant is > 5e-8 and < 6e-8, - "Stefan-Boltzmann constant should be approximately 5.67 × 10⁻⁸ W/(m²·K⁴)"); - } - - [TestMethod] - public void WienDisplacementConstant_DerivedFromFundamentals() - { - // Wien displacement constant: b = hc/(xk) where x ≈ 4.965 - double h = PhysicalConstants.Fundamental.PlanckConstant; - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double k = PhysicalConstants.Fundamental.BoltzmannConstant; - double x = 4.965; // Solution to transcendental equation - - double wienConstant = h * c / (x * k); - - // Should be approximately 2.90 × 10⁻³ m·K - Assert.IsTrue(wienConstant is > 2.8e-3 and < 3.0e-3, - "Wien displacement constant should be approximately 2.90 × 10⁻³ m·K"); - } - - [TestMethod] - public void FirstRadiationConstant_DerivedFromFundamentals() - { - // First radiation constant: c₁ = 2πhc² - double h = PhysicalConstants.Fundamental.PlanckConstant; - double c = PhysicalConstants.Fundamental.SpeedOfLight; - - double firstRadiationConstant = 2 * Math.PI * h * c * c; - - // Should be approximately 3.74 × 10⁻¹⁶ W·m² - Assert.IsTrue(firstRadiationConstant is > 3.7e-16 and < 3.8e-16, - "First radiation constant should be approximately 3.74 × 10⁻¹⁶ W·m²"); - } - - [TestMethod] - public void SecondRadiationConstant_DerivedFromFundamentals() - { - // Second radiation constant: c₂ = hc/k - double h = PhysicalConstants.Fundamental.PlanckConstant; - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double k = PhysicalConstants.Fundamental.BoltzmannConstant; - - double secondRadiationConstant = h * c / k; - - // Should be approximately 1.44 × 10⁻² m·K - Assert.IsTrue(secondRadiationConstant is > 1.4e-2 and < 1.5e-2, - "Second radiation constant should be approximately 1.44 × 10⁻² m·K"); - } - - [TestMethod] - public void RydbergEnergy_DerivedFromFundamentals() - { - // Rydberg energy: Ry = mₑe⁴/(8ε₀²h²) = α²mₑc²/2 - // Should be approximately 13.6 eV - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double fineStructureConstant = 1.0 / 137.036; // Approximate - double electronRestMassEnergy = 0.511e6 * elementaryCharge; // 0.511 MeV in joules - - double rydbergEnergy = fineStructureConstant * fineStructureConstant * electronRestMassEnergy / 2; - double rydbergEnergyInEV = rydbergEnergy / elementaryCharge; - - // Should be approximately 13.6 eV - Assert.IsTrue(rydbergEnergyInEV is > 13.0 and < 14.0, - "Rydberg energy should be approximately 13.6 eV"); - } - - [TestMethod] - public void ComptonWavelength_DerivedFromFundamentals() - { - // Compton wavelength: λc = h/(mₑc) - double h = PhysicalConstants.Fundamental.PlanckConstant; - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double electronRestMassEnergy = 0.511e6 * PhysicalConstants.Fundamental.ElementaryCharge; - double electronMass = electronRestMassEnergy / (c * c); - - double comptonWavelength = h / (electronMass * c); - - // Should be approximately 2.43 × 10⁻¹² m - Assert.IsTrue(comptonWavelength is > 2.4e-12 and < 2.5e-12, - "Compton wavelength should be approximately 2.43 × 10⁻¹² m"); - } - - [TestMethod] - public void PlanckUnits_DerivedFromFundamentals() - { - // Planck length: lₚ = √(ℏG/c³) - double hbar = PhysicalConstants.Fundamental.PlanckConstant / (2 * Math.PI); - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double G = 6.674e-11; // Approximate gravitational constant - - double planckLength = Math.Sqrt(hbar * G / (c * c * c)); - - // Should be approximately 1.6 × 10⁻³⁵ m - Assert.IsTrue(planckLength is > 1e-35 and < 2e-35, - "Planck length should be approximately 1.6 × 10⁻³⁵ m"); - - // Planck time: tₚ = √(ℏG/c⁵) - double planckTime = Math.Sqrt(hbar * G / Math.Pow(c, 5)); - - // Should be approximately 5.4 × 10⁻⁴⁴ s - Assert.IsTrue(planckTime is > 5e-44 and < 6e-44, - "Planck time should be approximately 5.4 × 10⁻⁴⁴ s"); - } - - [TestMethod] - public void AvogadroConstant_RelationshipToBoltzmann() - { - // Gas constant R = NA × k - double avogadroNumber = PhysicalConstants.Fundamental.AvogadroNumber; - double boltzmannConstant = PhysicalConstants.Fundamental.BoltzmannConstant; - double gasConstant = PhysicalConstants.Fundamental.GasConstant; - - double calculatedGasConstant = avogadroNumber * boltzmannConstant; - - Assert.AreEqual(gasConstant, calculatedGasConstant, Tolerance); - } - - [TestMethod] - public void ElectronChargeToMassRatio_OrderOfMagnitude() - { - // e/m ratio for electron should be approximately 1.76 × 10¹¹ C/kg - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double electronRestMassEnergy = 0.511e6 * elementaryCharge; // 0.511 MeV - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double electronMass = electronRestMassEnergy / (c * c); - - double chargeToMassRatio = elementaryCharge / electronMass; - - // Should be approximately 1.76 × 10¹¹ C/kg - Assert.IsTrue(chargeToMassRatio is > 1.7e11 and < 1.8e11, - "Electron charge-to-mass ratio should be approximately 1.76 × 10¹¹ C/kg"); - } - - [TestMethod] - public void FineStructureConstant_RelationshipToOtherConstants() - { - // α = e²/(4πε₀ℏc) ≈ 1/137 - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double hbar = PhysicalConstants.Fundamental.PlanckConstant / (2 * Math.PI); - double mu0 = 4 * Math.PI * 1e-7; - double epsilon0 = 1.0 / (mu0 * c * c); - - double calculatedAlpha = elementaryCharge * elementaryCharge / - (4 * Math.PI * epsilon0 * hbar * c); - - // Should be approximately 1/137 ≈ 0.00729 - Assert.IsTrue(calculatedAlpha is > 0.007 and < 0.008, - "Fine structure constant should be approximately 1/137"); - } - - [TestMethod] - public void ClassicalElectronRadius_ToComptonWavelengthRatio() - { - // rₑ/λc = α/(2π) where α is fine structure constant - double fineStructureConstant = 1.0 / 137.036; - double expectedRatio = fineStructureConstant / (2 * Math.PI); - - // This ratio should be approximately 0.001 - Assert.IsTrue(expectedRatio is > 0.0005 and < 0.002, - "Classical electron radius to Compton wavelength ratio should be α/(2π)"); - } - - [TestMethod] - public void StandardAirDensity_RelatesToIdealGasLaw() - { - // At standard conditions (15°C, 1 atm), verify air density makes sense - double standardAirDensity = PhysicalConstants.FluidDynamics.StandardAirDensity; - double standardPressure = PhysicalConstants.Mechanical.StandardAtmosphericPressure; - double gasConstant = PhysicalConstants.Fundamental.GasConstant; - - // For air: M ≈ 28.97 g/mol, T = 15°C = 288.15 K - // ρ = PM/(RT) - double airMolarMass = 0.02897; // kg/mol - double temperatureAt15C = 273.15 + 15; // K - - double calculatedDensity = standardPressure * airMolarMass / (gasConstant * temperatureAt15C); - - // Should match within reasonable tolerance (air composition varies slightly) - Assert.AreEqual(standardAirDensity, calculatedDensity, 1e-3); - } - - [TestMethod] - public void ThermodynamicRelationships_HeatCapacityRatios() - { - // For ideal diatomic gas: Cp/Cv = γ = 7/5 = 1.4 - double gasConstant = PhysicalConstants.Fundamental.GasConstant; - - // Cv = (5/2)R for diatomic gas - // Cp = Cv + R = (7/2)R - double cvDiatomic = 2.5 * gasConstant; - double cpDiatomic = 3.5 * gasConstant; - double gamma = cpDiatomic / cvDiatomic; - - Assert.AreEqual(1.4, gamma, Tolerance); - } - - [TestMethod] - public void EnergyEquivalents_MassEnergyRelation() - { - // E = mc² relationship verification - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double electronRestMassEnergy = 0.511e6; // eV - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - - // Calculate electron mass from energy - double electronMass = electronRestMassEnergy * elementaryCharge / (c * c); - - // Verify the reverse calculation - double calculatedEnergy = electronMass * c * c / elementaryCharge; - - Assert.AreEqual(electronRestMassEnergy, calculatedEnergy, RelaxedTolerance); - } - - [TestMethod] - public void QuantumMechanical_UncertaintyPrinciple() - { - // Heisenberg uncertainty principle: ΔxΔp ≥ ℏ/2 - double hbar = PhysicalConstants.Fundamental.PlanckConstant / (2 * Math.PI); - double minimumUncertaintyProduct = hbar / 2; - - // For a particle confined to atomic scale (~10⁻¹⁰ m) - double positionUncertainty = 1e-10; // m - double minimumMomentumUncertainty = minimumUncertaintyProduct / positionUncertainty; - - // Should give reasonable momentum uncertainty for atomic scale - Assert.IsTrue(minimumMomentumUncertainty is > 1e-25 and < 1e-23, - "Momentum uncertainty should be reasonable for atomic confinement"); - } - - [TestMethod] - public void NuclearBinding_MassDefectRelationships() - { - // For nuclear physics, binding energies per nucleon are typically 1-8 MeV - double atomicMassUnit = PhysicalConstants.Nuclear.AtomicMassUnit; - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - - // 1 amu ≈ 931.5 MeV/c² - double amuInMeV = atomicMassUnit * c * c / (1e6 * elementaryCharge); - - Assert.IsTrue(amuInMeV is > 930 and < 935, - "Atomic mass unit should be approximately 931.5 MeV/c²"); - } - - [TestMethod] - public void ElectromagneticSpectrum_PhotonEnergyFrequency() - { - // E = hf for photons - double h = PhysicalConstants.Fundamental.PlanckConstant; - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - - // Visible light: λ ≈ 500 nm - double wavelength = 500e-9; // m - double frequency = c / wavelength; - double photonEnergy = h * frequency; - double photonEnergyInEV = photonEnergy / elementaryCharge; - - // Should be approximately 2.5 eV - Assert.IsTrue(photonEnergyInEV is > 2.0 and < 3.0, - "500 nm photon should have energy around 2.5 eV"); - } - - [TestMethod] - public void ThermodynamicTemperatureScale_AbsoluteZero() - { - // Absolute zero relationships - double absoluteZero = PhysicalConstants.Temperature.AbsoluteZeroInCelsius; - double waterTriplePoint = PhysicalConstants.Temperature.WaterTriplePoint; - double standardTemperature = PhysicalConstants.Temperature.StandardTemperature; - - // Verify temperature scale consistency - Assert.AreEqual(273.15, absoluteZero, Tolerance, "Absolute zero should be exactly 273.15 K"); - Assert.AreEqual(standardTemperature, absoluteZero, Tolerance, "Standard temperature equals absolute zero"); - Assert.IsGreaterThan(absoluteZero, waterTriplePoint, "Water triple point should be above absolute zero"); - } - - [TestMethod] - public void AcousticConstants_SoundSpeedRelationships() - { - // Sound speed in air: v = √(γRT/M) where γ = 1.4 for air - double gasConstant = PhysicalConstants.Fundamental.GasConstant; - double temperatureAt20C = 273.15 + 20; // K - double airMolarMass = 0.02897; // kg/mol - double gamma = 1.4; // Heat capacity ratio for air - - double soundSpeed = Math.Sqrt(gamma * gasConstant * temperatureAt20C / airMolarMass); - - // Should be approximately 343 m/s at 20°C - Assert.IsTrue(soundSpeed is > 340 and < 350, - "Sound speed in air should be approximately 343 m/s at 20°C"); - } - - [TestMethod] - public void FluidDynamics_ReynoldsNumberDimensionless() - { - // Reynolds number is dimensionless: Re = ρvL/μ - // This tests that the combination of density, velocity, length, and viscosity is dimensionless - double airDensity = PhysicalConstants.FluidDynamics.StandardAirDensity; - - // For typical air flow: v = 10 m/s, L = 1 m, μ ≈ 1.8e-5 Pa·s - double velocity = 10; // m/s - double length = 1; // m - double viscosity = 1.8e-5; // Pa·s (typical for air) - - double reynoldsNumber = airDensity * velocity * length / viscosity; - - // Should be dimensionless and reasonable for air flow - Assert.IsTrue(reynoldsNumber is > 500000 and < 1000000, - "Reynolds number should be reasonable for typical air flow"); - } - - // ============================================================================ - // COMPREHENSIVE ELECTROMAGNETIC CONSTANT DERIVATIONS - // ============================================================================ - - [TestMethod] - public void VacuumPermeability_RelationshipToSpeedOfLight() - { - // μ₀ = 4π × 10⁻⁷ H/m (exact in old SI) - // ε₀ = 1/(μ₀c²) (derived) - // c = 1/√(μ₀ε₀) (Maxwell's equations) - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double mu0 = 4 * Math.PI * 1e-7; // Exact by definition - double epsilon0 = 1.0 / (mu0 * c * c); - - // Verify Maxwell relation: c = 1/√(μ₀ε₀) - double calculatedSpeedOfLight = 1.0 / Math.Sqrt(mu0 * epsilon0); - - Assert.AreEqual(c, calculatedSpeedOfLight, RelaxedTolerance); - } - - [TestMethod] - public void CoulombConstant_DerivedFromVacuumPermittivity() - { - // k_e = 1/(4πε₀) ≈ 8.99 × 10⁹ N⋅m²/C² - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double mu0 = 4 * Math.PI * 1e-7; - double epsilon0 = 1.0 / (mu0 * c * c); - - double coulombConstant = 1.0 / (4 * Math.PI * epsilon0); - - // Should be approximately 8.99 × 10⁹ N⋅m²/C² - Assert.IsTrue(coulombConstant is > 8.9e9 and < 9.0e9, - "Coulomb constant should be approximately 8.99 × 10⁹ N⋅m²/C²"); - } - - [TestMethod] - public void JosephsonConstant_DerivedFromPlanckAndCharge() - { - // K_J = 2e/h (Josephson constant) - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double planckConstant = PhysicalConstants.Fundamental.PlanckConstant; - - double josephsonConstant = 2 * elementaryCharge / planckConstant; - - // Should be approximately 4.84 × 10¹⁴ Hz/V - Assert.IsTrue(josephsonConstant is > 4.8e14 and < 4.9e14, - "Josephson constant should be approximately 4.84 × 10¹⁴ Hz/V"); - } - - [TestMethod] - public void VonKlitzingConstant_DerivedFromPlanckAndCharge() - { - // R_K = h/e² (von Klitzing constant) - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double planckConstant = PhysicalConstants.Fundamental.PlanckConstant; - - double vonKlitzingConstant = planckConstant / (elementaryCharge * elementaryCharge); - - // Should be approximately 2.58 × 10⁴ Ω - Assert.IsTrue(vonKlitzingConstant is > 2.5e4 and < 2.6e4, - "von Klitzing constant should be approximately 2.58 × 10⁴ Ω"); - } - - [TestMethod] - public void BohrMagneton_DerivedFromFundamentals() - { - // μ_B = eℏ/(2m_e) (Bohr magneton) - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double hbar = PhysicalConstants.Fundamental.PlanckConstant / (2 * Math.PI); - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double electronRestMassEnergy = 0.511e6 * elementaryCharge; // 0.511 MeV - double electronMass = electronRestMassEnergy / (c * c); - - double bohrMagneton = elementaryCharge * hbar / (2 * electronMass); - - // Should be approximately 9.27 × 10⁻²⁴ J/T - Assert.IsTrue(bohrMagneton is > 9.2e-24 and < 9.3e-24, - "Bohr magneton should be approximately 9.27 × 10⁻²⁴ J/T"); - } - - // ============================================================================ - // THERMODYNAMIC CONSTANT DERIVATIONS - // ============================================================================ - - [TestMethod] - public void RayleighJeansConstant_DerivedFromFundamentals() - { - // c₁L = 2ck (first radiation constant for luminance) - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double k = PhysicalConstants.Fundamental.BoltzmannConstant; - - double rayleighJeansConstant = 2 * c * k; - - // Should be of the correct order of magnitude for radiation constants - Assert.IsTrue(rayleighJeansConstant is > 8e-16 and < 9e-15, - $"Rayleigh-Jeans constant calculated as {rayleighJeansConstant:E3} should be approximately 8.3 × 10⁻¹⁵ W⋅m⁻²⋅sr⁻¹⋅K⁻¹"); - } - - [TestMethod] - public void LoschmidtNumber_DerivedFromIdealGasLaw() - { - // Loschmidt number: n₀ = N_A/V_m = P/(kT) at STP - double avogadroNumber = PhysicalConstants.Fundamental.AvogadroNumber; - double molarVolumeSTP = PhysicalConstants.Chemical.MolarVolumeSTP / 1000; // Convert to m³/mol - double standardPressure = PhysicalConstants.Mechanical.StandardAtmosphericPressure; - double boltzmannConstant = PhysicalConstants.Fundamental.BoltzmannConstant; - double standardTemperature = PhysicalConstants.Temperature.StandardTemperature; - - double loschmidtFromAvogadro = avogadroNumber / molarVolumeSTP; - double loschmidtFromPressure = standardPressure / (boltzmannConstant * standardTemperature); - - // Both methods should give the same result - Assert.AreEqual(loschmidtFromAvogadro, loschmidtFromPressure, RelaxedTolerance); - - // Should be approximately 2.69 × 10²⁵ molecules/m³ - Assert.IsTrue(loschmidtFromAvogadro is > 2.6e25 and < 2.7e25, - "Loschmidt number should be approximately 2.69 × 10²⁵ molecules/m³"); - } - - // ============================================================================ - // NUCLEAR AND PARTICLE PHYSICS DERIVATIONS - // ============================================================================ - - [TestMethod] - public void NuclearMagneton_PreciseCalculationFromFundamentals() - { - // μ_N = eℏ/(2m_p) where m_p ≈ 938.3 MeV/c² - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double hbar = PhysicalConstants.Fundamental.PlanckConstant / (2 * Math.PI); - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double protonRestMassEnergy = 938.3e6 * elementaryCharge; // 938.3 MeV - double protonMass = protonRestMassEnergy / (c * c); - - double calculatedNuclearMagneton = elementaryCharge * hbar / (2 * protonMass); - double storedNuclearMagneton = PhysicalConstants.Nuclear.NuclearMagneton; - - // Should match within reasonable tolerance (considering approximate proton mass) - Assert.AreEqual(storedNuclearMagneton, calculatedNuclearMagneton, LooseTolerance); - } - - [TestMethod] - public void AtomicMassUnit_ExactRelationshipToKilogram() - { - // 1 u = 1/12 × mass of ¹²C atom = 1.66053906660 × 10⁻²⁷ kg (CODATA 2018) - double atomicMassUnit = PhysicalConstants.Nuclear.AtomicMassUnit; - double avogadroNumber = PhysicalConstants.Fundamental.AvogadroNumber; - - // 1 u × N_A should equal exactly 1 g/mol - double gramsPerMole = atomicMassUnit * avogadroNumber * 1000; // Convert to g/mol - - Assert.AreEqual(1.0, gramsPerMole, RelaxedTolerance); - } - - [TestMethod] - public void ElectronGFactor_RelationshipToFineStructure() - { - // g_e ≈ 2(1 + α/(2π) + ...) where α is fine structure constant - // Leading QED correction: α/(2π) ≈ 0.00116 - double fineStructureConstant = 1.0 / 137.036; // Approximate - double qedCorrection = fineStructureConstant / (2 * Math.PI); - double approximateGFactor = 2 * (1 + qedCorrection); - - // Should be approximately 2.002319 - Assert.IsTrue(approximateGFactor is > 2.002 and < 2.003, - "Electron g-factor should be approximately 2.002319"); - } - - // ============================================================================ - // GRAVITATIONAL AND COSMOLOGICAL DERIVATIONS - // ============================================================================ - - [TestMethod] - public void PlanckMass_DerivedFromFundamentals() - { - // m_P = √(ℏc/G) (Planck mass) - double hbar = PhysicalConstants.Fundamental.PlanckConstant / (2 * Math.PI); - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double G = 6.674e-11; // Approximate gravitational constant - - double planckMass = Math.Sqrt(hbar * c / G); - - // Should be approximately 2.18 × 10⁻⁸ kg - Assert.IsTrue(planckMass is > 2.1e-8 and < 2.2e-8, - "Planck mass should be approximately 2.18 × 10⁻⁸ kg"); - } - - [TestMethod] - public void SchwarzschildRadius_ScalingWithMass() - { - // r_s = 2GM/c² (Schwarzschild radius) - double G = 6.674e-11; // Approximate gravitational constant - double c = PhysicalConstants.Fundamental.SpeedOfLight; - - // For solar mass (≈ 2 × 10³⁰ kg) - double solarMass = 2e30; // kg - double schwarzschildRadius = 2 * G * solarMass / (c * c); - - // Should be approximately 3 km for the sun - Assert.IsTrue(schwarzschildRadius is > 2500 and < 3500, - "Schwarzschild radius of the sun should be approximately 3 km"); - } - - // ============================================================================ - // QUANTUM MECHANICS AND ATOMIC PHYSICS DERIVATIONS - // ============================================================================ - - [TestMethod] - public void ReducedPlanckConstant_RelationshipToPlanck() - { - // ℏ = h/(2π) - double planckConstant = PhysicalConstants.Fundamental.PlanckConstant; - double calculatedHbar = planckConstant / (2 * Math.PI); - - // Should be approximately 1.055 × 10⁻³⁴ J⋅s - Assert.IsTrue(calculatedHbar is > 1.05e-34 and < 1.06e-34, - "Reduced Planck constant should be approximately 1.055 × 10⁻³⁴ J⋅s"); - } - - [TestMethod] - public void HartreeEnergy_DerivedFromRydbergAndFineStructure() - { - // E_h = 2Ry = α²m_ec² (Hartree energy) - double fineStructureConstant = 1.0 / 137.036; - double electronRestMassEnergy = 0.511e6; // eV - - double hartreeEnergyInEV = fineStructureConstant * fineStructureConstant * electronRestMassEnergy; - - // Should be approximately 27.2 eV - Assert.IsTrue(hartreeEnergyInEV is > 27.0 and < 27.5, - "Hartree energy should be approximately 27.2 eV"); - } - - [TestMethod] - public void QuantumOfConductance_DerivedFromFundamentals() - { - // G₀ = 2e²/h (quantum of conductance) - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double planckConstant = PhysicalConstants.Fundamental.PlanckConstant; - - double quantumOfConductance = 2 * elementaryCharge * elementaryCharge / planckConstant; - - // Should be approximately 7.75 × 10⁻⁵ S - Assert.IsTrue(quantumOfConductance is > 7.7e-5 and < 7.8e-5, - "Quantum of conductance should be approximately 7.75 × 10⁻⁵ S"); - - // Should be reciprocal of half von Klitzing constant - double vonKlitzingConstant = planckConstant / (elementaryCharge * elementaryCharge); - double reciprocalCheck = 1.0 / (vonKlitzingConstant / 2); - - Assert.AreEqual(quantumOfConductance, reciprocalCheck, Tolerance); - } - - // ============================================================================ - // OPTICAL AND PHOTONIC CONSTANT DERIVATIONS - // ============================================================================ - - [TestMethod] - public void PhotonMomentum_RelationshipToEnergy() - { - // p = E/c = hf/c = h/λ for photons - double h = PhysicalConstants.Fundamental.PlanckConstant; - double c = PhysicalConstants.Fundamental.SpeedOfLight; - - // For 500 nm light - double wavelength = 500e-9; // m - double photonMomentum = h / wavelength; - double photonEnergy = h * c / wavelength; - double momentumFromEnergy = photonEnergy / c; - - Assert.AreEqual(photonMomentum, momentumFromEnergy, Tolerance); - } - - [TestMethod] - public void RadiationPressure_RelationshipToIntensity() - { - // Radiation pressure: P = I/c for perfect absorption - double c = PhysicalConstants.Fundamental.SpeedOfLight; - - // For solar constant (≈ 1361 W/m²) - double solarConstant = 1361; // W/m² - double radiationPressure = solarConstant / c; - - // Should be approximately 4.5 × 10⁻⁶ Pa - Assert.IsTrue(radiationPressure is > 4e-6 and < 5e-6, - "Solar radiation pressure should be approximately 4.5 × 10⁻⁶ Pa"); - } - - // ============================================================================ - // COMPREHENSIVE CHAIN VALIDATION TESTS - // ============================================================================ - - [TestMethod] - public void CompleteThermodynamicChain_STPConditions() - { - // Complete derivation chain for STP molar volume - double avogadroNumber = PhysicalConstants.Fundamental.AvogadroNumber; - double boltzmannConstant = PhysicalConstants.Fundamental.BoltzmannConstant; - double standardTemperature = PhysicalConstants.Temperature.StandardTemperature; - double standardPressure = PhysicalConstants.Mechanical.StandardAtmosphericPressure; - - // Step 1: Gas constant from fundamentals - double gasConstant = avogadroNumber * boltzmannConstant; - - // Step 2: Molar volume from ideal gas law - double molarVolume = gasConstant * standardTemperature / standardPressure; - double molarVolumeInLiters = molarVolume * 1000; - - // Step 3: Compare with stored value - double storedMolarVolume = PhysicalConstants.Chemical.MolarVolumeSTP; - - Assert.AreEqual(storedMolarVolume, molarVolumeInLiters, RelaxedTolerance); - } - - [TestMethod] - public void CompleteElectromagneticChain_FineStructureToBohr() - { - // Complete derivation chain from fine structure constant to Bohr radius - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double hbar = PhysicalConstants.Fundamental.PlanckConstant / (2 * Math.PI); - - // Step 1: Fine structure constant (approximate) - double mu0 = 4 * Math.PI * 1e-7; - double epsilon0 = 1.0 / (mu0 * c * c); - double fineStructureConstant = elementaryCharge * elementaryCharge / (4 * Math.PI * epsilon0 * hbar * c); - - // Step 2: Electron mass from rest energy - double electronRestMassEnergy = 0.511e6 * elementaryCharge; // 0.511 MeV - double electronMass = electronRestMassEnergy / (c * c); - - // Step 3: Bohr radius from fine structure constant - double bohrRadius = hbar / (electronMass * c * fineStructureConstant); - - // Should be approximately 5.29 × 10⁻¹¹ m - Assert.IsTrue(bohrRadius is > 5.2e-11 and < 5.3e-11, - "Derived Bohr radius should be approximately 5.29 × 10⁻¹¹ m"); - } - - [TestMethod] - public void CompleteNuclearChain_MassEnergyUnits() - { - // Complete derivation chain for nuclear mass-energy relationships - double atomicMassUnit = PhysicalConstants.Nuclear.AtomicMassUnit; - double c = PhysicalConstants.Fundamental.SpeedOfLight; - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double avogadroNumber = PhysicalConstants.Fundamental.AvogadroNumber; - - // Step 1: AMU energy equivalent - double amuEnergyInJoules = atomicMassUnit * c * c; - double amuEnergyInMeV = amuEnergyInJoules / (1e6 * elementaryCharge); - - // Step 2: Verify consistency with molar mass - double molarMassOfAMU = atomicMassUnit * avogadroNumber * 1000; // g/mol - - // Step 3: Cross-check with binding energy scales - Assert.IsTrue(amuEnergyInMeV is > 930 and < 935, "1 amu should be ≈ 931.5 MeV"); - Assert.AreEqual(1.0, molarMassOfAMU, RelaxedTolerance, "1 amu × N_A should equal 1 g/mol"); - } - - [TestMethod] - public void CompleteQuantumChain_UncertaintyToTunneling() - { - // Complete quantum mechanical derivation chain - double hbar = PhysicalConstants.Fundamental.PlanckConstant / (2 * Math.PI); - double elementaryCharge = PhysicalConstants.Fundamental.ElementaryCharge; - double c = PhysicalConstants.Fundamental.SpeedOfLight; - - // Step 1: Uncertainty principle for atomic confinement - double atomicSize = 1e-10; // m (typical atomic diameter) - double minimumMomentum = hbar / (2 * atomicSize); - - // Step 2: Corresponding kinetic energy (non-relativistic) - double electronMass = 0.511e6 * elementaryCharge / (c * c); // From rest mass energy - double kineticEnergy = minimumMomentum * minimumMomentum / (2 * electronMass); - double kineticEnergyInEV = kineticEnergy / elementaryCharge; - - // Step 3: Verify this gives reasonable atomic binding energies - Assert.IsTrue(kineticEnergyInEV is > 0.1 and < 1000, - "Quantum confinement energy should be in eV range for atoms"); - } - - // ============================================================================ - // DERIVED CONSTANTS VALIDATION TESTS - // ============================================================================ - - [TestMethod] - public void DerivedConstants_AreaConversions_MatchCalculatedValues() - { - // Test SquareFeetToSquareMeters = FeetToMeters² - double feetToMeters = PhysicalConstants.Conversion.FeetToMeters; - double calculatedSquareFeetToSquareMeters = feetToMeters * feetToMeters; - double storedSquareFeetToSquareMeters = PhysicalConstants.Conversion.SquareFeetToSquareMeters; - - Assert.AreEqual(calculatedSquareFeetToSquareMeters, storedSquareFeetToSquareMeters, Tolerance, - "SquareFeetToSquareMeters should equal FeetToMeters²"); - - // Test SquareInchesToSquareMeters = InchesToMeters² - double inchesToMeters = PhysicalConstants.Conversion.InchesToMeters; - double calculatedSquareInchesToSquareMeters = inchesToMeters * inchesToMeters; - double storedSquareInchesToSquareMeters = PhysicalConstants.Conversion.SquareInchesToSquareMeters; - - Assert.AreEqual(calculatedSquareInchesToSquareMeters, storedSquareInchesToSquareMeters, Tolerance, - "SquareInchesToSquareMeters should equal InchesToMeters²"); - - // Test SquareMilesToSquareMeters = MilesToMeters² - double milesToMeters = PhysicalConstants.Conversion.MilesToMeters; - double calculatedSquareMilesToSquareMeters = milesToMeters * milesToMeters; - double storedSquareMilesToSquareMeters = PhysicalConstants.Conversion.SquareMilesToSquareMeters; - - Assert.AreEqual(calculatedSquareMilesToSquareMeters, storedSquareMilesToSquareMeters, Tolerance, - "SquareMilesToSquareMeters should equal MilesToMeters²"); - } - - [TestMethod] - public void DerivedConstants_TimeRelationships_MatchCalculatedValues() - { - // Test SecondsPerHour = SecondsPerMinute * MinutesPerHour - double secondsPerMinute = PhysicalConstants.Time.SecondsPerMinute; - double minutesPerHour = PhysicalConstants.Time.MinutesPerHour; - double calculatedSecondsPerHour = secondsPerMinute * minutesPerHour; - double storedSecondsPerHour = PhysicalConstants.Time.SecondsPerHour; - - Assert.AreEqual(calculatedSecondsPerHour, storedSecondsPerHour, Tolerance, - "SecondsPerHour should equal SecondsPerMinute * MinutesPerHour"); - - // Test SecondsPerDay = SecondsPerHour * HoursPerDay - double hoursPerDay = PhysicalConstants.Time.HoursPerDay; - double calculatedSecondsPerDay = storedSecondsPerHour * hoursPerDay; - double storedSecondsPerDay = PhysicalConstants.Time.SecondsPerDay; - - Assert.AreEqual(calculatedSecondsPerDay, storedSecondsPerDay, Tolerance, - "SecondsPerDay should equal SecondsPerHour * HoursPerDay"); - - // Test SecondsPerWeek = SecondsPerDay * DaysPerWeek - double daysPerWeek = PhysicalConstants.Time.DaysPerWeek; - double calculatedSecondsPerWeek = storedSecondsPerDay * daysPerWeek; - double storedSecondsPerWeek = PhysicalConstants.Time.SecondsPerWeek; - - Assert.AreEqual(calculatedSecondsPerWeek, storedSecondsPerWeek, Tolerance, - "SecondsPerWeek should equal SecondsPerDay * DaysPerWeek"); - - // Test SecondsPerJulianYear = SecondsPerDay * DaysPerJulianYear - double daysPerJulianYear = PhysicalConstants.Time.DaysPerJulianYear; - double calculatedSecondsPerJulianYear = storedSecondsPerDay * daysPerJulianYear; - double storedSecondsPerJulianYear = PhysicalConstants.Time.SecondsPerJulianYear; - - Assert.AreEqual(calculatedSecondsPerJulianYear, storedSecondsPerJulianYear, Tolerance, - "SecondsPerJulianYear should equal SecondsPerDay * DaysPerJulianYear"); - } - - [TestMethod] - public void DerivedConstants_TemperatureConversions_MatchCalculatedValues() - { - // Test FahrenheitToCelsiusSlope = 5/9 - double calculatedFahrenheitToCelsiusSlope = 5.0 / 9.0; - double storedFahrenheitToCelsiusSlope = PhysicalConstants.Conversion.FahrenheitToCelsiusSlope; - - Assert.AreEqual(calculatedFahrenheitToCelsiusSlope, storedFahrenheitToCelsiusSlope, Tolerance, - "FahrenheitToCelsiusSlope should equal 5/9"); - - // Test that CelsiusToFahrenheitSlope and FahrenheitToCelsiusSlope are reciprocals - double celsiusToFahrenheitSlope = PhysicalConstants.Conversion.CelsiusToFahrenheitSlope; - double reciprocalProduct = celsiusToFahrenheitSlope * storedFahrenheitToCelsiusSlope; - - Assert.AreEqual(1.0, reciprocalProduct, Tolerance, - "Temperature conversion slopes should be reciprocals"); - - // Test CelsiusToFahrenheitSlope = 9/5 - double calculatedCelsiusToFahrenheitSlope = 9.0 / 5.0; - Assert.AreEqual(calculatedCelsiusToFahrenheitSlope, celsiusToFahrenheitSlope, Tolerance, - "CelsiusToFahrenheitSlope should equal 9/5"); - } - - [TestMethod] - public void DerivedConstants_MathematicalFractions_MatchCalculatedValues() - { - // Test TwoThirds = 2/3 - double calculatedTwoThirds = 2.0 / 3.0; - double storedTwoThirds = PhysicalConstants.Mathematical.TwoThirds; - - Assert.AreEqual(calculatedTwoThirds, storedTwoThirds, Tolerance, - "TwoThirds should equal 2/3"); - - // Test FourThirds = 4/3 - double calculatedFourThirds = 4.0 / 3.0; - double storedFourThirds = PhysicalConstants.Mathematical.FourThirds; - - Assert.AreEqual(calculatedFourThirds, storedFourThirds, Tolerance, - "FourThirds should equal 4/3"); - - // Test OneHalf = 1/2 - double calculatedOneHalf = 1.0 / 2.0; - double storedOneHalf = PhysicalConstants.Mathematical.OneHalf; - - Assert.AreEqual(calculatedOneHalf, storedOneHalf, Tolerance, - "OneHalf should equal 1/2"); - - // Test ThreeHalves = 3/2 - double calculatedThreeHalves = 3.0 / 2.0; - double storedThreeHalves = PhysicalConstants.Mathematical.ThreeHalves; - - Assert.AreEqual(calculatedThreeHalves, storedThreeHalves, Tolerance, - "ThreeHalves should equal 3/2"); - } - - [TestMethod] - public void DerivedConstants_EnergyConversions_MatchCalculatedValues() - { - // Test KilowattHourToJoule = 1000 W/kW * 3600 s/h - double calculatedKilowattHourToJoule = 1000.0 * 3600.0; - double storedKilowattHourToJoule = PhysicalConstants.Conversion.KilowattHourToJoule; - - Assert.AreEqual(calculatedKilowattHourToJoule, storedKilowattHourToJoule, Tolerance, - "KilowattHourToJoule should equal 1000 * 3600 (kW to W * seconds per hour)"); - - // Verify the time component comes from SecondsPerHour - double secondsPerHour = PhysicalConstants.Time.SecondsPerHour; - double alternativeCalculation = 1000.0 * secondsPerHour; - - Assert.AreEqual(alternativeCalculation, storedKilowattHourToJoule, Tolerance, - "KilowattHourToJoule should use SecondsPerHour constant"); - } - - [TestMethod] - public void DerivedConstants_LogarithmicRelationships_MatchCalculatedValues() - { - // Test Ln10 = ln(10) - double calculatedLn10 = Math.Log(10.0); - double storedLn10 = PhysicalConstants.Mathematical.Ln10; - - Assert.AreEqual(calculatedLn10, storedLn10, Tolerance, - "Ln10 should equal ln(10)"); - - // Test Log10E = log₁₀(e) - double calculatedLog10E = Math.Log10(Math.E); - double storedLog10E = PhysicalConstants.Mathematical.Log10E; - - Assert.AreEqual(calculatedLog10E, storedLog10E, Tolerance, - "Log10E should equal log₁₀(e)"); - - // Test Ln2 = ln(2) - double calculatedLn2 = Math.Log(2.0); - double storedLn2 = PhysicalConstants.Chemical.Ln2; - - Assert.AreEqual(calculatedLn2, storedLn2, Tolerance, - "Ln2 should equal ln(2)"); - - // Test reciprocal relationship: Ln10 * Log10E = 1 - double reciprocalProduct = storedLn10 * storedLog10E; - Assert.AreEqual(1.0, reciprocalProduct, Tolerance, - "Ln10 * Log10E should equal 1"); - } - - [TestMethod] - public void DerivedConstants_FundamentalRelationships_MatchCalculatedValues() - { - // Test GasConstant = AvogadroNumber * BoltzmannConstant - double avogadroNumber = PhysicalConstants.Fundamental.AvogadroNumber; - double boltzmannConstant = PhysicalConstants.Fundamental.BoltzmannConstant; - double calculatedGasConstant = avogadroNumber * boltzmannConstant; - double storedGasConstant = PhysicalConstants.Fundamental.GasConstant; - - Assert.AreEqual(calculatedGasConstant, storedGasConstant, Tolerance, - "GasConstant should equal AvogadroNumber * BoltzmannConstant"); - - // Test MolarVolumeSTP = GasConstant * StandardTemperature / StandardAtmosphericPressure - double standardTemperature = PhysicalConstants.Temperature.StandardTemperature; - double standardPressure = PhysicalConstants.Mechanical.StandardAtmosphericPressure; - double calculatedMolarVolume = storedGasConstant * standardTemperature / standardPressure; - double calculatedMolarVolumeInLiters = calculatedMolarVolume * 1000; // Convert m³ to L - double storedMolarVolume = PhysicalConstants.Chemical.MolarVolumeSTP; - - Assert.AreEqual(calculatedMolarVolumeInLiters, storedMolarVolume, RelaxedTolerance, - "MolarVolumeSTP should equal R*T/P in liters"); - } - - [TestMethod] - public void DerivedConstants_GenericGetters_MatchDirectConstants() - { - // Test that all derived constants accessible through Generic getters match their direct values - - // Area conversions - Assert.AreEqual(PhysicalConstants.Conversion.SquareFeetToSquareMeters, - PhysicalConstants.Generic.SquareFeetToSquareMeters(), Tolerance, - "Generic SquareFeetToSquareMeters should match direct constant"); - - Assert.AreEqual(PhysicalConstants.Conversion.SquareInchesToSquareMeters, - PhysicalConstants.Generic.SquareInchesToSquareMeters(), Tolerance, - "Generic SquareInchesToSquareMeters should match direct constant"); - - Assert.AreEqual(PhysicalConstants.Conversion.SquareMilesToSquareMeters, - PhysicalConstants.Generic.SquareMilesToSquareMeters(), Tolerance, - "Generic SquareMilesToSquareMeters should match direct constant"); - - // Time relationships - Assert.AreEqual(PhysicalConstants.Time.SecondsPerMinute, - PhysicalConstants.Generic.SecondsPerMinute(), Tolerance, - "Generic SecondsPerMinute should match direct constant"); - - Assert.AreEqual(PhysicalConstants.Time.SecondsPerHour, - PhysicalConstants.Generic.SecondsPerHour(), Tolerance, - "Generic SecondsPerHour should match direct constant"); - - Assert.AreEqual(PhysicalConstants.Time.SecondsPerDay, - PhysicalConstants.Generic.SecondsPerDay(), Tolerance, - "Generic SecondsPerDay should match direct constant"); - - // Mathematical fractions - Assert.AreEqual(PhysicalConstants.Mathematical.TwoThirds, - PhysicalConstants.Generic.TwoThirds(), Tolerance, - "Generic TwoThirds should match direct constant"); - - Assert.AreEqual(PhysicalConstants.Mathematical.FourThirds, - PhysicalConstants.Generic.FourThirds(), Tolerance, - "Generic FourThirds should match direct constant"); - - // Temperature conversions - Assert.AreEqual(PhysicalConstants.Conversion.FahrenheitToCelsiusSlope, - PhysicalConstants.Generic.FahrenheitToCelsiusSlope(), Tolerance, - "Generic FahrenheitToCelsiusSlope should match direct constant"); - - Assert.AreEqual(PhysicalConstants.Conversion.CelsiusToFahrenheitSlope, - PhysicalConstants.Generic.CelsiusToFahrenheitSlope(), Tolerance, - "Generic CelsiusToFahrenheitSlope should match direct constant"); - } - - [TestMethod] - public void DerivedConstants_ConsistencyAcrossNumericTypes() - { - // Test that derived constants maintain relationships across different numeric types - - // Test area conversion with float - float feetToMetersFloat = PhysicalConstants.Generic.FeetToMeters(); - float squareFeetToSquareMetersFloat = PhysicalConstants.Generic.SquareFeetToSquareMeters(); - float calculatedSquareFeetFloat = feetToMetersFloat * feetToMetersFloat; - - Assert.AreEqual(calculatedSquareFeetFloat, squareFeetToSquareMetersFloat, (float)RelaxedTolerance, - "SquareFeetToSquareMeters relationship should hold for float type"); - - // Test time relationship with decimal (where precision allows) - decimal secondsPerMinuteDecimal = PhysicalConstants.Generic.SecondsPerMinute(); - decimal minutesPerHourDecimal = PhysicalConstants.Generic.MinutesPerHour(); - decimal secondsPerHourDecimal = PhysicalConstants.Generic.SecondsPerHour(); - decimal calculatedSecondsPerHourDecimal = secondsPerMinuteDecimal * minutesPerHourDecimal; - - Assert.AreEqual(calculatedSecondsPerHourDecimal, secondsPerHourDecimal, - "SecondsPerHour relationship should hold for decimal type"); - - // Test mathematical fraction with float - float twoThirdsFloat = PhysicalConstants.Generic.TwoThirds(); - float calculatedTwoThirdsFloat = 2.0f / 3.0f; - - Assert.AreEqual(calculatedTwoThirdsFloat, twoThirdsFloat, (float)RelaxedTolerance, - "TwoThirds relationship should hold for float type"); - } - - [TestMethod] - public void DerivedConstants_ChainedCalculations_MaintainAccuracy() - { - // Test chained calculations using derived constants maintain accuracy - - // Chain: Seconds -> Minutes -> Hours -> Days -> Weeks - double secondsPerMinute = PhysicalConstants.Generic.SecondsPerMinute(); - double minutesPerHour = PhysicalConstants.Generic.MinutesPerHour(); - double hoursPerDay = PhysicalConstants.Generic.HoursPerDay(); - double daysPerWeek = PhysicalConstants.Generic.DaysPerWeek(); - - double calculatedSecondsPerWeek = secondsPerMinute * minutesPerHour * hoursPerDay * daysPerWeek; - double storedSecondsPerWeek = PhysicalConstants.Generic.SecondsPerWeek(); - - Assert.AreEqual(calculatedSecondsPerWeek, storedSecondsPerWeek, Tolerance, - "Chained time calculation should match stored SecondsPerWeek"); - - // Chain: Linear -> Area conversions for different units - double inchesToMeters = PhysicalConstants.Generic.InchesToMeters(); - double feetToMeters = PhysicalConstants.Generic.FeetToMeters(); - - // 1 square foot = 144 square inches - double squareInchesPerSquareFoot = 144.0; - double calculatedSquareInchesToSquareMetersViaFeet = - PhysicalConstants.Generic.SquareFeetToSquareMeters() / squareInchesPerSquareFoot; - double storedSquareInchesToSquareMeters = PhysicalConstants.Generic.SquareInchesToSquareMeters(); - - Assert.AreEqual(calculatedSquareInchesToSquareMetersViaFeet, storedSquareInchesToSquareMeters, RelaxedTolerance, - "Square inch conversion via square foot conversion should match direct conversion"); - } -} diff --git a/Semantics.Test/PhysicalDimensionExtensionsTests.cs b/Semantics.Test/PhysicalDimensionExtensionsTests.cs deleted file mode 100644 index cd093ce..0000000 --- a/Semantics.Test/PhysicalDimensionExtensionsTests.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public sealed class PhysicalDimensionExtensionsTests -{ - private const double Tolerance = 1e-10; - - [TestMethod] - public void Length_Extensions_ConvertToBaseUnits() - { - Assert.AreEqual(1.0, 1.0.Meters().Value, Tolerance); - Assert.AreEqual(1000.0, 1.0.Kilometers().Value, Tolerance); - Assert.AreEqual(0.01, 1.0.Centimeters().Value, Tolerance); - Assert.AreEqual(0.001, 1.0.Millimeters().Value, Tolerance); - Assert.AreEqual(0.3048, 1.0.Feet().Value, Tolerance); - Assert.AreEqual(0.0254, 1.0.Inches().Value, Tolerance); - Assert.AreEqual(0.9144, 1.0.Yards().Value, Tolerance); - Assert.AreEqual(1609.344, 1.0.Miles().Value, Tolerance); - } - - [TestMethod] - public void Mass_Extensions_ConvertToBaseUnits() - { - Assert.AreEqual(1.0, 1.0.Kilograms().Value, Tolerance); - Assert.AreEqual(0.001, 1.0.Grams().Value, Tolerance); - Assert.AreEqual(0.45359237, 1.0.Pounds().Value, 1e-6); - Assert.AreEqual(0.028349523125, 1.0.Ounces().Value, 1e-6); - Assert.AreEqual(6.35029318, 1.0.Stones().Value, 1e-5); - } - - [TestMethod] - public void Time_Extensions_ConvertToBaseUnits() - { - Assert.AreEqual(1.0, 1.0.Seconds().Value, Tolerance); - Assert.AreEqual(60.0, 1.0.Minutes().Value, Tolerance); - Assert.AreEqual(3600.0, 1.0.Hours().Value, Tolerance); - Assert.AreEqual(86400.0, 1.0.Days().Value, Tolerance); - Assert.AreEqual(0.001, 1.0.Milliseconds().Value, Tolerance); - } - - [TestMethod] - public void Area_Extensions_ConvertToBaseUnits() - { - Assert.AreEqual(1.0, 1.0.SquareMeters().Value, Tolerance); - Assert.AreEqual(1_000_000.0, 1.0.SquareKilometers().Value, Tolerance); - Assert.AreEqual(0.09290304, 1.0.SquareFeet().Value, 1e-10); - Assert.AreEqual(4046.8564224, 1.0.Acres().Value, 1e-7); - } - - [TestMethod] - public void Volume_Extensions_ConvertToBaseUnits() - { - Assert.AreEqual(1.0, 1.0.CubicMeters().Value, Tolerance); - Assert.AreEqual(0.001, 1.0.Liters().Value, Tolerance); - Assert.AreEqual(0.000001, 1.0.Milliliters().Value, Tolerance); - Assert.AreEqual(0.028316846592, 1.0.CubicFeet().Value, 1e-7); - Assert.AreEqual(0.003785411784, 1.0.USGallons().Value, 1e-8); - } -} - diff --git a/Semantics.Test/PhysicalDimensionTests.cs b/Semantics.Test/PhysicalDimensionTests.cs deleted file mode 100644 index e8ffa6a..0000000 --- a/Semantics.Test/PhysicalDimensionTests.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public sealed class PhysicalDimensionTests -{ - [TestMethod] - public void Equality_And_HashCode_Work() - { - PhysicalDimension a = new(baseUnit: BootstrapUnits.Meter, length: 1, time: -1); - PhysicalDimension b = new(baseUnit: BootstrapUnits.Meter, length: 1, time: -1); - PhysicalDimension c = new(baseUnit: BootstrapUnits.Meter, length: 2, time: -2); - - Assert.IsTrue(a == b, "Dimensions with same exponents should be equal"); - Assert.IsFalse(a == c, "Dimensions with different exponents should not be equal"); - Assert.AreEqual(a.GetHashCode(), b.GetHashCode()); - } - - [TestMethod] - public void Multiply_And_Divide_ComposeDimensions() - { - PhysicalDimension length = PhysicalDimensions.Length; - PhysicalDimension time = PhysicalDimensions.Time; - PhysicalDimension velocity = length / time; - PhysicalDimension acceleration = velocity / time; - - Assert.AreEqual(PhysicalDimensions.Velocity, velocity); - Assert.AreEqual(PhysicalDimensions.Acceleration, acceleration); - } - - [TestMethod] - public void Pow_RaisesDimension() - { - PhysicalDimension length = PhysicalDimensions.Length; - PhysicalDimension area = PhysicalDimension.Pow(length, 2); - PhysicalDimension volume = PhysicalDimension.Pow(length, 3); - - Assert.AreEqual(PhysicalDimensions.Area, area); - Assert.AreEqual(PhysicalDimensions.Volume, volume); - } - - [TestMethod] - public void ToString_FormatsCorrectly() - { - Assert.AreEqual("1", PhysicalDimensions.Dimensionless.ToString()); - Assert.AreEqual("L", PhysicalDimensions.Length.ToString()); - Assert.AreEqual("L²", PhysicalDimensions.Area.ToString()); - Assert.AreEqual("L T⁻¹", PhysicalDimensions.Velocity.ToString()); - // Order in ToString follows insertion order; verify content rather than exact order - string z = PhysicalDimensions.AcousticImpedance.ToString(); - StringAssert.Contains(z, "M"); - StringAssert.Contains(z, "L⁻²"); - StringAssert.Contains(z, "T⁻¹"); - } -} - diff --git a/Semantics.Test/PhysicalQuantityCoreTests.cs b/Semantics.Test/PhysicalQuantityCoreTests.cs index 8505a38..24d0591 100644 --- a/Semantics.Test/PhysicalQuantityCoreTests.cs +++ b/Semantics.Test/PhysicalQuantityCoreTests.cs @@ -4,51 +4,79 @@ namespace ktsu.Semantics.Test; -using ktsu.Semantics; +using System; +using ktsu.Semantics.Quantities; +using ktsu.Semantics.Quantities.Units; using Microsoft.VisualStudio.TestTools.UnitTesting; +/// +/// Exercises the canonical surface from #59: +/// the Dimension accessor, dimensionally-typed In(I<Dim>Unit) +/// (compile-time safety), and same-dimension CompareTo / Equals. +/// [TestClass] public sealed class PhysicalQuantityCoreTests { [TestMethod] - public void In_LinearUnit_And_OffsetUnit() + public void In_LinearUnit() { Length tenMeters = Length.Create(10.0); double inKilometers = tenMeters.In(Units.Kilometer); Assert.AreEqual(0.01, inKilometers, 1e-12); + } + [TestMethod] + public void In_OffsetUnit() + { Temperature kelvin300 = Temperature.Create(300.0); double celsius = kelvin300.In(Units.Celsius); Assert.AreEqual(26.85, celsius, 1e-2); } + // Note: the previous "should throw UnitConversionException on dimension mismatch" + // test is now expressed at compile time via the typed I{Dim}Unit parameter on In(). + // E.g. `length.In(Units.Kilogram)` no longer compiles because Kilogram implements + // IMassUnit, not ILengthUnit. The UnitConversionException type is kept for any + // future runtime-dispatch path. + [TestMethod] - public void In_InvalidDimension_ShouldThrowUnitConversionException() + public void Dimension_ReportsDimensionForGeneratedQuantity() { Length length = Length.Create(1.0); - Assert.ThrowsExactly(() => length.In(Units.Kilogram)); + Assert.AreEqual("Length", length.Dimension.Name); } [TestMethod] - public void CompareTo_And_Equals_Behavior() + public void CompareTo_SameDimension() { Length a = Length.Create(1.0); Length b = Length.Create(2.0); Assert.IsLessThan(0, a.CompareTo(b)); Assert.IsGreaterThan(0, b.CompareTo(a)); - Assert.IsTrue(a.Equals(Length.Create(1.0))); + Assert.AreEqual(0, a.CompareTo(Length.Create(1.0))); + } - IPhysicalQuantity other = new FakeQuantity(); - Assert.ThrowsExactly(() => a.CompareTo(other)); + [TestMethod] + public void CompareTo_CrossDimension_Throws() + { + Length length = Length.Create(1.0); + IPhysicalQuantity mass = Mass.Create(1.0); + Assert.ThrowsExactly(() => length.CompareTo(mass)); } - private sealed class FakeQuantity : IPhysicalQuantity + [TestMethod] + public void Equals_SameDimensionAndValue() { - public PhysicalDimension Dimension => PhysicalDimensions.Mass; - public bool IsPhysicallyValid => true; - public double Quantity => 0.0; - public double In(IUnit targetUnit) => 0.0; - public int CompareTo(IPhysicalQuantity? other) => 0; - public bool Equals(IPhysicalQuantity? other) => false; + Length a = Length.Create(1.0); + Length b = Length.Create(1.0); + Assert.IsTrue(a.Equals((IPhysicalQuantity)b)); + } + + [TestMethod] + public void Equals_CrossDimension_ReturnsFalse() + { + Length length = Length.Create(1.0); + IPhysicalQuantity mass = Mass.Create(1.0); + Assert.IsFalse(length.Equals(mass)); } } diff --git a/Semantics.Test/Quantities/GeneratorOutputInvariantTests.cs b/Semantics.Test/Quantities/GeneratorOutputInvariantTests.cs new file mode 100644 index 0000000..00134b4 --- /dev/null +++ b/Semantics.Test/Quantities/GeneratorOutputInvariantTests.cs @@ -0,0 +1,157 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Test.Quantities; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using ktsu.Semantics.Quantities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Locks in invariants on the source generator's output. Issue #57 raised the concern that +/// the generator's dedup keys could let two methods (or operators) with the same name and +/// parameter types land on the same type — which the C# compiler already rejects, but this +/// test makes the property explicit and regression-proof if the dedup logic changes. +/// +[TestClass] +public sealed class GeneratorOutputInvariantTests +{ + /// + /// For every generated quantity type in ktsu.Semantics.Quantities, no two public + /// static methods (including operators) share both the same name and the same parameter + /// type list. Walks the runtime assembly rather than re-parsing the .g.cs files because + /// the compiled types are the source of truth — anything the test sees is what consumers + /// would call. + /// + [TestMethod] + public void NoDuplicatePublicStaticMethodsOrOperatorsPerGeneratedType() + { + Assembly assembly = typeof(Mass<>).Assembly; + List generatedQuantityTypes = [.. CollectGeneratedQuantityTypes(assembly)]; + + // Sanity: we should be looking at a non-trivial set, otherwise the test is silently + // vacuous (e.g. namespace got renamed and the filter dropped everything). + Assert.IsTrue( + generatedQuantityTypes.Count > 50, + $"Expected to find many generated quantity types (got {generatedQuantityTypes.Count}). The filter likely needs updating."); + + List failures = []; + foreach (Type type in generatedQuantityTypes) + { + MethodInfo[] staticMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); + + IEnumerable> groups = staticMethods.GroupBy(SignatureKey); + foreach (IGrouping group in groups) + { + int count = group.Count(); + if (count > 1) + { + failures.Add($"{type.Name}: {group.Key} appears {count} times"); + } + } + } + + if (failures.Count > 0) + { + Assert.Fail( + $"Found duplicate public static method signatures on generated quantity types:\n " + + string.Join("\n ", failures)); + } + } + + /// + /// Cross-dimensional operator * overloads should exist in both operand orders so + /// either-order user code (mass * accel and accel * mass) compiles. The + /// generator's CollectAllOperators emits both directions; this test asserts the + /// commutativity property explicitly so a regression in the dedup keys would fail here + /// before it reaches a downstream consumer. + /// + [TestMethod] + public void EveryCrossDimensionalMultiplicationHasBothOperandOrders() + { + Assembly assembly = typeof(Mass<>).Assembly; + List types = [.. CollectGeneratedQuantityTypes(assembly)]; + + // Collect every observed operator * signature as a tuple (left, right, returnType). + HashSet<(string Left, string Right, string Result)> observed = []; + foreach (Type type in types) + { + foreach (MethodInfo m in type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)) + { + if (m.Name != "op_Multiply") + { + continue; + } + + ParameterInfo[] pars = m.GetParameters(); + if (pars.Length != 2) + { + continue; + } + + observed.Add((pars[0].ParameterType.Name, pars[1].ParameterType.Name, m.ReturnType.Name)); + } + } + + // For every cross-dimensional product (operands of distinct types), the swapped pair + // should also be present with the same return. Same-type products (T * T) are exempt + // because there is no swap — they're idempotent under reorder. + List missing = []; + foreach ((string left, string right, string result) in observed) + { + if (left == right) + { + continue; + } + + if (!observed.Contains((right, left, result))) + { + missing.Add($"missing reverse pair: {right} * {left} -> {result} (forward {left} * {right} -> {result} exists)"); + } + } + + if (missing.Count > 0) + { + Assert.Fail( + "Cross-dimensional multiplication should be emitted in both operand orders, but found " + + $"{missing.Count} unmatched forward(s):\n " + + string.Join("\n ", missing)); + } + } + + private static IEnumerable CollectGeneratedQuantityTypes(Assembly assembly) + { + foreach (Type type in assembly.GetTypes()) + { + if (type.Namespace != "ktsu.Semantics.Quantities") + { + continue; + } + + if (!type.IsGenericTypeDefinition) + { + continue; + } + + // Generated quantity types implement one of IVector0..IVector4 (closed over TSelf, T). + bool isQuantity = type.GetInterfaces().Any(static i => + i.IsGenericType && i.Name.StartsWith("IVector", StringComparison.Ordinal)); + if (!isQuantity) + { + continue; + } + + yield return type; + } + } + + private static string SignatureKey(MethodInfo m) + { + string parameterList = string.Join(",", m.GetParameters().Select(static p => p.ParameterType.FullName ?? p.ParameterType.Name)); + return $"{m.Name}({parameterList})"; + } +} diff --git a/Semantics.Test/Quantities/MultiUnitFactoryTests.cs b/Semantics.Test/Quantities/MultiUnitFactoryTests.cs new file mode 100644 index 0000000..c871269 --- /dev/null +++ b/Semantics.Test/Quantities/MultiUnitFactoryTests.cs @@ -0,0 +1,148 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Test.Quantities; + +using ktsu.Semantics.Quantities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Verifies that QuantitiesGenerator emits one From{Unit} factory for every +/// unit listed in a dimension's availableUnits, applying the conversion factor. +/// Issue #48. +/// +[TestClass] +public sealed class MultiUnitFactoryTests +{ + private const double Tolerance = 1e-9; + + // ---- Length ---- + + [TestMethod] + public void Length_FromMeters_Identity() + { + Length l = Length.FromMeter(1.0); + Assert.AreEqual(1.0, l.Value, Tolerance); + } + + [TestMethod] + public void Length_FromKilometers_Scales_By_1000() + { + Length l = Length.FromKilometer(1.0); + Assert.AreEqual(1000.0, l.Value, Tolerance); + } + + [TestMethod] + public void Length_FromCentimeters_Scales_By_0_01() + { + Length l = Length.FromCentimeter(1.0); + Assert.AreEqual(0.01, l.Value, Tolerance); + } + + [TestMethod] + public void Length_FromMillimeters_Scales_By_0_001() + { + Length l = Length.FromMillimeter(1.0); + Assert.AreEqual(0.001, l.Value, Tolerance); + } + + [TestMethod] + public void Length_FromFeet_Uses_FeetToMeters_Constant() + { + Length l = Length.FromFoot(1.0); + Assert.AreEqual(0.3048, l.Value, Tolerance); + } + + [TestMethod] + public void Length_FromInches_Uses_InchesToMeters_Constant() + { + Length l = Length.FromInch(1.0); + Assert.AreEqual(0.0254, l.Value, Tolerance); + } + + [TestMethod] + public void Length_FromMiles_Uses_MileToMeters_Constant() + { + Length l = Length.FromMile(1.0); + Assert.AreEqual(1609.344, l.Value, Tolerance); + } + + // ---- Mass ---- + + [TestMethod] + public void Mass_FromKilograms_Identity() + { + Mass m = Mass.FromKilogram(1.0); + Assert.AreEqual(1.0, m.Value, Tolerance); + } + + [TestMethod] + public void Mass_FromGrams_Scales_By_0_001() + { + Mass m = Mass.FromGram(1.0); + Assert.AreEqual(0.001, m.Value, Tolerance); + } + + [TestMethod] + public void Mass_FromPounds_Uses_PoundToKilograms_Constant() + { + Mass m = Mass.FromPound(1.0); + Assert.AreEqual(0.45359237, m.Value, Tolerance); + } + + // ---- Time / Duration ---- + + [TestMethod] + public void Duration_FromMinutes_Equals_60_Seconds() + { + Duration d = Duration.FromMinute(1.0); + Assert.AreEqual(60.0, d.Value, Tolerance); + } + + [TestMethod] + public void Duration_FromHours_Equals_3600_Seconds() + { + Duration d = Duration.FromHour(1.0); + Assert.AreEqual(3600.0, d.Value, Tolerance); + } + + // ---- Semantic overloads inherit their dimension's full unit set ---- + + [TestMethod] + public void Distance_FromKilometers_Scales_By_1000() + { + Distance d = Distance.FromKilometer(1.0); + Assert.AreEqual(1000.0, d.Value, Tolerance); + } + + [TestMethod] + public void Diameter_FromMillimeters_Scales_By_0_001() + { + Diameter d = Diameter.FromMillimeter(1.0); + Assert.AreEqual(0.001, d.Value, Tolerance); + } + + [TestMethod] + public void Wavelength_FromNanometers_Uses_Nano_Magnitude() + { + Wavelength w = Wavelength.FromNanometer(550.0); + Assert.AreEqual(550.0e-9, w.Value, 1e-15); + } + + // ---- Storage genericity ---- + + [TestMethod] + public void Length_FromKilometers_Works_With_Float() + { + Length l = Length.FromKilometer(1.0f); + Assert.AreEqual(1000.0f, l.Value, 1e-3f); + } + + [TestMethod] + public void Length_FromFeet_Works_With_Decimal() + { + Length l = Length.FromFoot(1m); + Assert.AreEqual(0.3048m, l.Value); + } +} diff --git a/Semantics.Test/Quantities/QuantityBackfillTests.cs b/Semantics.Test/Quantities/QuantityBackfillTests.cs new file mode 100644 index 0000000..61b5611 --- /dev/null +++ b/Semantics.Test/Quantities/QuantityBackfillTests.cs @@ -0,0 +1,190 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Test.Quantities; + +using ktsu.Semantics.Quantities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Covers the quantities backfilled from main: new electrical/chemical/thermal/photometric +/// dimensions, acoustic overloads and coefficients, and the hand-written logarithmic-scale +/// companions (sound levels and pH). +/// +[TestClass] +public sealed class QuantityBackfillTests +{ + private const double Tolerance = 1e-9; + + // ---- New generated dimensions ---- + + [TestMethod] + public void Luminance_Factories_And_Intensity_Relationship() + { + Luminance nit = Luminance.FromNit(100.0); + Assert.AreEqual(100.0, nit.Value, Tolerance); + Assert.AreEqual(3.4262590996353905, Luminance.FromFootLambert(1.0).Value, 1e-9); + + LuminousIntensity intensity = nit * Area.FromSquareMeter(2.0); + Assert.AreEqual(200.0, intensity.Value, Tolerance); + } + + [TestMethod] + public void Permittivity_And_Conductivity_Factories() + { + Assert.AreEqual(8.854e-12, Permittivity.FromFaradPerMeter(8.854e-12).Value, 1e-21); + Assert.AreEqual(5.96e7, ElectricConductivity.FromSiemensPerMeter(5.96e7).Value, 1.0); + } + + [TestMethod] + public void ElectricFlux_From_Field_Times_Area() + { + ElectricFieldMagnitude field = ElectricFieldMagnitude.FromVoltPerMeter(100.0); + ElectricFlux flux = field * Area.FromSquareMeter(0.5); + Assert.AreEqual(50.0, flux.Value, Tolerance); + } + + [TestMethod] + public void ElectricPowerDensity_Times_Volume_Is_Power() + { + ElectricPowerDensity density = ElectricPowerDensity.FromWattPerCubicMeter(250.0); + Power power = density * Volume.FromCubicMeter(4.0); + Assert.AreEqual(1000.0, power.Value, Tolerance); + } + + [TestMethod] + public void Sensitivity_Times_Pressure_Is_Voltage() + { + // A 50 mV/Pa microphone at 1 Pa (94 dB SPL) produces 50 mV. + Sensitivity mic = Sensitivity.FromVoltPerPascal(0.05); + VoltageMagnitude output = mic * Pressure.FromPascal(1.0); + Assert.AreEqual(0.05, output.Value, Tolerance); + } + + [TestMethod] + public void ThermalResistance_And_RateConstant_Factories() + { + Assert.AreEqual(2.5, ThermalResistance.FromKelvinPerWatt(2.5).Value, Tolerance); + Assert.AreEqual(0.693, RateConstant.FromPerSecond(0.693).Value, Tolerance); + } + + [TestMethod] + public void Loudness_And_Sharpness_Factories() + { + Assert.AreEqual(2.0, Loudness.FromSone(2.0).Value, Tolerance); + Assert.AreEqual(1.5, Sharpness.FromAcum(1.5).Value, Tolerance); + } + + // ---- New overloads ---- + + [TestMethod] + public void Impedance_Widens_To_Resistance() + { + Impedance z = Impedance.FromOhm(8.0); + Resistance r = z; + Assert.AreEqual(8.0, r.Value, Tolerance); + + // Ohm's law applies through the overload: V = I·Z. + VoltageMagnitude v = CurrentMagnitude.FromAmpere(2.0) * z; + Assert.AreEqual(16.0, v.Value, Tolerance); + } + + [TestMethod] + public void SoundPressure_And_SoundPower_Widen_To_Bases() + { + Pressure p = SoundPressure.FromPascal(0.2); + Assert.AreEqual(0.2, p.Value, Tolerance); + + Power w = SoundPower.FromWatt(0.01); + Assert.AreEqual(0.01, w.Value, Tolerance); + } + + [TestMethod] + public void Acoustic_Coefficients_Are_Ratio_Overloads() + { + SoundAbsorption alpha = SoundAbsorption.Create(0.85); + Ratio asRatio = alpha; + Assert.AreEqual(0.85, asRatio.Value, Tolerance); + + Assert.AreEqual(0.65, NoiseReductionCoefficient.Create(0.65).Value, Tolerance); + Assert.AreEqual(52.0, SoundTransmissionClass.Create(52.0).Value, Tolerance); + } + + [TestMethod] + public void ReflectionCoefficient_Is_Signed() + { + // A pressure-release boundary reflects with inverted phase. + ReflectionCoefficient r = ReflectionCoefficient.Create(-1.0); + Assert.AreEqual(-1.0, r.Value, Tolerance); + } + + // ---- Logarithmic-scale companions ---- + + [TestMethod] + public void SoundPressureLevel_From_Pressure_And_Back() + { + // 0.02 Pa is 1000× the 20 µPa reference: 20·log10(1000) = 60 dB. + SoundPressureLevel spl = SoundPressureLevel.FromSoundPressure(SoundPressure.FromPascal(0.02)); + Assert.AreEqual(60.0, spl.Value, 1e-9); + + SoundPressure back = spl.ToSoundPressure(); + Assert.AreEqual(0.02, back.Value, 1e-12); + } + + [TestMethod] + public void SoundIntensityLevel_From_Intensity_And_Back() + { + SoundIntensityLevel sil = SoundIntensityLevel.FromSoundIntensity(SoundIntensity.FromWattPerSquareMeter(1e-6)); + Assert.AreEqual(60.0, sil.Value, 1e-9); + Assert.AreEqual(1e-6, sil.ToSoundIntensity().Value, 1e-15); + } + + [TestMethod] + public void SoundPowerLevel_From_Power_And_Back() + { + SoundPowerLevel swl = SoundPowerLevel.FromSoundPower(SoundPower.FromWatt(1e-3)); + Assert.AreEqual(90.0, swl.Value, 1e-9); + Assert.AreEqual(1e-3, swl.ToSoundPower().Value, 1e-12); + } + + [TestMethod] + public void DirectionalityIndex_RoundTrips_Intensity_Ratio() + { + DirectionalityIndex di = DirectionalityIndex.FromIntensityRatio(Ratio.Create(10.0)); + Assert.AreEqual(10.0, di.Value, 1e-9); + Assert.AreEqual(10.0, di.ToIntensityRatio().Value, 1e-9); + Assert.AreEqual(0.0, DirectionalityIndex.Omnidirectional.Value, Tolerance); + } + + [TestMethod] + public void PH_From_Concentration_And_Back() + { + // Pure water: [H+] = 1e-7 mol/L. + PH ph = PH.FromHydrogenConcentration(Concentration.FromMolar(1e-7)); + Assert.AreEqual(7.0, ph.Value, 1e-9); + + Concentration back = ph.ToHydrogenConcentration(); + Assert.AreEqual(Concentration.FromMolar(1e-7).Value, back.Value, 1e-12); + } + + [TestMethod] + public void PH_Acidity_Classification_And_POH() + { + PH lemon = PH.Create(2.0); + Assert.IsTrue(lemon.IsAcidic); + Assert.IsFalse(lemon.IsBasic); + Assert.AreEqual(12.0, lemon.ToPOH().Value, Tolerance); + Assert.AreEqual(7.0, PH.Neutral.Value, Tolerance); + Assert.IsFalse(PH.Neutral.IsAcidic); + } + + [TestMethod] + public void Levels_Compare_And_Add_In_Decibel_Space() + { + SoundPressureLevel quiet = SoundPressureLevel.FromDecibels(40.0); + SoundPressureLevel loud = SoundPressureLevel.FromDecibels(90.0); + Assert.IsTrue(quiet < loud); + Assert.AreEqual(50.0, (loud - quiet).Value, Tolerance); + } +} diff --git a/Semantics.Test/Quantities/SemanticOverloadTests.cs b/Semantics.Test/Quantities/SemanticOverloadTests.cs new file mode 100644 index 0000000..59be66d --- /dev/null +++ b/Semantics.Test/Quantities/SemanticOverloadTests.cs @@ -0,0 +1,148 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Test.Quantities; + +using ktsu.Semantics.Quantities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Covers semantic overload conversions and metadata-driven relationships. +/// Issue #55. +/// +[TestClass] +public sealed class SemanticOverloadTests +{ + private const double Tolerance = 1e-10; + + // ----------------------------------------- Implicit widening to base + + [TestMethod] + public void Weight_Widens_Implicitly_To_ForceMagnitude() + { + Weight w = Weight.FromNewton(686.0); + ForceMagnitude baseValue = w; // implicit conversion + Assert.AreEqual(686.0, baseValue.Value, Tolerance); + } + + [TestMethod] + public void Distance_Widens_Implicitly_To_Length() + { + Distance d = Distance.FromMeter(42.0); + Length len = d; + Assert.AreEqual(42.0, len.Value, Tolerance); + } + + [TestMethod] + public void Diameter_Widens_Implicitly_To_Length() + { + Diameter diam = Diameter.FromMeter(10.0); + Length len = diam; + Assert.AreEqual(10.0, len.Value, Tolerance); + } + + // ---------------------------------------- Explicit narrowing from base + + [TestMethod] + public void ForceMagnitude_Narrows_Explicitly_To_Weight() + { + ForceMagnitude fm = ForceMagnitude.FromNewton(686.0); + Weight w = (Weight)fm; + Assert.AreEqual(686.0, w.Value, Tolerance); + } + + [TestMethod] + public void Length_Narrows_Explicitly_To_Distance() + { + Length len = Length.FromMeter(42.0); + Distance d = (Distance)len; + Assert.AreEqual(42.0, d.Value, Tolerance); + } + + // --------------------------------------------- From(base) factory + + [TestMethod] + public void Weight_From_ForceMagnitude_Constructs() + { + ForceMagnitude fm = ForceMagnitude.FromNewton(100.0); + Weight w = Weight.From(fm); + Assert.AreEqual(100.0, w.Value, Tolerance); + } + + [TestMethod] + public void Distance_From_Length_Constructs() + { + Length len = Length.FromMeter(7.0); + Distance d = Distance.From(len); + Assert.AreEqual(7.0, d.Value, Tolerance); + } + + // -------------------- Round-trip widen/narrow preserves value + + [TestMethod] + public void Weight_RoundTrip_Through_ForceMagnitude_Preserves_Value() + { + Weight original = Weight.FromNewton(123.456); + ForceMagnitude widened = original; + Weight narrowed = (Weight)widened; + Assert.AreEqual(original.Value, narrowed.Value, Tolerance); + } + + // ------------------ Metadata-defined relationship: Diameter <-> Radius + + [TestMethod] + public void Diameter_ToRadius_Halves_Value() + { + Diameter d = Diameter.FromMeter(10.0); + Radius r = d.ToRadius(); + Assert.AreEqual(5.0, r.Value, Tolerance); + } + + [TestMethod] + public void Diameter_FromRadius_Doubles_Value() + { + Radius r = Radius.FromMeter(5.0); + Diameter d = Diameter.FromRadius(r); + Assert.AreEqual(10.0, d.Value, Tolerance); + } + + [TestMethod] + public void Diameter_RoundTrip_Through_Radius_Preserves_Value() + { + Diameter d = Diameter.FromMeter(20.0); + Radius r = d.ToRadius(); + Diameter back = Diameter.FromRadius(r); + Assert.AreEqual(d.Value, back.Value, Tolerance); + } + + // ----------------- V0 overload subtraction + // Locked in #52: V0 - V0 returns the same V0 of T.Abs(a - b). + + [TestMethod] + public void Weight_Minus_Weight_Returns_Absolute_Weight() + { + Weight a = Weight.FromNewton(100.0); + Weight b = Weight.FromNewton(150.0); + Weight diff = a - b; + Assert.AreEqual(50.0, diff.Value, Tolerance); + } + + // ----------------- Storage-type genericity sanity + + [TestMethod] + public void Diameter_ToRadius_Works_With_Float_Storage() + { + Diameter d = Diameter.FromMeter(10.0f); + Radius r = d.ToRadius(); + Assert.AreEqual(5.0f, r.Value, 1e-6f); + } + + [TestMethod] + public void Diameter_ToRadius_Works_With_Decimal_Storage() + { + Diameter d = Diameter.FromMeter(10m); + Radius r = d.ToRadius(); + Assert.AreEqual(5m, r.Value); + } +} diff --git a/Semantics.Test/Quantities/UnitBackfillTests.cs b/Semantics.Test/Quantities/UnitBackfillTests.cs new file mode 100644 index 0000000..31b3d4e --- /dev/null +++ b/Semantics.Test/Quantities/UnitBackfillTests.cs @@ -0,0 +1,231 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Test.Quantities; + +using ktsu.Semantics.Quantities; +using ktsu.Semantics.Quantities.Units; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Covers the unit catalog backfilled from main (imperial/US/CGS/traditional units and +/// SI-prefixed conveniences) plus the corrected Fahrenheit/Rankine affine conversions. +/// +[TestClass] +public sealed class UnitBackfillTests +{ + private const double Tolerance = 1e-9; + + // ---- Temperature: the affine conversion was inverted before the backfill ---- + + [TestMethod] + public void Temperature_FromFahrenheit_FreezingPoint_Is_273_15_Kelvin() + { + Temperature t = Temperature.FromFahrenheit(32.0); + Assert.AreEqual(273.15, t.Value, Tolerance); + } + + [TestMethod] + public void Temperature_FromFahrenheit_BoilingPoint_Is_373_15_Kelvin() + { + Temperature t = Temperature.FromFahrenheit(212.0); + Assert.AreEqual(373.15, t.Value, Tolerance); + } + + [TestMethod] + public void Temperature_In_Fahrenheit_RoundTrips() + { + Temperature t = Temperature.FromFahrenheit(98.6); + Assert.AreEqual(98.6, t.In(Units.Fahrenheit), Tolerance); + } + + [TestMethod] + public void Temperature_FromRankine_Is_Absolute_With_Fahrenheit_Degrees() + { + Temperature t = Temperature.FromRankine(491.67); + Assert.AreEqual(273.15, t.Value, Tolerance); + } + + // ---- Length / Area / Volume ---- + + [TestMethod] + public void Length_FromNauticalMiles_Is_1852_Meters() + { + Length l = Length.FromNauticalMile(1.0); + Assert.AreEqual(1852.0, l.Value, Tolerance); + Assert.AreEqual(1.0, l.In(Units.NauticalMile), Tolerance); + } + + [TestMethod] + public void Area_FromAcres_And_Hectares() + { + Assert.AreEqual(4046.8564224, Area.FromAcre(1.0).Value, Tolerance); + Assert.AreEqual(10000.0, Area.FromHectare(1.0).Value, Tolerance); + Assert.AreEqual(2589988.110336, Area.FromSquareMile(1.0).Value, Tolerance); + Assert.AreEqual(1e6, Area.FromSquareKilometer(1.0).Value, Tolerance); + } + + [TestMethod] + public void Volume_From_Imperial_And_US_Units() + { + Assert.AreEqual(0.028316846592, Volume.FromCubicFoot(1.0).Value, Tolerance); + Assert.AreEqual(0.00454609, Volume.FromImperialGallon(1.0).Value, Tolerance); + Assert.AreEqual(0.000946352946, Volume.FromUSQuart(1.0).Value, Tolerance); + Assert.AreEqual(1e-6, Volume.FromCubicCentimeter(1.0).Value, Tolerance); + } + + // ---- Mass ---- + + [TestMethod] + public void Mass_FromStone_And_ShortTons() + { + Assert.AreEqual(6.35029318, Mass.FromStone(1.0).Value, Tolerance); + Assert.AreEqual(907.18474, Mass.FromShortTon(1.0).Value, Tolerance); + } + + [TestMethod] + public void Mass_FromAtomicMassUnits_Is_CODATA_Value() + { + Mass m = Mass.FromAtomicMassUnit(1.0); + Assert.AreEqual(1.66053906660e-27, m.Value, 1e-36); + } + + // ---- Mechanics ---- + + [TestMethod] + public void Speed_FromKnots_Is_NauticalMilePerHour() + { + Speed s = Speed.FromKnot(1.0); + Assert.AreEqual(1852.0 / 3600.0, s.Value, Tolerance); + } + + [TestMethod] + public void AccelerationMagnitude_FromStandardGravity_Is_9_80665() + { + AccelerationMagnitude a = AccelerationMagnitude.FromStandardGravity(1.0); + Assert.AreEqual(9.80665, a.Value, Tolerance); + } + + [TestMethod] + public void ForceMagnitude_FromPoundsForce_And_Dynes() + { + Assert.AreEqual(4.4482216152605, ForceMagnitude.FromPoundForce(1.0).Value, Tolerance); + Assert.AreEqual(1e-5, ForceMagnitude.FromDyne(1.0).Value, Tolerance); + } + + [TestMethod] + public void Pressure_FromKilopascals_And_Torr() + { + Assert.AreEqual(1000.0, Pressure.FromKilopascal(1.0).Value, Tolerance); + Assert.AreEqual(101325.0, Pressure.FromTorr(760.0).Value, 1e-6); + } + + [TestMethod] + public void Energy_From_Btus_WattHours_Ergs_Kilocalories() + { + Assert.AreEqual(1055.05585262, Energy.FromBtu(1.0).Value, Tolerance); + Assert.AreEqual(3600.0, Energy.FromWattHour(1.0).Value, Tolerance); + Assert.AreEqual(1e-7, Energy.FromErg(1.0).Value, Tolerance); + Assert.AreEqual(4184.0, Energy.FromKilocalorie(1.0).Value, Tolerance); + } + + [TestMethod] + public void Power_FromKilowatts_And_Megawatts() + { + Assert.AreEqual(1500.0, Power.FromKilowatt(1.5).Value, Tolerance); + Assert.AreEqual(1e6, Power.FromMegawatt(1.0).Value, Tolerance); + } + + // ---- Electromagnetism ---- + + [TestMethod] + public void ChargeMagnitude_FromAmpereHours_Is_3600_Coulombs() + { + Assert.AreEqual(3600.0, ChargeMagnitude.FromAmpereHour(1.0).Value, Tolerance); + } + + [TestMethod] + public void Capacitance_From_SI_Prefixed_Farads() + { + Assert.AreEqual(1e-6, Capacitance.FromMicrofarad(1.0).Value, 1e-15); + Assert.AreEqual(1e-12, Capacitance.FromPicofarad(1.0).Value, 1e-21); + } + + // ---- Frequency / angle / ratio ---- + + [TestMethod] + public void Frequency_FromKilohertz_And_Megahertz() + { + Assert.AreEqual(1000.0, Frequency.FromKilohertz(1.0).Value, Tolerance); + Assert.AreEqual(1e6, Frequency.FromMegahertz(1.0).Value, Tolerance); + } + + [TestMethod] + public void Angle_FromGradians_And_Revolutions() + { + Assert.AreEqual(Math.PI, Angle.FromGradian(200.0).Value, Tolerance); + Assert.AreEqual(2.0 * Math.PI, Angle.FromRevolution(1.0).Value, Tolerance); + } + + [TestMethod] + public void Ratio_FromPartPerMillion_And_Billion() + { + Assert.AreEqual(1e-6, Ratio.FromPartPerMillion(1.0).Value, 1e-15); + Assert.AreEqual(1e-9, Ratio.FromPartPerBillion(1.0).Value, 1e-18); + } + + // ---- Chemistry ---- + + [TestMethod] + public void Concentration_FromMillimolars_Is_One_MolePerCubicMeter() + { + Assert.AreEqual(1.0, Concentration.FromMillimolar(1.0).Value, Tolerance); + } + + [TestMethod] + public void MolarMass_FromDaltons_Equals_GramPerMole() + { + Assert.AreEqual(0.001, MolarMass.FromDalton(1.0).Value, Tolerance); + Assert.AreEqual(MolarMass.FromGramPerMole(1.0).Value, MolarMass.FromDalton(1.0).Value, Tolerance); + } + + // ---- Radiology ---- + + [TestMethod] + public void Traditional_Radiological_Units_Convert_To_SI() + { + Assert.AreEqual(3.7e10, RadioactiveActivity.FromCurie(1.0).Value, 1.0); + Assert.AreEqual(1.0, AbsorbedDose.FromRad(100.0).Value, Tolerance); + Assert.AreEqual(1.0, EquivalentDose.FromRem(100.0).Value, Tolerance); + Assert.AreEqual(2.58e-4, Exposure.FromRoentgen(1.0).Value, 1e-12); + } + + // ---- Density ---- + + [TestMethod] + public void Density_FromGramPerCubicCentimeter_Is_1000_KilogramPerCubicMeter() + { + Assert.AreEqual(1000.0, Density.FromGramPerCubicCentimeter(1.0).Value, Tolerance); + Assert.AreEqual(1.0, Density.FromGramPerLiter(1.0).Value, Tolerance); + } + + // ---- Physical constants backfilled into domains.json ---- + + [TestMethod] + public void Acoustic_Reference_Constants_Are_Available() + { + Assert.AreEqual(20e-6, PhysicalConstants.Generic.ReferenceSoundPressure(), 1e-12); + Assert.AreEqual(1e-12, PhysicalConstants.Generic.ReferenceSoundIntensity(), 1e-21); + Assert.AreEqual(0.161, PhysicalConstants.Generic.SabineConstant(), Tolerance); + } + + [TestMethod] + public void Optical_Nuclear_And_Fluid_Constants_Are_Available() + { + Assert.AreEqual(683.0, PhysicalConstants.Generic.LuminousEfficacy(), Tolerance); + Assert.AreEqual(5.0507837461e-27, PhysicalConstants.Generic.NuclearMagneton(), 1e-36); + Assert.AreEqual(1.225, PhysicalConstants.Generic.StandardAirDensity(), Tolerance); + Assert.AreEqual(0.0728, PhysicalConstants.Generic.WaterSurfaceTension(), Tolerance); + } +} diff --git a/Semantics.Test/Quantities/Vector0InvariantTests.cs b/Semantics.Test/Quantities/Vector0InvariantTests.cs new file mode 100644 index 0000000..7274ca6 --- /dev/null +++ b/Semantics.Test/Quantities/Vector0InvariantTests.cs @@ -0,0 +1,286 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Test.Quantities; + +using System; +using ktsu.Semantics.Quantities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Verifies the Vector0 invariants locked in docs/strategy-unified-vector-quantities.md: +/// +/// Issue #50: factories reject negative inputs with . +/// Issue #52: V0 - V0 returns the same V0 of T.Abs(a - b). +/// +/// +[TestClass] +public sealed class Vector0InvariantTests +{ + private const double Tolerance = 1e-10; + + // =========================================================== #50: Non-negativity + + [TestMethod] + public void Speed_FromMeterPerSecond_Negative_Throws() + => _ = Assert.ThrowsExactly(() => Speed.FromMeterPerSecond(-1.0)); + + [TestMethod] + public void Mass_FromKilograms_Negative_Throws() + => _ = Assert.ThrowsExactly(() => Mass.FromKilogram(-0.5)); + + [TestMethod] + public void Length_FromMeters_Negative_Throws() + => _ = Assert.ThrowsExactly(() => Length.FromMeter(-3.0)); + + [TestMethod] + public void Energy_FromJoules_Negative_Throws() + => _ = Assert.ThrowsExactly(() => Energy.FromJoule(-100.0)); + + [TestMethod] + public void Speed_FromMeterPerSecond_Zero_Allowed() + { + Speed s = Speed.FromMeterPerSecond(0.0); + Assert.AreEqual(0.0, s.Value, Tolerance); + } + + [TestMethod] + public void Mass_FromKilograms_Positive_Returns_Same_Value() + { + Mass m = Mass.FromKilogram(2.5); + Assert.AreEqual(2.5, m.Value, Tolerance); + } + + // V0 overloads inherit non-negativity from their dimension. + + [TestMethod] + public void Distance_FromMeters_Negative_Throws() + => _ = Assert.ThrowsExactly(() => Distance.FromMeter(-1.0)); + + [TestMethod] + public void Weight_FromNewtons_Negative_Throws() + => _ = Assert.ThrowsExactly(() => Weight.FromNewton(-9.81)); + + // V1 quantities are signed and accept any input. + + [TestMethod] + public void Velocity1D_FromMeterPerSecond_Negative_Allowed() + { + Velocity1D v = Velocity1D.FromMeterPerSecond(-3.5); + Assert.AreEqual(-3.5, v.Value, Tolerance); + } + + [TestMethod] + public void TemperatureDelta_FromKelvins_Negative_Allowed() + { + TemperatureDelta dt = TemperatureDelta.FromKelvin(-10.0); + Assert.AreEqual(-10.0, dt.Value, Tolerance); + } + + // Storage-type genericity for the guard. + + [TestMethod] + public void Mass_FromKilograms_Negative_Throws_With_Float() + => _ = Assert.ThrowsExactly(() => Mass.FromKilogram(-1.0f)); + + [TestMethod] + public void Mass_FromKilograms_Negative_Throws_With_Decimal() + => _ = Assert.ThrowsExactly(() => Mass.FromKilogram(-1m)); + + // =========================================================== #52: Absolute subtraction + + [TestMethod] + public void Mass_Minus_Larger_Mass_Returns_Mass_Of_Absolute_Difference() + { + Mass small = Mass.FromKilogram(3.0); + Mass large = Mass.FromKilogram(5.0); + Mass diff = small - large; + Assert.AreEqual(2.0, diff.Value, Tolerance); + Assert.IsInstanceOfType>(diff); + } + + [TestMethod] + public void Mass_Minus_Smaller_Mass_Returns_Positive_Mass() + { + Mass large = Mass.FromKilogram(5.0); + Mass small = Mass.FromKilogram(3.0); + Mass diff = large - small; + Assert.AreEqual(2.0, diff.Value, Tolerance); + } + + [TestMethod] + public void Speed_Minus_Speed_Returns_Speed_Of_Absolute_Difference() + { + Speed a = Speed.FromMeterPerSecond(20.0); + Speed b = Speed.FromMeterPerSecond(50.0); + Speed diff = a - b; + Assert.AreEqual(30.0, diff.Value, Tolerance); + } + + [TestMethod] + public void Length_Minus_Length_Returns_Length() + { + Length a = Length.FromMeter(7.0); + Length b = Length.FromMeter(2.0); + Length diff = a - b; + Assert.AreEqual(5.0, diff.Value, Tolerance); + } + + // V0 overloads preserve their type under subtraction (no longer fall through to V1). + + [TestMethod] + public void Weight_Minus_Weight_Stays_Weight_With_Absolute_Difference() + { + Weight a = Weight.FromNewton(100.0); + Weight b = Weight.FromNewton(150.0); + Weight diff = a - b; + Assert.AreEqual(50.0, diff.Value, Tolerance); + Assert.IsInstanceOfType>(diff); + } + + [TestMethod] + public void Distance_Minus_Distance_Stays_Distance() + { + Distance a = Distance.FromMeter(2.5); + Distance b = Distance.FromMeter(7.5); + Distance diff = a - b; + Assert.AreEqual(5.0, diff.Value, Tolerance); + Assert.IsInstanceOfType>(diff); + } + + // Storage-type genericity for subtraction. + + [TestMethod] + public void Mass_Minus_Mass_With_Float_Storage() + { + Mass a = Mass.FromKilogram(1.0f); + Mass b = Mass.FromKilogram(4.0f); + Mass diff = a - b; + Assert.AreEqual(3.0f, diff.Value, 1e-6f); + } + + [TestMethod] + public void Mass_Minus_Mass_With_Decimal_Storage() + { + Mass a = Mass.FromKilogram(1m); + Mass b = Mass.FromKilogram(4m); + Mass diff = a - b; + Assert.AreEqual(3m, diff.Value); + } + + // Vector0Guards.EnsureNonNegative directly (sanity check on the helper). + + [TestMethod] + public void Vector0Guards_Allows_Zero_And_Positive() + { + Assert.AreEqual(0.0, Vector0Guards.EnsureNonNegative(0.0, "v")); + Assert.AreEqual(3.5, Vector0Guards.EnsureNonNegative(3.5, "v")); + } + + [TestMethod] + public void Vector0Guards_Throws_On_Negative_With_ParamName() + { + ArgumentException ex = Assert.ThrowsExactly( + () => Vector0Guards.EnsureNonNegative(-1.0, "myParam")); + Assert.AreEqual("myParam", ex.ParamName); + } + + // =========================================================== #51: Strict-positive overloads + + // Wavelength, Period, HalfLife declare physicalConstraints.minExclusive: "0" in + // dimensions.json; their generated From{Unit} factories use Vector0Guards.EnsurePositive + // instead of EnsureNonNegative, rejecting zero as well as negative values. + + [TestMethod] + public void Wavelength_FromMeters_Zero_Throws() + => _ = Assert.ThrowsExactly(() => Wavelength.FromMeter(0.0)); + + [TestMethod] + public void Wavelength_FromMeters_Negative_Throws() + => _ = Assert.ThrowsExactly(() => Wavelength.FromMeter(-1e-9)); + + [TestMethod] + public void Wavelength_FromMeters_Positive_Succeeds() + { + Wavelength w = Wavelength.FromMeter(550e-9); + Assert.AreEqual(550e-9, w.Value, 1e-15); + } + + [TestMethod] + public void Wavelength_FromNanometers_Zero_Throws() + => _ = Assert.ThrowsExactly(() => Wavelength.FromNanometer(0.0)); + + [TestMethod] + public void Period_FromSeconds_Zero_Throws() + => _ = Assert.ThrowsExactly(() => Period.FromSecond(0.0)); + + [TestMethod] + public void Period_FromSeconds_Positive_Succeeds() + { + Period p = Period.FromSecond(0.001); + Assert.AreEqual(0.001, p.Value, Tolerance); + } + + [TestMethod] + public void HalfLife_FromSeconds_Zero_Throws() + => _ = Assert.ThrowsExactly(() => HalfLife.FromSecond(0.0)); + + // The base type (Length, Duration) has no minExclusive, so zero is still allowed — + // only the constrained overloads reject it. + + [TestMethod] + public void Length_FromMeters_Zero_Allowed() + { + Length l = Length.FromMeter(0.0); + Assert.AreEqual(0.0, l.Value, Tolerance); + } + + [TestMethod] + public void Duration_FromSeconds_Zero_Allowed() + { + Duration d = Duration.FromSecond(0.0); + Assert.AreEqual(0.0, d.Value, Tolerance); + } + + // Other Length / Duration overloads without the constraint also still allow zero. + + [TestMethod] + public void Distance_FromMeters_Zero_Allowed() + { + Distance d = Distance.FromMeter(0.0); + Assert.AreEqual(0.0, d.Value, Tolerance); + } + + [TestMethod] + public void Latency_FromSeconds_Zero_Allowed() + { + // Latency has no minExclusive — a zero-latency response is meaningful. + Latency l = Latency.FromSecond(0.0); + Assert.AreEqual(0.0, l.Value, Tolerance); + } + + // Vector0Guards.EnsurePositive directly. + + [TestMethod] + public void Vector0Guards_EnsurePositive_Allows_Positive() + { + Assert.AreEqual(3.5, Vector0Guards.EnsurePositive(3.5, "v")); + } + + [TestMethod] + public void Vector0Guards_EnsurePositive_Throws_On_Zero_With_ParamName() + { + ArgumentException ex = Assert.ThrowsExactly( + () => Vector0Guards.EnsurePositive(0.0, "myParam")); + Assert.AreEqual("myParam", ex.ParamName); + } + + [TestMethod] + public void Vector0Guards_EnsurePositive_Throws_On_Negative_With_ParamName() + { + ArgumentException ex = Assert.ThrowsExactly( + () => Vector0Guards.EnsurePositive(-1.0, "myParam")); + Assert.AreEqual("myParam", ex.ParamName); + } +} diff --git a/Semantics.Test/Quantities/VectorQuantityTests.cs b/Semantics.Test/Quantities/VectorQuantityTests.cs new file mode 100644 index 0000000..a8d3087 --- /dev/null +++ b/Semantics.Test/Quantities/VectorQuantityTests.cs @@ -0,0 +1,208 @@ +// Copyright (c) ktsu.dev +// All rights reserved. +// Licensed under the MIT license. + +namespace ktsu.Semantics.Test.Quantities; + +using ktsu.Semantics.Quantities; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +/// +/// Covers .. contracts: +/// magnitude extraction, typed dot/cross products, vector arithmetic, and V0 invariants. +/// Issue #54. +/// +[TestClass] +public sealed class VectorQuantityTests +{ + private const double Tolerance = 1e-10; + + // -------------------------------------------------------------- Magnitude + + [TestMethod] + public void Velocity3D_Magnitude_Of_3_4_0_Is_Speed_5() + { + Velocity3D v = new() { X = 3.0, Y = 4.0, Z = 0.0 }; + Speed s = v.Magnitude(); + Assert.AreEqual(5.0, s.Value, Tolerance); + } + + [TestMethod] + public void Force3D_Magnitude_Is_Always_NonNegative_Even_With_Negative_Components() + { + Force3D f = new() { X = -3.0, Y = -4.0, Z = 0.0 }; + ForceMagnitude m = f.Magnitude(); + Assert.AreEqual(5.0, m.Value, Tolerance); + } + + [TestMethod] + public void Velocity3D_Magnitude_Returns_Speed_Type_Statically() + { + Velocity3D v = new() { X = 1.0, Y = 0.0, Z = 0.0 }; + Speed s = v.Magnitude(); + Assert.IsInstanceOfType>(s); + } + + [TestMethod] + public void Velocity3D_Magnitude_Of_Zero_Vector_Is_Zero() + { + Velocity3D zero = Velocity3D.Zero; + Speed s = zero.Magnitude(); + Assert.AreEqual(0.0, s.Value, Tolerance); + } + + // ------------------------------------------------------ Typed dot product + + [TestMethod] + public void Force3D_Dot_Displacement3D_Returns_Energy_Aligned() + { + Force3D f = new() { X = 10.0, Y = 0.0, Z = 0.0 }; + Displacement3D r = new() { X = 2.0, Y = 0.0, Z = 0.0 }; + Energy work = f.Dot(r); + Assert.AreEqual(20.0, work.Value, Tolerance); + } + + [TestMethod] + public void Force3D_Dot_Displacement3D_Is_Zero_For_Perpendicular() + { + Force3D f = new() { X = 10.0, Y = 0.0, Z = 0.0 }; + Displacement3D r = new() { X = 0.0, Y = 5.0, Z = 0.0 }; + Energy work = f.Dot(r); + Assert.AreEqual(0.0, work.Value, Tolerance); + } + + // ---------------------------------------------------- Typed cross product + + [TestMethod] + public void Force3D_Cross_Displacement3D_Returns_Torque3D() + { + Force3D f = new() { X = 0.0, Y = 10.0, Z = 0.0 }; + Displacement3D r = new() { X = 0.5, Y = 0.0, Z = 0.0 }; + Torque3D t = f.Cross(r); + // (Y*rZ - Z*rY, Z*rX - X*rZ, X*rY - Y*rX) = (0, 0, -5) + Assert.AreEqual(0.0, t.X, Tolerance); + Assert.AreEqual(0.0, t.Y, Tolerance); + Assert.AreEqual(-5.0, t.Z, Tolerance); + } + + [TestMethod] + public void Force3D_Cross_Self_Is_Zero_Vector() + { + Force3D f = new() { X = 1.0, Y = 2.0, Z = 3.0 }; + // Same-dimension structural cross returns Force3D (the dimension itself, not a typed dimensional product). + Force3D c = f.Cross(f); + Assert.AreEqual(0.0, c.X, Tolerance); + Assert.AreEqual(0.0, c.Y, Tolerance); + Assert.AreEqual(0.0, c.Z, Tolerance); + } + + // --------------------------------------------- Same-dimension dot product + + [TestMethod] + public void Velocity3D_Dot_Velocity3D_Returns_Raw_Storage_Scalar() + { + // Same-dimension Dot is structural and returns the raw storage type; it isn't + // a typed dimensional product (no "Speed²" exists in the type system). + Velocity3D a = new() { X = 1.0, Y = 2.0, Z = 3.0 }; + Velocity3D b = new() { X = 4.0, Y = 5.0, Z = 6.0 }; + double dot = a.Dot(b); + Assert.AreEqual(32.0, dot, Tolerance); + } + + // ------------------------------------------------ Vector form arithmetic + + [TestMethod] + public void Force3D_Plus_Force3D_Stays_Force3D_Componentwise() + { + Force3D a = new() { X = 1.0, Y = 2.0, Z = 3.0 }; + Force3D b = new() { X = 4.0, Y = 5.0, Z = 6.0 }; + Force3D sum = a + b; + Assert.AreEqual(5.0, sum.X, Tolerance); + Assert.AreEqual(7.0, sum.Y, Tolerance); + Assert.AreEqual(9.0, sum.Z, Tolerance); + } + + [TestMethod] + public void Force3D_Minus_Force3D_Componentwise() + { + Force3D a = new() { X = 5.0, Y = 7.0, Z = 9.0 }; + Force3D b = new() { X = 1.0, Y = 2.0, Z = 3.0 }; + Force3D diff = a - b; + Assert.AreEqual(4.0, diff.X, Tolerance); + Assert.AreEqual(5.0, diff.Y, Tolerance); + Assert.AreEqual(6.0, diff.Z, Tolerance); + } + + [TestMethod] + public void Force3D_Negation_Inverts_Each_Component() + { + Force3D f = new() { X = 1.0, Y = -2.0, Z = 3.0 }; + Force3D n = -f; + Assert.AreEqual(-1.0, n.X, Tolerance); + Assert.AreEqual(2.0, n.Y, Tolerance); + Assert.AreEqual(-3.0, n.Z, Tolerance); + } + + // ------------------------------------------------------------- V0 + V0 + + [TestMethod] + public void Mass_Plus_Mass_Returns_Mass() + { + Mass a = Mass.FromKilogram(3.0); + Mass b = Mass.FromKilogram(5.0); + Mass sum = a + b; + Assert.AreEqual(8.0, sum.Value, Tolerance); + Assert.IsInstanceOfType>(sum); + } + + [TestMethod] + public void Speed_Plus_Speed_Returns_Speed() + { + Speed a = Speed.FromMeterPerSecond(3.0); + Speed b = Speed.FromMeterPerSecond(5.0); + Speed sum = a + b; + Assert.AreEqual(8.0, sum.Value, Tolerance); + } + + // ------------------------------------------------------------- V0 - V0 + // #52: V0 - V0 returns the same V0 of T.Abs(a - b). The generated derived operator + // wins overload resolution over PhysicalQuantity's plain (signable) subtraction. + + [TestMethod] + public void Mass_Minus_Mass_Returns_Absolute_Difference() + { + Mass a = Mass.FromKilogram(3.0); + Mass b = Mass.FromKilogram(5.0); + Mass diff = a - b; + Assert.AreEqual(2.0, diff.Value, Tolerance); + } + + // ---------------------------------------------------- V0 non-negativity + // #50: factories on Vector0 quantities reject negative inputs with ArgumentException + // via Vector0Guards.EnsureNonNegative (the guard runs after unit conversion). + + [TestMethod] + public void Speed_From_Negative_Throws() + { + _ = Assert.ThrowsExactly( + () => Speed.FromMeterPerSecond(-1.0)); + } + + [TestMethod] + public void Mass_From_Negative_Throws() + { + _ = Assert.ThrowsExactly( + () => Mass.FromKilogram(-1.0)); + } + + // -------------------------------------------------- Magnitude on V1 + // Velocity1D.Magnitude() should return Speed of T.Abs(value). + + [TestMethod] + public void Velocity1D_Magnitude_Of_Negative_Is_Positive_Speed() + { + Velocity1D v = Velocity1D.FromMeterPerSecond(-3.5); + Speed s = v.Magnitude(); + Assert.AreEqual(3.5, s.Value, Tolerance); + } +} diff --git a/Semantics.Test/ReflectionCoefficientTests.cs b/Semantics.Test/ReflectionCoefficientTests.cs deleted file mode 100644 index 3a390e0..0000000 --- a/Semantics.Test/ReflectionCoefficientTests.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public sealed class ReflectionCoefficientTests -{ - private const double Tolerance = 1e-10; - - [TestMethod] - public void FromImpedances_NormalIncidence_ShouldMatchFormula() - { - AcousticImpedance z1 = AcousticImpedance.FromRayls(415.0); - AcousticImpedance z2 = AcousticImpedance.FromRayls(1000.0); - - ReflectionCoefficient r = ReflectionCoefficient.FromImpedances(z1, z2); - double expected = (z2.Value - z1.Value) / (z2.Value + z1.Value); - Assert.AreEqual(expected, r.Value, Tolerance); - } - - [TestMethod] - public void TransmissionCoefficient_ShouldBeOneMinusReflection() - { - ReflectionCoefficient r = ReflectionCoefficient.FromCoefficient(0.3); - Assert.AreEqual(0.7, r.TransmissionCoefficient(), Tolerance); - } - - [TestMethod] - public void FromAbsorptionCoefficient_And_ToAbsorptionCoefficient_RoundTrip() - { - SoundAbsorption alpha = SoundAbsorption.Create(0.25); - ReflectionCoefficient r = ReflectionCoefficient.FromAbsorptionCoefficient(alpha); - SoundAbsorption back = r.ToAbsorptionCoefficient(); - - Assert.AreEqual(0.75, r.Value, Tolerance); - Assert.AreEqual(alpha.Value, back.Value, Tolerance); - } - - [TestMethod] - public void AtObliqueIncidence_ShouldReduceReflectionComparedToNormal_ForSmallAngles() - { - double angleRad = Math.PI / 6.0; // 30 degrees - double ratio = 1000.0 / 415.0; // Z2/Z1 - - ReflectionCoefficient rOblique = ReflectionCoefficient.AtObliqueIncidence(angleRad, ratio); - double rNormal = (ratio - 1.0) / (ratio + 1.0); - - Assert.IsLessThan(rNormal, rOblique.Value); - } -} - diff --git a/Semantics.Test/SemanticPathTests.cs b/Semantics.Test/SemanticPathTests.cs index 60be882..64b756b 100644 --- a/Semantics.Test/SemanticPathTests.cs +++ b/Semantics.Test/SemanticPathTests.cs @@ -4,9 +4,9 @@ namespace ktsu.Semantics.Test; +using ktsu.Semantics.Paths; using System; using System.Linq; -using ktsu.Semantics.Paths; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] diff --git a/Semantics.Test/SemanticQuantityTests.cs b/Semantics.Test/SemanticQuantityTests.cs index 92fb5fa..8b97908 100644 --- a/Semantics.Test/SemanticQuantityTests.cs +++ b/Semantics.Test/SemanticQuantityTests.cs @@ -4,6 +4,7 @@ namespace ktsu.Semantics.Test; +using ktsu.Semantics.Quantities; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] diff --git a/Semantics.Test/SemanticStringTests.cs b/Semantics.Test/SemanticStringTests.cs index 4bbc07e..c87f088 100644 --- a/Semantics.Test/SemanticStringTests.cs +++ b/Semantics.Test/SemanticStringTests.cs @@ -4,11 +4,11 @@ namespace ktsu.Semantics.Test; +using ktsu.Semantics.Strings; using System.Collections; using System.Globalization; using System.Linq; using System.Text; -using ktsu.Semantics.Strings; using Microsoft.VisualStudio.TestTools.UnitTesting; public record MySemanticString : SemanticString { } diff --git a/Semantics.Test/Semantics.Test.csproj b/Semantics.Test/Semantics.Test.csproj index 2685fb4..174013d 100644 --- a/Semantics.Test/Semantics.Test.csproj +++ b/Semantics.Test/Semantics.Test.csproj @@ -1,5 +1,5 @@ - - + + diff --git a/Semantics.Test/SoundSpeedTests.cs b/Semantics.Test/SoundSpeedTests.cs deleted file mode 100644 index 20f00f0..0000000 --- a/Semantics.Test/SoundSpeedTests.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public sealed class SoundSpeedTests -{ - private const double Tolerance = 1e-10; - - [TestMethod] - public void FromFeetPerSecond_ShouldConvertToMetersPerSecond() - { - // 1 ft/s == 0.3048 m/s - SoundSpeed fromFps = SoundSpeed.FromFeetPerSecond(1.0); - Assert.AreEqual(0.3048, fromFps.Value, Tolerance); - } - - [TestMethod] - public void Multiply_Wavelength_And_Frequency_ShouldYieldSpeed() - { - Wavelength wavelength = Wavelength.Create(0.343); // 343 m/s at 1kHz - Frequency frequency = Frequency.Create(1000.0); - - SoundSpeed speed1 = SoundSpeed.Multiply(wavelength, frequency); - SoundSpeed speed2 = wavelength * frequency; // operator path - SoundSpeed speed3 = frequency * wavelength; // commutative operator - - Assert.AreEqual(343.0, speed1.Value, 1e-9); - Assert.AreEqual(speed1.Value, speed2.Value, Tolerance); - Assert.AreEqual(speed1.Value, speed3.Value, Tolerance); - } -} - diff --git a/Semantics.Test/TextValidationAttributesTests.cs b/Semantics.Test/TextValidationAttributesTests.cs index 1dc4678..131a66f 100644 --- a/Semantics.Test/TextValidationAttributesTests.cs +++ b/Semantics.Test/TextValidationAttributesTests.cs @@ -40,18 +40,42 @@ public void StartsWithAttribute_InvalidString_ShouldThrow() Assert.ThrowsExactly(() => SemanticString.Create("invalidmiddleend")); } + [TestMethod] + public void EndsWithAttribute_ValidString_ShouldPass() + { + TestValidatedString validString = SemanticString.Create("testmiddleend"); + Assert.IsNotNull(validString); + Assert.AreEqual("testmiddleend", validString.WeakString); + } + [TestMethod] public void EndsWithAttribute_InvalidString_ShouldThrow() { Assert.ThrowsExactly(() => SemanticString.Create("testmiddleinvalid")); } + [TestMethod] + public void ContainsAttribute_ValidString_ShouldPass() + { + TestValidatedString validString = SemanticString.Create("testmiddleend"); + Assert.IsNotNull(validString); + Assert.AreEqual("testmiddleend", validString.WeakString); + } + [TestMethod] public void ContainsAttribute_InvalidString_ShouldThrow() { Assert.ThrowsExactly(() => SemanticString.Create("testinvalidend")); } + [TestMethod] + public void RegexMatchAttribute_ValidString_ShouldPass() + { + TestValidatedString validString = SemanticString.Create("testmiddleend"); + Assert.IsNotNull(validString); + Assert.AreEqual("testmiddleend", validString.WeakString); + } + [TestMethod] public void RegexMatchAttribute_InvalidString_ShouldThrow() { diff --git a/Semantics.Test/ThermalAcousticQuantitiesTests.cs b/Semantics.Test/ThermalAcousticQuantitiesTests.cs deleted file mode 100644 index 22aa406..0000000 --- a/Semantics.Test/ThermalAcousticQuantitiesTests.cs +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public static class ThermalAcousticQuantitiesTests -{ - [TestClass] - public class ThermalQuantitiesTests - { - [TestMethod] - public void Temperature_BasicOperations_ShouldWork() - { - // Test temperature creation and conversions - Temperature tempCelsius = Temperature.Create(25.0 + 273.15); // Celsius to Kelvin - Temperature tempKelvin = Temperature.Create(298.15); - Temperature tempFahrenheit = Temperature.Create(((77.0 - 32) * 5 / 9) + 273.15); // Fahrenheit to Kelvin - - // Test conversions - Assert.AreEqual(298.15, tempCelsius.Value, 1e-10); - Assert.AreEqual(298.15, tempKelvin.Value, 1e-10); - Assert.AreEqual(298.15, tempFahrenheit.Value, 1e-10); - } - - [TestMethod] - public void Temperature_ArithmeticOperations_ShouldWork() - { - Temperature temp1 = Temperature.Create(283.15); // 10°C - Temperature temp2 = Temperature.Create(278.15); // 5°C - - Temperature sum = temp1 + temp2; - Temperature difference = temp1 - temp2; - Temperature scaled = temp1 * 2.0; - - Assert.AreEqual(561.3, sum.Value, 1e-10); - Assert.AreEqual(5.0, difference.Value, 1e-10); - Assert.AreEqual(566.3, scaled.Value, 1e-10); - } - - [TestMethod] - public void ThermalConductivity_BasicOperations_ShouldWork() - { - ThermalConductivity conductivity1 = ThermalConductivity.Create(200.0); // Copper - ThermalConductivity conductivity2 = ThermalConductivity.Create(0.5); // Insulator - - // Test arithmetic operations - ThermalConductivity sum = conductivity1 + conductivity2; - double ratio = conductivity1.Value / conductivity2.Value; - - Assert.AreEqual(200.5, sum.Value, 1e-10); - Assert.AreEqual(400.0, ratio, 1e-10); - } - - [TestMethod] - public void SpecificHeat_BasicOperations_ShouldWork() - { - SpecificHeat specificHeat = SpecificHeat.Create(4184.0); // Water - - // Test scaling - SpecificHeat doubledHeat = specificHeat * 2.0; - Assert.AreEqual(8368.0, doubledHeat.Value, 1e-10); - } - - [TestMethod] - public void ThermalResistance_BasicOperations_ShouldWork() - { - ThermalResistance resistance1 = ThermalResistance.Create(0.1); - ThermalResistance resistance2 = ThermalResistance.Create(0.2); - - // Series thermal resistance - ThermalResistance seriesResistance = resistance1 + resistance2; - Assert.AreEqual(0.3, seriesResistance.Value, 1e-10); - - // Parallel thermal resistance - double parallelResistance = 1.0 / ((1.0 / resistance1.Value) + (1.0 / resistance2.Value)); - Assert.AreEqual(0.0666667, parallelResistance, 1e-6); - } - - [TestMethod] - public void HeatFlux_BasicOperations_ShouldWork() - { - Power heatFlux1 = Power.Create(1000.0); - Power heatFlux2 = Power.Create(500.0); - - Power sum = heatFlux1 + heatFlux2; - Power difference = heatFlux1 - heatFlux2; - - Assert.AreEqual(1500.0, sum.Value, 1e-10); - Assert.AreEqual(500.0, difference.Value, 1e-10); - } - - [TestMethod] - public void ThermalExpansion_BasicOperations_ShouldWork() - { - ThermalExpansion expansion = ThermalExpansion.Create(12e-6); // Steel - - // Test temperature-dependent expansion - Temperature tempChange = Temperature.Create(100.0); - double strain = expansion.Value * tempChange.Value; - - Assert.AreEqual(0.0012, strain, 1e-10); - } - } - - [TestClass] - public class AcousticQuantitiesTests - { - [TestMethod] - public void SoundPressure_BasicOperations_ShouldWork() - { - SoundPressure pressure1 = SoundPressure.Create(20.0); // About 120 dB SPL - SoundPressure pressure2 = SoundPressure.Create(2.0); // About 100 dB SPL - - SoundPressure sum = pressure1 + pressure2; - double ratio = pressure1.Value / pressure2.Value; - - Assert.AreEqual(22.0, sum.Value, 1e-10); - Assert.AreEqual(10.0, ratio, 1e-10); - } - - [TestMethod] - public void SoundIntensity_BasicOperations_ShouldWork() - { - SoundIntensity intensity = SoundIntensity.Create(1e-6); // Moderate sound - - // Test scaling - SoundIntensity doubled = intensity * 2.0; - Assert.AreEqual(2e-6, doubled.Value, 1e-16); - } - - [TestMethod] - public void Frequency_BasicOperations_ShouldWork() - { - Frequency frequency1 = Frequency.Create(440.0); // A4 note - Frequency frequency2 = Frequency.Create(1000.0); - - // Test unit conversions - Assert.AreEqual(440.0, frequency1.Value, 1e-10); - Assert.AreEqual(1000.0, frequency2.Value, 1e-10); - } - - [TestMethod] - public void WaveLength_FrequencyRelationship_ShouldWork() - { - Frequency frequency = Frequency.Create(1000.0); - double soundSpeed = 343.0; // m/s in air at room temperature - double expectedWaveLength = soundSpeed / frequency.Value; - - Length waveLength = Length.Create(expectedWaveLength); - Assert.AreEqual(0.343, waveLength.Value, 1e-10); - } - - [TestMethod] - public void AcousticImpedance_BasicOperations_ShouldWork() - { - AcousticImpedance impedance = AcousticImpedance.Create(415.0); // Air at room temperature - - // Test scaling - AcousticImpedance scaled = impedance * 2.0; - Assert.AreEqual(830.0, scaled.Value, 1e-10); - } - - [TestMethod] - public void SoundLevel_DecibelCalculations_ShouldWork() - { - // Test sound pressure level calculations - double referencePressure = 20e-6; // 20 μPa reference - SoundPressure pressure = SoundPressure.Create(2.0); - - double spl = 20 * Math.Log10(pressure.Value / referencePressure); - Assert.AreEqual(100.0, spl, 1e-10); // 100 dB SPL - } - - [TestMethod] - public void AcousticPower_BasicOperations_ShouldWork() - { - Power power1 = Power.Create(0.1); - Power power2 = Power.Create(0.05); - - Power sum = power1 + power2; - double ratio = power1.Value / power2.Value; - - Assert.AreEqual(0.15, sum.Value, 1e-10); - Assert.AreEqual(2.0, ratio, 1e-10); - } - - [TestMethod] - public void ResonantFrequency_BasicCalculations_ShouldWork() - { - // Simple harmonic oscillator: f = 1/(2π) * sqrt(k/m) - double springConstant = 1000.0; // N/m - double mass = 1.0; // kg - double expectedFreq = 1.0 / (2.0 * Math.PI) * Math.Sqrt(springConstant / mass); - - Frequency frequency = Frequency.Create(expectedFreq); - Assert.AreEqual(5.032, frequency.Value, 1e-3); - } - } - - [TestClass] - public class ThermalAcousticInteractionTests - { - [TestMethod] - public void TemperatureEffect_OnSoundSpeed_ShouldWork() - { - // Sound speed in air: v = sqrt(γRT/M) - Temperature temp1 = Temperature.Create(293.15); // 20°C - Temperature temp2 = Temperature.Create(273.15); // 0°C - - // Approximate sound speed at 20°C and 0°C - double soundSpeed20 = 343.0; // m/s - double soundSpeed0 = 331.0; // m/s - - double speedRatio = soundSpeed20 / soundSpeed0; - double tempRatio = Math.Sqrt(temp1.Value / temp2.Value); - - Assert.AreEqual(tempRatio, speedRatio, 1e-2); - } - - [TestMethod] - public void ThermalNoise_BasicCalculations_ShouldWork() - { - // Johnson-Nyquist thermal noise: V²=4kTRΔf - Temperature temperature = Temperature.Create(300.0); - double resistance = 1000.0; // Ω - double bandwidth = 1000.0; // Hz - double boltzmannConstant = 1.38e-23; // J/K - - double thermalNoise = 4 * boltzmannConstant * temperature.Value * resistance * bandwidth; - Assert.AreEqual(1.656e-14, thermalNoise, 1e-17); - } - - [TestMethod] - public void ThermoacousticEffect_QualitativeTest_ShouldWork() - { - // Test that we can represent thermoacoustic quantities - Temperature tempGradient = Temperature.Create(100.0); - Power acousticPower = Power.Create(0.01); - Frequency frequency = Frequency.Create(1000.0); - - // These should all be valid quantities - Assert.IsGreaterThan(0, tempGradient.Value); - Assert.IsGreaterThan(0, acousticPower.Value); - Assert.IsGreaterThan(0, frequency.Value); - } - } - - [TestClass] - public class EdgeCasesAndErrorConditionsTests - { - [TestMethod] - public void Temperature_AbsoluteZero_ShouldWork() - { - Temperature absoluteZero = Temperature.Create(0.0); - Assert.AreEqual(0.0, absoluteZero.Value); - } - - [TestMethod] - public void Temperature_BelowAbsoluteZero_ShouldThrow() - { - Assert.ThrowsExactly(() => Temperature.Create(-1.0)); - } - - [TestMethod] - public void Frequency_Zero_ShouldWork() - { - Frequency zeroFreq = Frequency.Create(0.0); - Assert.AreEqual(0.0, zeroFreq.Value); - } - - [TestMethod] - public void Frequency_Negative_ShouldThrow() - { - Assert.ThrowsExactly(() => Frequency.Create(-1.0)); - } - - [TestMethod] - public void ThermalQuantities_VeryLargeValues_ShouldWork() - { - Temperature veryHot = Temperature.Create(1e9); // 1 billion K - ThermalConductivity highConductivity = ThermalConductivity.Create(1e6); - - Assert.AreEqual(1e9, veryHot.Value); - Assert.AreEqual(1e6, highConductivity.Value); - } - - [TestMethod] - public void AcousticQuantities_VerySmallValues_ShouldWork() - { - SoundPressure veryQuiet = SoundPressure.Create(1e-12); // Extremely quiet - Assert.AreEqual(1e-12, veryQuiet.Value); - } - } -} diff --git a/Semantics.Test/UnitExtensionsAndExceptionTests.cs b/Semantics.Test/UnitExtensionsAndExceptionTests.cs deleted file mode 100644 index 4b73df4..0000000 --- a/Semantics.Test/UnitExtensionsAndExceptionTests.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) ktsu.dev -// All rights reserved. -// Licensed under the MIT license. - -namespace ktsu.Semantics.Test; - -using ktsu.Semantics; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[TestClass] -public sealed class UnitExtensionsAndExceptionTests -{ - [TestMethod] - public void UnitExtensions_BasicChecks() - { - Assert.IsTrue(Units.Meter.IsBaseUnit()); - Assert.IsTrue(Units.Meter.IsSI()); - Assert.IsTrue(Units.Kilometer.IsSI()); - Assert.IsTrue(Units.Centimeter.IsMetric()); - Assert.IsTrue(Units.Foot.IsImperial()); - Assert.IsFalse(Units.NauticalMile.IsImperial()); - } - - [TestMethod] - public void UnitConversionException_MessageAndProperties() - { - UnitConversionException ex = new(Units.Meter, Units.Second, "different dimensions"); - StringAssert.Contains(ex.Message, Units.Meter.Symbol); - StringAssert.Contains(ex.Message, Units.Second.Symbol); - Assert.AreEqual(Units.Meter, ex.SourceUnit); - Assert.AreEqual(Units.Second, ex.TargetUnit); - } -} - diff --git a/Semantics.Test/ValidationStrategyTests.cs b/Semantics.Test/ValidationStrategyTests.cs index 27a00f6..5ccf38d 100644 --- a/Semantics.Test/ValidationStrategyTests.cs +++ b/Semantics.Test/ValidationStrategyTests.cs @@ -4,8 +4,8 @@ namespace ktsu.Semantics.Test; -using System; using ktsu.Semantics.Strings; +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] diff --git a/Semantics.sln b/Semantics.sln index 37972fc..92f0451 100644 --- a/Semantics.sln +++ b/Semantics.sln @@ -1,38 +1,130 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 +VisualStudioVersion = 17.5.2.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semantics.Strings", "Semantics.Strings\Semantics.Strings.csproj", "{5E6F3A4B-B2C3-4B3C-9D4E-AD2C8EB3F88E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semantics.Strings", "Semantics.Strings\Semantics.Strings.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semantics.Paths", "Semantics.Paths\Semantics.Paths.csproj", "{6F7A4B5C-C3D4-4C4D-AE5F-BE3D9FC4A99F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semantics.Paths", "Semantics.Paths\Semantics.Paths.csproj", "{B2C3D4E5-F6A7-8901-BCDE-F12345678901}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semantics.Quantities", "Semantics.Quantities\Semantics.Quantities.csproj", "{7A8B5C6D-D4E5-4D5E-BF6A-CF4EAFA5BAA0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semantics.Quantities", "Semantics.Quantities\Semantics.Quantities.csproj", "{C3D4E5F6-A7B8-9012-CDEF-123456789012}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semantics.Test", "Semantics.Test\Semantics.Test.csproj", "{E8989BF7-A154-4932-8BC6-E3B39A15A4A5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semantics.SourceGenerators", "Semantics.SourceGenerators\Semantics.SourceGenerators.csproj", "{115400CC-A193-77BB-A534-FCD0EC7445CB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semantics.Quantities.Double", "Semantics.Quantities.Double\Semantics.Quantities.Double.csproj", "{EF36D3E1-3AAB-4D78-B1C1-212018A42394}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semantics.Quantities.Float", "Semantics.Quantities.Float\Semantics.Quantities.Float.csproj", "{23B201D2-1C39-4637-B514-6EE741566442}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semantics.Quantities.Decimal", "Semantics.Quantities.Decimal\Semantics.Quantities.Decimal.csproj", "{3F4C7182-8C92-48D6-A1F0-DA91EE4DF2D9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5E6F3A4B-B2C3-4B3C-9D4E-AD2C8EB3F88E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5E6F3A4B-B2C3-4B3C-9D4E-AD2C8EB3F88E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5E6F3A4B-B2C3-4B3C-9D4E-AD2C8EB3F88E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5E6F3A4B-B2C3-4B3C-9D4E-AD2C8EB3F88E}.Release|Any CPU.Build.0 = Release|Any CPU - {6F7A4B5C-C3D4-4C4D-AE5F-BE3D9FC4A99F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6F7A4B5C-C3D4-4C4D-AE5F-BE3D9FC4A99F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6F7A4B5C-C3D4-4C4D-AE5F-BE3D9FC4A99F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6F7A4B5C-C3D4-4C4D-AE5F-BE3D9FC4A99F}.Release|Any CPU.Build.0 = Release|Any CPU - {7A8B5C6D-D4E5-4D5E-BF6A-CF4EAFA5BAA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7A8B5C6D-D4E5-4D5E-BF6A-CF4EAFA5BAA0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7A8B5C6D-D4E5-4D5E-BF6A-CF4EAFA5BAA0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7A8B5C6D-D4E5-4D5E-BF6A-CF4EAFA5BAA0}.Release|Any CPU.Build.0 = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.ActiveCfg = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.Build.0 = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.ActiveCfg = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x86.Build.0 = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.ActiveCfg = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.Build.0 = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.ActiveCfg = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.Build.0 = Release|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|x64.ActiveCfg = Debug|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|x64.Build.0 = Debug|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|x86.ActiveCfg = Debug|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|x86.Build.0 = Debug|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.Build.0 = Release|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|x64.ActiveCfg = Release|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|x64.Build.0 = Release|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|x86.ActiveCfg = Release|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|x86.Build.0 = Release|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|x64.ActiveCfg = Debug|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|x64.Build.0 = Debug|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|x86.ActiveCfg = Debug|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|x86.Build.0 = Debug|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.Build.0 = Release|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|x64.ActiveCfg = Release|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|x64.Build.0 = Release|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|x86.ActiveCfg = Release|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|x86.Build.0 = Release|Any CPU {E8989BF7-A154-4932-8BC6-E3B39A15A4A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E8989BF7-A154-4932-8BC6-E3B39A15A4A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8989BF7-A154-4932-8BC6-E3B39A15A4A5}.Debug|x64.ActiveCfg = Debug|Any CPU + {E8989BF7-A154-4932-8BC6-E3B39A15A4A5}.Debug|x64.Build.0 = Debug|Any CPU + {E8989BF7-A154-4932-8BC6-E3B39A15A4A5}.Debug|x86.ActiveCfg = Debug|Any CPU + {E8989BF7-A154-4932-8BC6-E3B39A15A4A5}.Debug|x86.Build.0 = Debug|Any CPU {E8989BF7-A154-4932-8BC6-E3B39A15A4A5}.Release|Any CPU.ActiveCfg = Release|Any CPU {E8989BF7-A154-4932-8BC6-E3B39A15A4A5}.Release|Any CPU.Build.0 = Release|Any CPU + {E8989BF7-A154-4932-8BC6-E3B39A15A4A5}.Release|x64.ActiveCfg = Release|Any CPU + {E8989BF7-A154-4932-8BC6-E3B39A15A4A5}.Release|x64.Build.0 = Release|Any CPU + {E8989BF7-A154-4932-8BC6-E3B39A15A4A5}.Release|x86.ActiveCfg = Release|Any CPU + {E8989BF7-A154-4932-8BC6-E3B39A15A4A5}.Release|x86.Build.0 = Release|Any CPU + {115400CC-A193-77BB-A534-FCD0EC7445CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {115400CC-A193-77BB-A534-FCD0EC7445CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {115400CC-A193-77BB-A534-FCD0EC7445CB}.Debug|x64.ActiveCfg = Debug|Any CPU + {115400CC-A193-77BB-A534-FCD0EC7445CB}.Debug|x64.Build.0 = Debug|Any CPU + {115400CC-A193-77BB-A534-FCD0EC7445CB}.Debug|x86.ActiveCfg = Debug|Any CPU + {115400CC-A193-77BB-A534-FCD0EC7445CB}.Debug|x86.Build.0 = Debug|Any CPU + {115400CC-A193-77BB-A534-FCD0EC7445CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {115400CC-A193-77BB-A534-FCD0EC7445CB}.Release|Any CPU.Build.0 = Release|Any CPU + {115400CC-A193-77BB-A534-FCD0EC7445CB}.Release|x64.ActiveCfg = Release|Any CPU + {115400CC-A193-77BB-A534-FCD0EC7445CB}.Release|x64.Build.0 = Release|Any CPU + {115400CC-A193-77BB-A534-FCD0EC7445CB}.Release|x86.ActiveCfg = Release|Any CPU + {115400CC-A193-77BB-A534-FCD0EC7445CB}.Release|x86.Build.0 = Release|Any CPU + {EF36D3E1-3AAB-4D78-B1C1-212018A42394}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF36D3E1-3AAB-4D78-B1C1-212018A42394}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF36D3E1-3AAB-4D78-B1C1-212018A42394}.Debug|x64.ActiveCfg = Debug|Any CPU + {EF36D3E1-3AAB-4D78-B1C1-212018A42394}.Debug|x64.Build.0 = Debug|Any CPU + {EF36D3E1-3AAB-4D78-B1C1-212018A42394}.Debug|x86.ActiveCfg = Debug|Any CPU + {EF36D3E1-3AAB-4D78-B1C1-212018A42394}.Debug|x86.Build.0 = Debug|Any CPU + {EF36D3E1-3AAB-4D78-B1C1-212018A42394}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF36D3E1-3AAB-4D78-B1C1-212018A42394}.Release|Any CPU.Build.0 = Release|Any CPU + {EF36D3E1-3AAB-4D78-B1C1-212018A42394}.Release|x64.ActiveCfg = Release|Any CPU + {EF36D3E1-3AAB-4D78-B1C1-212018A42394}.Release|x64.Build.0 = Release|Any CPU + {EF36D3E1-3AAB-4D78-B1C1-212018A42394}.Release|x86.ActiveCfg = Release|Any CPU + {EF36D3E1-3AAB-4D78-B1C1-212018A42394}.Release|x86.Build.0 = Release|Any CPU + {23B201D2-1C39-4637-B514-6EE741566442}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23B201D2-1C39-4637-B514-6EE741566442}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23B201D2-1C39-4637-B514-6EE741566442}.Debug|x64.ActiveCfg = Debug|Any CPU + {23B201D2-1C39-4637-B514-6EE741566442}.Debug|x64.Build.0 = Debug|Any CPU + {23B201D2-1C39-4637-B514-6EE741566442}.Debug|x86.ActiveCfg = Debug|Any CPU + {23B201D2-1C39-4637-B514-6EE741566442}.Debug|x86.Build.0 = Debug|Any CPU + {23B201D2-1C39-4637-B514-6EE741566442}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23B201D2-1C39-4637-B514-6EE741566442}.Release|Any CPU.Build.0 = Release|Any CPU + {23B201D2-1C39-4637-B514-6EE741566442}.Release|x64.ActiveCfg = Release|Any CPU + {23B201D2-1C39-4637-B514-6EE741566442}.Release|x64.Build.0 = Release|Any CPU + {23B201D2-1C39-4637-B514-6EE741566442}.Release|x86.ActiveCfg = Release|Any CPU + {23B201D2-1C39-4637-B514-6EE741566442}.Release|x86.Build.0 = Release|Any CPU + {3F4C7182-8C92-48D6-A1F0-DA91EE4DF2D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F4C7182-8C92-48D6-A1F0-DA91EE4DF2D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F4C7182-8C92-48D6-A1F0-DA91EE4DF2D9}.Debug|x64.ActiveCfg = Debug|Any CPU + {3F4C7182-8C92-48D6-A1F0-DA91EE4DF2D9}.Debug|x64.Build.0 = Debug|Any CPU + {3F4C7182-8C92-48D6-A1F0-DA91EE4DF2D9}.Debug|x86.ActiveCfg = Debug|Any CPU + {3F4C7182-8C92-48D6-A1F0-DA91EE4DF2D9}.Debug|x86.Build.0 = Debug|Any CPU + {3F4C7182-8C92-48D6-A1F0-DA91EE4DF2D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F4C7182-8C92-48D6-A1F0-DA91EE4DF2D9}.Release|Any CPU.Build.0 = Release|Any CPU + {3F4C7182-8C92-48D6-A1F0-DA91EE4DF2D9}.Release|x64.ActiveCfg = Release|Any CPU + {3F4C7182-8C92-48D6-A1F0-DA91EE4DF2D9}.Release|x64.Build.0 = Release|Any CPU + {3F4C7182-8C92-48D6-A1F0-DA91EE4DF2D9}.Release|x86.ActiveCfg = Release|Any CPU + {3F4C7182-8C92-48D6-A1F0-DA91EE4DF2D9}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/TAGS.md b/TAGS.md index c32f47c..f3a91a2 100644 --- a/TAGS.md +++ b/TAGS.md @@ -1 +1 @@ -semantic strings;type safety;validation;string wrapper;domain modeling;semantic types;path handling;strongly typed;primitive obsession;value objects;csharp;dotnet;validation attributes;filesystem;type system;physics quantities;physical constants;dimensional analysis;unit conversions;scientific computing;mechanical physics;thermal physics;optical physics;acoustic physics;chemical physics;nuclear physics;fluid dynamics;si units;imperial units;bootstrap architecture;performance optimized;enterprise ready;factory pattern;dependency injection +semantic strings;semantic quantities;type safety;validation;string wrapper;domain modeling;semantic types;path handling;strongly typed;primitive obsession;value objects;csharp;dotnet;validation attributes;filesystem;type system;physical constants;dimensional analysis;unit conversions;vector quantities;source generator;code generation;scientific computing;mechanical physics;thermal physics;optical physics;acoustic physics;chemical physics;nuclear physics;fluid dynamics;si units;imperial units;performance optimized;factory pattern;dependency injection diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 45ee878..0310671 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -80,10 +80,15 @@ public class BusinessRuleValidationStrategy : IValidationStrategy } } -// Use custom strategies with validation attributes -[ValidateWith(typeof(BusinessRuleValidationStrategy))] -[IsNotEmpty, IsEmail] // Critical validations -[IsCompanyEmail, IsInternalDomain] // Non-critical validations +// To wire a custom strategy in, register it via ValidationStrategyFactory +// at startup and reference it from your validation pipeline: +// +// ValidationStrategyFactory.Register( +// new BusinessRuleValidationStrategy()); +// +// Strategies are keyed by the semantic-string type that owns them. +[HasNonWhitespaceContent, IsEmailAddress] +[IsCompanyEmail, IsInternalDomain] // your own custom attributes public sealed record BusinessEmail : SemanticString { } ``` @@ -123,7 +128,7 @@ public sealed record ProductCode : SemanticString { } // Usage with automatic validation var factory = new SemanticStringFactory(); var validCode = factory.Create("A12345"); // ✅ Valid -// factory.Create("123ABC"); // ❌ Throws FormatException +// factory.Create("123ABC"); // ❌ Throws ArgumentException ``` ## Contract Validation and LSP Compliance @@ -153,9 +158,9 @@ public class SemanticStringValidator where T : SemanticString [Test] public void EmailAddress_ShouldSatisfySemanticStringContracts() { - var email1 = EmailAddress.FromString("user@example.com"); - var email2 = EmailAddress.FromString("admin@example.com"); - var email3 = EmailAddress.FromString("test@example.com"); + var email1 = EmailAddress.Create("user@example.com"); + var email2 = EmailAddress.Create("admin@example.com"); + var email3 = EmailAddress.Create("test@example.com"); var validator = new SemanticStringValidator(); Assert.IsTrue(validator.ValidateImplementation(email1, email2, email3)); @@ -190,12 +195,11 @@ public sealed record ExistingAbsolutePath : SemanticPath { // Require ANY validation attribute to pass [ValidateAny] -[IsEmail, IsUrl] +[IsEmailAddress, IsUri] public sealed record ContactInfo : SemanticString { } -// Custom validation strategy (shown earlier) -[ValidateWith(typeof(BusinessRuleValidationStrategy))] -[IsNotEmpty, IsEmail] +// Custom strategies plug in via ValidationStrategyFactory.Register() +[HasNonWhitespaceContent, IsEmailAddress] public sealed record StrictBusinessEmail : SemanticString { } ``` @@ -204,8 +208,8 @@ public sealed record StrictBusinessEmail : SemanticString { Specialized operations for working with file system paths: ```csharp -var from = AbsolutePath.FromString(@"C:\Projects\App"); -var to = AbsolutePath.FromString(@"C:\Projects\Lib\Utils.cs"); +var from = AbsolutePath.Create(@"C:\Projects\App"); +var to = AbsolutePath.Create(@"C:\Projects\Lib\Utils.cs"); // Create relative path between two absolute paths var relativePath = RelativePath.Make(from, to); @@ -221,7 +225,7 @@ Console.WriteLine(filePath.FileExtension); // .json Console.WriteLine(filePath.DirectoryPath); // C:\temp // Check file system properties -var absolutePath = AbsolutePath.FromString(@"C:\Projects\MyApp"); +var absolutePath = AbsolutePath.Create(@"C:\Projects\MyApp"); Console.WriteLine(absolutePath.Exists); // True/False Console.WriteLine(absolutePath.IsDirectory); // True/False ``` diff --git a/docs/architecture.md b/docs/architecture.md index f09f0c7..e2f94d4 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,6 +1,10 @@ # Architecture Guide -This document provides a detailed overview of the Semantics library architecture, focusing on the SOLID principles and DRY practices implemented throughout the codebase. +This document covers the architecture of the **semantic strings, paths, and validation** subsystems. The semantic quantities subsystem is metadata-driven and is documented separately: + +- [`docs/strategy-unified-vector-quantities.md`](strategy-unified-vector-quantities.md) — the unified `IVector0..IVector4` model. +- [`docs/physics-generator.md`](physics-generator.md) — `dimensions.json` schema and the source-generator pipeline. +- The [Semantic Quantities: Metadata-Driven Generation](#semantic-quantities-metadata-driven-generation) section below provides a short orientation and links into those documents. ## Table of Contents @@ -10,11 +14,12 @@ This document provides a detailed overview of the Semantics library architecture - [Design Patterns](#design-patterns) - [Class Hierarchy](#class-hierarchy) - [Validation System](#validation-system) +- [Semantic Quantities: Metadata-Driven Generation](#semantic-quantities-metadata-driven-generation) - [Testing Strategy](#testing-strategy) ## Overview -The Semantics library is designed around clean architecture principles, with a focus on maintainability, extensibility, and testability. The core philosophy is to provide type-safe string wrappers while maintaining excellent separation of concerns and avoiding code duplication. +The Semantics library is built around three pillars — **semantic strings**, **semantic paths**, and **semantic quantities** — sharing a single philosophy: replace primitive obsession with strongly-typed, self-validating domain models. Strings and paths use a hand-authored attribute → strategy → rule → factory pipeline (described in this document). Semantic quantities are emitted at compile time by a Roslyn incremental generator from declarative metadata (described in [Semantic Quantities: Metadata-Driven Generation](#semantic-quantities-metadata-driven-generation)). Strings and paths target `net8.0`–`net10.0` plus `netstandard2.0`/`netstandard2.1`; semantic quantities target `net8.0`–`net10.0`. ## SOLID Principles Implementation @@ -271,6 +276,121 @@ User Creates Semantic String Return Valid Object or Throw Exception ``` +## Semantic Quantities: Metadata-Driven Generation + +Unlike strings and paths — which are hand-authored — every semantic quantity type, factory, operator, and constant is emitted by a Roslyn incremental generator. The single source of truth is `Semantics.SourceGenerators/Metadata/`: + +| File | Contents | +|---|---| +| `dimensions.json` | Every physical dimension, the vector forms it supports (`Vector0`..`Vector4`), its `availableUnits`, semantic overloads (e.g. `Weight` over `ForceMagnitude`), and cross-dimensional relationships (`integrals`, `derivatives`, `dotProducts`, `crossProducts`). | +| `units.json` | Unit declarations (singular-lemma `name`, used verbatim as the `From{Name}` factory suffix) and a base-unit conversion expression. | +| `magnitudes.json` | SI magnitude prefixes for unit derivations. | +| `conversions.json` | Conversion factors between non-SI units and the SI base. | +| `domains.json` | Domain grouping for `PhysicalConstants` (e.g. `Fundamental`, `Chemistry`, `AngularMechanics`). | + +### Generator pipeline + +``` +Metadata/*.json + │ + ▼ +Semantics.SourceGenerators (Roslyn IIncrementalGenerator) + │ + ├── QuantitiesGenerator → one record per quantity (V0/V1/V2/V3/V4 + overloads) + │ + From{Unit} factory per declared unit + │ + Vector0Guards.EnsureNonNegative / EnsurePositive + │ + cross-dimensional *, /, Dot, Cross operators + ├── ConversionsGenerator → unit-to-SI conversion helpers + ├── PhysicalConstantsGenerator → PhysicalConstants..* (PreciseNumber) + │ + PhysicalConstants.Generic.*() (T.CreateChecked) + └── StorageHelpersGenerator → DivideToStorage with DivideByZeroException + │ + ▼ +Semantics.Quantities/Generated/ (committed source — diff before commit) +``` + +### Runtime contract — `IPhysicalQuantity` + +Every generated V0 / V1 quantity (and V0/V1 semantic overload) implements +`IPhysicalQuantity` through the `PhysicalQuantity` base. The contract is +deliberately slim: + +```csharp +public interface IPhysicalQuantity + : ISemanticQuantity + , IComparable> + , IEquatable> + where T : struct, INumber +{ + T Value { get; } // stored in the dimension's SI base unit + bool IsPhysicallyValid { get; } // structural: finite, non-NaN + DimensionInfo Dimension { get; } // PhysicalDimensions.X (generated singleton) +} +``` + +Semantics (locked in #59): + +1. **`Dimension`** is generated per quantity as `=> PhysicalDimensions.{Name}` so every + instance knows what it measures without reflection. +2. **`CompareTo(IPhysicalQuantity?)`** compares stored SI-base values, but throws + `ArgumentException` when the dimensions differ — quantities of different dimensions + are not ordered. +3. **`Equals(IPhysicalQuantity?)`** is total: cross-dimension comparisons return + `false` rather than throwing, because equality (unlike ordering) must be defined + for every pair. + +V2 / V3 / V4 vector types implement only their `IVectorN` interface — the +slim `IPhysicalQuantity` contract applies to scalar-storage quantities. + +### Unit conversion — typed `In(...)` + +Each V0/V1 quantity emits a dimensionally-typed `In` method: + +```csharp +// On generated Length: +public T In(ILengthUnit unit) => unit.FromBase(Value); + +// On generated Temperature: +public T In(ITemperatureUnit unit) => unit.FromBase(Value); +``` + +`ILengthUnit`, `ITemperatureUnit`, etc. are marker interfaces generated by +`DimensionsGenerator` — each declared unit implements `IUnit` plus the marker(s) for +the dimension(s) it belongs to. Cross-dimension calls fail at compile time: + +```csharp +length.In(Units.Kilogram); // ❌ compile error — Kilogram : IMassUnit, not ILengthUnit +length.In(Units.Kilometer); // ✓ +``` + +The `IUnit` interface carries `Name`, `Symbol`, `Dimension`, and the affine conversion +(`base = value × ToBaseFactor + ToBaseOffset`). `ToBase` / `FromBase` are default +interface methods, so each concrete unit only has to declare its factor and offset. +The static `Units` catalogue exposes one singleton per declared unit. + +`UnitConversionException` remains in the runtime for any future untyped-unit dispatch +path; the typed `In(I{Dim}Unit)` path is compile-time-safe and does not throw. + +### Vector-form invariants + +These are enforced structurally by the generated types and locked in by `Semantics.Test`: + +1. `V0` magnitudes are non-negative; the SI factory throws `ArgumentException` on a negative value, and `V0 - V0` returns `T.Abs(a - b)` to preserve the invariant. +2. A V0 overload can opt into a strict-positive guard with `physicalConstraints: { "minExclusive": "0" }` (used by `Wavelength`, `Period`, `HalfLife`); `EnsurePositive` then rejects zero as well. +3. Semantic overloads widen implicitly to their base, narrow explicitly (`Weight.From(forceMagnitude)`). +4. `IVectorN.Magnitude()` for N ≥ 1 returns the corresponding `IVector0`. + +### Generator diagnostics + +Metadata errors fail the build rather than silently emitting wrong code: + +- **SEM001** — a relationship references a dimension that does not exist. +- **SEM002** — schema-level metadata issue (missing `name` / `symbol`, empty `availableUnits`, duplicate type names, no vector forms declared). +- **SEM003** — a relationship's explicit `forms` list references a vector form not declared on a participating dimension. +- **SEM004** — a dimension's `availableUnits` array references a unit name that isn't declared in `units.json` (catches typos that would otherwise produce a wrong identity-conversion factory). + +For the schema, an end-to-end "add a dimension" walk-through, and the design rationale, see [`docs/physics-generator.md`](physics-generator.md) and [`docs/strategy-unified-vector-quantities.md`](strategy-unified-vector-quantities.md). + ## Testing Strategy ### Contract Testing diff --git a/docs/complete-library-guide.md b/docs/complete-library-guide.md index a39eda2..3c031fb 100644 --- a/docs/complete-library-guide.md +++ b/docs/complete-library-guide.md @@ -1,281 +1,277 @@ # Complete Semantics Library Guide -This document provides a comprehensive overview of all features and components in the ktsu.Semantics library. +ktsu.Semantics is a .NET library for replacing primitive obsession with strongly-typed, self-validating domain models. It has three pillars: -## Table of Contents +- **Semantic Strings** — type-safe string wrappers with attribute-driven validation. +- **Semantic Paths** — polymorphic file system path types with rich operations. +- **Semantic Quantities** — a metadata-generated, type-safe physics system built on a unified vector model. -- [Core Components](#core-components) -- [Semantic Strings](#semantic-strings) -- [Semantic Quantities](#semantic-quantities) -- [Path System](#path-system) -- [Validation System](#validation-system) -- [Performance Features](#performance-features) +All three share a runtime philosophy: validate at construction time, fail fast with `ArgumentException`, and never let an invalid value into the type. -## Core Components +## Document map -The Semantics library consists of six main areas: +| Topic | Doc | +|---|---| +| Architecture (strings/paths/validation) | `architecture.md` | +| Architecture (semantic quantities) | `strategy-unified-vector-quantities.md` | +| Source-generator workflow | `physics-generator.md` | +| Validation attribute reference | `validation-reference.md` | +| Advanced patterns | `advanced-usage.md` | +| Physics quick reference by dimension | `physics-domains-guide.md` | -1. **Semantic Strings** - Type-safe string wrappers with validation -2. **Physics Quantities System** - Complete physics library with 80+ quantities across 8 domains -3. **Physical Constants** - Centralized, type-safe access to fundamental and derived constants -4. **Path System** - Comprehensive file system path handling -5. **Validation System** - 50+ validation attributes across multiple categories -6. **Performance Utilities** - Optimizations for high-performance scenarios +## Semantic strings -## Semantic Strings - -Transform primitive string obsession into strongly-typed domain models: +Define a strongly-typed string by deriving from `SemanticString` and decorating with validation attributes: ```csharp -[IsEmail] +[IsEmailAddress] public sealed record EmailAddress : SemanticString { } -[HasLength(8, 50), IsNotEmpty] +[StartsWith("USER_"), HasNonWhitespaceContent] public sealed record UserId : SemanticString { } +``` -// Usage with factory pattern -var emailFactory = new SemanticStringFactory(); -var email = emailFactory.Create("user@example.com"); +Construction goes through one of: -// Compile-time safety -public void SendEmail(EmailAddress to, UserId userId) { /* ... */ } -// SendEmail(userId, email); // ❌ Won't compile! -``` +```csharp +// Direct, type-inferred +var email = EmailAddress.Create("user@example.com"); +var userId = UserId.Create("USER_12345"); -## Physics Quantities System +// From char span / array +var email2 = EmailAddress.Create("user@example.com".AsSpan()); -A comprehensive physics library with **80+ quantities** across **8 scientific domains** featuring: +// Explicit cast +var email3 = (EmailAddress)"user@example.com"; -- **Type-safe arithmetic** with dimensional analysis -- **Automatic unit conversions** with compile-time safety -- **Physics relationships** as operators (F = ma, E = mc², etc.) -- **Physical constants** integrated throughout -- **Generic numeric types** (double, float, decimal) support +// Safe creation +if (EmailAddress.TryCreate("maybe@invalid", out EmailAddress? safeEmail)) { /* … */ } +``` -### Complete Domain Coverage +Compile-time safety prevents the classic mix-up: -#### 🔧 Mechanics (15 quantities) -Position, velocity, acceleration, force, pressure, energy, power, momentum, torque, angular velocity, angular acceleration, moment of inertia, density, and more. +```csharp +public void SendWelcomeEmail(EmailAddress to, UserId userId) { … } +// SendWelcomeEmail(userId, email); // ❌ won't compile +``` -#### ⚡ Electrical (11 quantities) -Voltage, current, resistance, power, charge, capacitance, inductance, electric field, magnetic field, and electrical properties. +Validation runs through the strategy/rule pipeline — see `architecture.md` and `validation-reference.md`. -#### 🌡️ Thermal (10 quantities) -Temperature, heat, entropy, thermal conductivity, heat capacity, thermal expansion, and thermodynamic properties. +### Factory pattern (DI) -#### 🧪 Chemical (10 quantities) -Amount of substance, molarity, reaction rates, pH, molar mass, activation energy, and chemical kinetics. +Use `SemanticStringFactory` when you want to inject construction: -#### 🔊 Acoustic (20 quantities) -Sound pressure, intensity, frequency, wavelength, acoustic impedance, loudness, pitch, and audio metrics. +```csharp +services.AddTransient, SemanticStringFactory>(); -#### ☢️ Nuclear (5 quantities) -Radioactive activity, absorbed dose, equivalent dose, exposure, and nuclear cross-sections. +public class UserService(ISemanticStringFactory emails) +{ + public Task CreateUserAsync(string raw) => + emails.TryCreate(raw, out var email) + ? Task.FromResult(new User(email)) + : throw new ArgumentException("invalid email"); +} +``` -#### 💡 Optical (6 quantities) -Luminous intensity, flux, illuminance, luminance, refractive index, and optical power. +## Semantic paths -#### 🌊 Fluid Dynamics (5 quantities) -Viscosity, flow rates, Reynolds numbers, bulk modulus, and fluid properties. +Paths are a separate hierarchy on top of `SemanticString`. Everything is a record so equality and immutability come for free. + +``` +IPath +├── IAbsolutePath ├── IFilePath +├── IRelativePath └── IDirectoryPath +├── IAbsoluteFilePath : IFilePath, IAbsolutePath +├── IRelativeFilePath : IFilePath, IRelativePath +├── IAbsoluteDirectoryPath : IDirectoryPath, IAbsolutePath +└── IRelativeDirectoryPath : IDirectoryPath, IRelativePath -### Usage Examples +IFileName, IFileExtension // separate hierarchies for non-path components +``` ```csharp -// Create quantities with dimensional safety -var force = Force.FromNewtons(100.0); -var distance = Length.FromMeters(5.0); -var time = Time.FromSeconds(2.0); - -// Physics relationships as operators -var work = force * distance; // W = F⋅d (Energy) -var power = work / time; // P = W/t (Power) -var velocity = distance / time; // v = d/t (Velocity) - -// Automatic unit conversions -Console.WriteLine(work.ToKilowattHours()); // 1.389e-7 kWh -Console.WriteLine(power.ToHorsepower()); // 6.705e-5 hp - -// Type safety prevents errors -// var invalid = force + time; // ❌ Compiler error! - -// Complex calculations with multiple domains -var temp = Temperature.FromCelsius(25.0); -var pressure = Pressure.FromPascals(101325.0); -var volume = Volume.FromLiters(22.4); -var gasConstant = PhysicalConstants.Generic.GasConstant(); - -// Ideal gas law: PV = nRT -var moles = (pressure * volume) / (gasConstant * temp); +var configFile = AbsoluteFilePath.Create(@"C:\app\config.json"); + +configFile.FileName; // config.json +configFile.FileExtension; // .json +configFile.DirectoryPath; // C:\app +configFile.Exists; // bool + +// Polymorphic collections +List all = [ + AbsoluteFilePath.Create(@"C:\data.txt"), + RelativeDirectoryPath.Create(@"logs\app"), + FilePath.Create(@"document.pdf") +]; + +var files = all.OfType().ToList(); +var absolutes = all.OfType().ToList(); ``` -## Physical Constants +### Conversion API + +- `AsAbsolute()` — using current working directory. +- `AsAbsolute(baseDirectory)` — using a specific base. +- `AsRelative(baseDirectory)` — relative against a base. + +## Semantic quantities + +The physics system is **metadata-driven**: the source of truth is `Semantics.SourceGenerators/Metadata/dimensions.json`, and the Roslyn generator emits one record per quantity into `Semantics.Quantities/Generated/`. + +### The unified vector model + +Every quantity is a vector. Direction-space dimensionality is part of the type: + +| Form | Sign | Examples | +|---|---|---| +| `IVector0` (magnitude) | `>= 0` | `Speed`, `Mass`, `Energy`, `Distance`, `Area` | +| `IVector1` (signed 1D) | signed | `Velocity1D`, `Force1D`, `Temperature`, `ElectricCharge` | +| `IVector2` (2D) | per-component | `Velocity2D`, `Force2D`, `Acceleration2D` | +| `IVector3` (3D) | per-component | `Velocity3D`, `Force3D`, `Position3D` | +| `IVector4` | per-component | reserved (relativistic / spacetime) | + +`IVectorN.Magnitude()` (for N >= 1) returns the corresponding `IVector0`. + +The model and its rationale live in `strategy-unified-vector-quantities.md`. Rules of thumb: -Centralized, type-safe access to **100+ physical constants** across all domains: +- A `Vector0` is *always* non-negative. `Speed.Create(-1)` throws. +- `V0 - V0` returns the same `V0` of `T.Abs(a - b)` (signed subtraction must use V1 explicitly). +- A semantic overload (e.g. `Weight` over `ForceMagnitude`) implicitly widens to its base; narrowing is explicit. +- All values are stored in SI base units. + +### Creating quantities ```csharp -// Fundamental constants (CODATA 2018) -var c = PhysicalConstants.Generic.SpeedOfLight(); // 299,792,458 m/s -var h = PhysicalConstants.Generic.PlanckConstant(); // 6.626070×10⁻³⁴ J⋅s -var Na = PhysicalConstants.Generic.AvogadroNumber(); // 6.022140×10²³ mol⁻¹ - -// Derived constants with automatic type conversion -var g = PhysicalConstants.Generic.StandardGravity(); // 9.80665 m/s² -var R = PhysicalConstants.Generic.GasConstant(); // 8.314462618 J/(mol⋅K) - -// Domain-specific constants -var rho = PhysicalConstants.Generic.StandardAirDensity(); // 1.225 kg/m³ -var c_sound = PhysicalConstants.Generic.SoundSpeedInAir(); // 343 m/s +// Vector0 — magnitudes (non-negative) +var speed = Speed.FromMeterPerSecond(15.0); +var mass = Mass.FromKilogram(10.0); +var distance = Distance.FromMeter(5.0); +var energy = Energy.FromJoule(1_000.0); + +// Vector1 — signed scalar +var v1 = Velocity1D.FromMeterPerSecond(-3.5); +var temp = Temperature.FromKelvin(300.0); + +// Vector3 — directional (object-initializer with X/Y/Z components) +var force3d = new Force3D { X = 0.0, Y = 0.0, Z = -9.8 }; +var disp3d = new Displacement3D { X = 3.0, Y = 4.0, Z = 0.0 }; ``` -## Path System +### Operators and dimensional analysis -Comprehensive polymorphic path handling with 11 different path types: +Cross-dimensional operators are declared in `dimensions.json` and emitted automatically: -### Interface Hierarchy +```csharp +// V0 × V0 (magnitudes) +var force = mass * AccelerationMagnitude.FromMeterPerSecondSquared(9.8); // Mass × Accel = Force +var work = ForceMagnitude.FromNewton(10.0) * distance; // F·d = Energy +var power = work / Duration.FromSecond(2.0); // W/t = Power + +// Vector ops +var workScalar = force3d.Dot(disp3d); // Energy (V0) +var torque = force3d.Cross(disp3d); // Torque3D (V3) +var magnitude = disp3d.Magnitude(); // Length (V0, always >= 0) + +// Type safety +// var nope = force + temp; // ❌ compiler error ``` -IPath (base) -├── IAbsolutePath : IPath -├── IRelativePath : IPath -├── IFilePath : IPath -├── IDirectoryPath : IPath -├── IAbsoluteFilePath : IFilePath, IAbsolutePath -├── IRelativeFilePath : IFilePath, IRelativePath -├── IAbsoluteDirectoryPath : IDirectoryPath, IAbsolutePath -└── IRelativeDirectoryPath : IDirectoryPath, IRelativePath -IFileName / IFileExtension (separate hierarchies) -``` +### Semantic overloads + +Several dimensions declare narrower-named overloads with implicit widening: -### Usage Example ```csharp -var filePath = AbsoluteFilePath.FromString(@"C:\app\config.json"); +var w = Weight.From(force); // Weight is a ForceMagnitude +var fm = ForceMagnitude.From(w); // implicit widening also OK +var d = Distance.FromMeter(10.0); +var rad = Radius.From(d); +var dia = rad.ToDiameter(); // 20m via metadata-defined relationship +``` -// Rich path operations -Console.WriteLine(filePath.FileName); // config.json -Console.WriteLine(filePath.FileExtension); // .json -Console.WriteLine(filePath.DirectoryPath); // C:\app -Console.WriteLine(filePath.Exists); // True/False +Overload preservation: `Weight + Weight => Weight`, but `Weight + Drag => ForceMagnitude` (narrowest-shared base). -// Polymorphic collections -List paths = [ - AbsoluteFilePath.FromString(@"C:\data.txt"), - RelativeDirectoryPath.FromString(@"temp\logs") -]; +### Physical constants + +Centralised, generated, and generic over storage type: -var files = paths.OfType().ToList(); +```csharp +var c = PhysicalConstants.Generic.SpeedOfLight(); // 299_792_458 m/s +var h = PhysicalConstants.Generic.PlanckConstant(); +var R = PhysicalConstants.Generic.GasConstant(); +var ftM = PhysicalConstants.Conversion.FeetToMeters(); // 0.3048 ``` -## Validation System - -The library includes 50+ validation attributes across multiple categories: - -### Text Validation -- `IsEmailAddress` - Email format validation -- `RegexMatch(pattern)` - Custom regex patterns -- `StartsWith` / `EndsWith` - Prefix/suffix validation -- `Contains` - Substring validation -- `IsBase64` - Base64 encoding validation - -### Format Validation -- `IsEmptyOrWhitespace` - Empty/whitespace validation -- `HasNonWhitespaceContent` - Non-whitespace requirement -- `IsSingleLine` / `IsMultiLine` - Line count validation -- `HasExactLines` / `HasMinimumLines` / `HasMaximumLines` - Line counts - -### First-Class Type Validation -- `IsBoolean` - Boolean representation validation -- `IsDateTime` - Date/time format validation -- `IsDecimal` / `IsDouble` / `IsInt32` - Numeric validation -- `IsGuid` - GUID format validation -- `IsIpAddress` - IP address validation -- `IsTimeSpan` - Time span validation -- `IsUri` - URI format validation -- `IsVersion` - Version string validation - -### Quantity Validation -- `IsPositive` / `IsNegative` - Sign validation -- `IsInRange(min, max)` - Value range validation - -### Path Validation -- `IsPath` - Path format validation -- `IsAbsolutePath` / `IsRelativePath` - Path type validation -- `IsFilePath` / `IsDirectoryPath` - Path category validation -- `DoesExist` - File system existence validation - -### Validation Strategies +Backing storage is `PreciseNumber`; the accessor converts via `T.CreateChecked` per call. + +### Adding new dimensions / overloads / relationships + +Edit `dimensions.json` and rebuild — see `physics-generator.md` for the full schema and an end-to-end walk-through. + +## Validation system + +Validation is attribute-driven and pipes through a strategy + rule architecture: + +1. **Attribute layer** — declarative validation on a type (`[IsEmailAddress]`, `[HasNonWhitespaceContent]`). +2. **Strategy layer** — `ValidateAllStrategy` (default), `ValidateAnyStrategy`, or a custom `IValidationStrategy`. +3. **Rule layer** — `IValidationRule` implementations selected per attribute. +4. **Factory layer** — `ValidationStrategyFactory` resolves the right strategy. + ```csharp -// All must pass (default) -[ValidateAll] -[IsNotEmpty, IsEmail, HasLength(5, 100)] +// Default: all must pass +[HasNonWhitespaceContent, IsEmailAddress, EndsWith(".com")] public sealed record BusinessEmail : SemanticString { } // Any can pass [ValidateAny] -[IsEmail, IsUri] +[IsEmailAddress, IsUri] public sealed record ContactMethod : SemanticString { } ``` -## Performance Features +The full attribute list is in `validation-reference.md`. The runtime architecture (interfaces, strategies, contracts) is in `architecture.md`. -The library is optimized for high-performance scenarios: +## Performance utilities -- **Span-based Operations** - Minimal memory allocations -- **Pooled String Builders** - Reused StringBuilder instances -- **Interned Path Strings** - Memory optimization for common paths -- **Zero-cost Conversions** - Efficient implicit conversions -- **Lazy Validation** - Validation only when needed - -### Performance Utilities -```csharp -// Pooled string builder for high-performance string operations -public class PooledStringBuilder : IDisposable +The library is tuned for throughput-sensitive scenarios: -// Interned strings for common paths -public static class InternedPathStrings +- `PooledStringBuilder` — pooled, disposable `StringBuilder` for hot paths. +- `InternedPathStrings` — intern frequently-used path literals. +- `SpanPathUtilities` — span-based path manipulation with no allocations. +- Validation runs once at construction and caches the verdict on the immutable record. -// Span-based path utilities for minimal allocations -public static class SpanPathUtilities -``` +## Integration -## Integration Examples +### ASP.NET Core model binding -### Dependency Injection ```csharp -services.AddTransient, SemanticStringFactory>(); - -public class UserService +[HttpPost] +public IActionResult CreateUser([FromBody] CreateUserRequest req) { - private readonly ISemanticStringFactory _emailFactory; - - public UserService(ISemanticStringFactory emailFactory) - { - _emailFactory = emailFactory; - } + if (!EmailAddress.TryCreate(req.Email, out var email)) + return BadRequest("Invalid email"); + return Ok(new User(email)); } ``` -### Entity Framework +### Entity Framework Core value conversion + ```csharp modelBuilder.Entity() .Property(u => u.Email) .HasConversion( email => email.ToString(), - value => EmailAddress.FromString(value)); + value => EmailAddress.Create(value)); ``` -### ASP.NET Core +### Dependency injection + ```csharp -[HttpPost] -public IActionResult CreateUser([FromBody] CreateUserRequest request) -{ - if (!_emailFactory.TryCreate(request.Email, out var email)) - { - return BadRequest("Invalid email format"); - } - - var user = new User(email); - return Ok(user); -} +services.AddTransient, SemanticStringFactory>(); ``` -This library transforms primitive-obsessed code into strongly-typed, self-validating domain models with comprehensive validation, complete physics capabilities across all major scientific domains, and excellent performance characteristics. With **80+ physics quantities**, **100+ physical constants**, and **50+ validation attributes**, it provides enterprise-ready solutions for scientific computing, engineering applications, and domain modeling. +## Where to go next + +- `strategy-unified-vector-quantities.md` for the physics architecture rationale and the full type hierarchy. +- `physics-generator.md` for the metadata schema and how to add new dimensions. +- `architecture.md` for SOLID/DRY patterns inside strings, paths, and validation. +- `validation-reference.md` for the complete attribute catalogue. +- `advanced-usage.md` for custom validation, contract validation, and DI patterns. diff --git a/docs/design-semantic-quantities.md b/docs/design-semantic-quantities.md deleted file mode 100644 index 4e9a745..0000000 --- a/docs/design-semantic-quantities.md +++ /dev/null @@ -1,1036 +0,0 @@ -# Semantic Quantities Library Design Document - -## Executive Summary - -This document presents the design for a strongly typed physical quantities library that provides compile-time safety, intuitive syntax, and comprehensive unit support, building upon the existing SemanticQuantity framework. - -## Overview - -The Semantic Quantities Library provides strongly typed quantities of physical dimensions with compile-time safety, intuitive syntax, and comprehensive unit support, building upon the existing SemanticQuantity framework. - -## API Design Goals - -The interface should remain clean and intuitive: - -```csharp -// Primary API - clean and intuitive with compile-time safety -var lengthA = 10.Meters(); // Uses configured storage type -var lengthB = 20.5f.Feet(); // Automatic conversion to configured type -var lengthC = 30.999.Kilometers(); // Multiple units supported - -var area = lengthA * lengthB; // Length × Length → Area (compile-time validated) -var volume = area * lengthC; // Area × Length → Volume (compile-time validated) - -var time = 10.Seconds(); -var velocity = lengthA / time; // Length ÷ Time → Velocity (compile-time validated) - -var combinedTime = 10.Minutes() + 30.Seconds(); // Same dimension addition -var speed = velocity.In(Units.KilometersPerHour); // Consistent unit conversion API - -// Explicit generic usage when needed for specific precision -var preciseLength = 10.123m.Meters(); -var preciseArea = preciseLength * preciseLength; // Returns Area - -// Compile-time safety: This would NOT compile -// var invalid = preciseLength + 5.Kilograms(); // Compile error: Length + Mass -// var mixed = preciseLength * 5.0.Meters(); // Compile error: mixed storage types -``` - -## Architecture Overview - -### Simplified Core Type System - -``` -ISemanticQuantity (existing base interface) -└── IPhysicalQuantity : ISemanticQuantity - ├── Length : IPhysicalQuantity, IMultiplyOperator, Area> - ├── Mass : IPhysicalQuantity - ├── Time : IPhysicalQuantity - ├── Area : IPhysicalQuantity, IMultiplyOperator, Volume> - ├── Velocity : IPhysicalQuantity, IDivideOperator, Acceleration> - └── [All other quantities with explicit operator interfaces...] -``` - -### Key Design Principles - -#### 1. Simplified Interface Hierarchy -**Simplified from complex self-referencing:** -```csharp -// Clean and simple -IPhysicalQuantity where T : struct, INumber -``` - -#### 2. Explicit Operator Interfaces -**Clear relationship definitions:** -```csharp -IMultiplyOperator, Area> // Length * Length = Area -IDivideOperator, Velocity> // Length / Time = Velocity -``` - -#### 3. Integration with SemanticQuantity Framework - -PhysicalQuantity extends the existing SemanticQuantity framework with: -- **Physical dimensional relationships** via explicit operator interfaces -- **Unit conversion capabilities** with SI base unit storage -- **Compile-time dimensional analysis** through interface contracts -- **Physical validation** (e.g., temperature above absolute zero) - -#### 4. Generic Storage Type Support - -The library inherits storage type flexibility from SemanticQuantity and adds physical-specific features: -- Support for the same storage types as SemanticQuantity (`float`, `double`, `decimal`, etc.) -- SI unit normalization for consistent calculations -- Physical unit conversion with precision preservation - -## Domain Coverage - -It should cover all base and derived SI physical dimensions, and be able to integrate and derive with other quantities from: - -### Base SI Units (7 fundamental dimensions) -- **Length** (meter, m) -- **Mass** (kilogram, kg) -- **Time** (second, s) -- **Electric Current** (ampere, A) -- **Thermodynamic Temperature** (kelvin, K) -- **Amount of Substance** (mole, mol) -- **Luminous Intensity** (candela, cd) - -### Domain-Specific Derived Quantities - -#### Mechanics -- Area (m²), Volume (m³) -- Velocity (m/s), Acceleration (m/s²) -- Force (N = kg⋅m/s²), Pressure (Pa = N/m²) -- Energy (J = N⋅m), Power (W = J/s) -- Momentum (kg⋅m/s), Angular Momentum (kg⋅m²/s) - -#### Electromagnetism -- Electric Charge (C = A⋅s) -- Electric Potential (V = W/A) -- Electric Resistance (Ω = V/A) -- Electric Capacitance (F = C/V) -- Magnetic Field (T = Wb/m²) -- Electric Field (V/m) -- Inductance (H = Wb/A) - -#### Thermodynamics -- Heat Capacity (J/K) -- Specific Heat Capacity (J/(kg⋅K)) -- Thermal Conductivity (W/(m⋅K)) -- Entropy (J/K) -- Heat Transfer Coefficient (W/(m²⋅K)) - -#### Chemistry -- Molarity (mol/L) -- Molality (mol/kg) -- Molar Mass (kg/mol) -- Reaction Rate (mol/(L⋅s)) -- Gas Constant applications - -#### Photometry -- Luminous Flux (lm = cd⋅sr) -- Illuminance (lx = lm/m²) -- Luminance (cd/m²) -- Luminous Efficacy (lm/W) - -#### Rotational Mechanics -- Angular Velocity (rad/s) -- Angular Acceleration (rad/s²) -- Torque (N⋅m) -- Moment of Inertia (kg⋅m²) -- Angular Momentum (kg⋅m²/s) - -## Core Interfaces - -### Base Physical Quantity Interface -```csharp -public interface IPhysicalQuantity : ISemanticQuantity, - IEquatable>, IComparable> - where T : struct, INumber -{ - /// Physical dimension (e.g., "Length [L]", "Force [M L T⁻²]") - string Dimension { get; } - - /// SI base unit for this quantity type - IUnit BaseUnit { get; } - - /// Convert to specified unit - T In(IUnit targetUnit); - - /// Convert to specified unit using generic unit type - T In() where TUnit : IUnit, new(); - - /// Get value with uncertainty if available - UncertainValue WithUncertainty { get; } - - /// Validate physical constraints (e.g., positive temperature) - bool IsPhysicallyValid { get; } -} -``` - -### Simplified Operator Interfaces -```csharp -// Correctly constrained operator interfaces with proper TSelf definition -public interface IMultiplyOperator - where TSelf : IPhysicalQuantity - where TOther : IPhysicalQuantity - where TResult : IPhysicalQuantity -{ - static abstract TResult operator *(TSelf left, TOther right); -} - -public interface IDivideOperator - where TSelf : IPhysicalQuantity - where TOther : IPhysicalQuantity - where TResult : IPhysicalQuantity -{ - static abstract TResult operator /(TSelf left, TOther right); -} - -// Addition/subtraction only allowed within same dimension -public interface IAddOperator where TSelf : IPhysicalQuantity -{ - static abstract TSelf operator +(TSelf left, TSelf right); - static abstract TSelf operator -(TSelf left, TSelf right); -} - -// Comparison operators for same dimension -public interface IComparisonOperator where TSelf : IPhysicalQuantity -{ - static abstract bool operator >(TSelf left, TSelf right); - static abstract bool operator <(TSelf left, TSelf right); - static abstract bool operator >=(TSelf left, TSelf right); - static abstract bool operator <=(TSelf left, TSelf right); - static abstract bool operator ==(TSelf left, TSelf right); - static abstract bool operator !=(TSelf left, TSelf right); -} -``` - -## Robust Unit System - -### Improved Unit Interface -```csharp -public interface IUnit -{ - string Name { get; } - string Symbol { get; } - string Dimension { get; } // Must match quantity dimension - UnitSystem System { get; } - bool IsBaseUnit { get; } -} - -public interface ILinearUnit : IUnit -{ - double ToBaseFactor { get; } // Multiplication factor to base unit -} - -public interface IOffsetUnit : IUnit -{ - double ToBaseFactor { get; } // For temperature: K = C + 273.15 - double ToBaseOffset { get; } -} - -public interface ICompoundUnit : IUnit -{ - IReadOnlyList Components { get; } // For m/s, m²/s³, etc. -} - -public record UnitComponent(IUnit Unit, int Power); // m² = (meter, 2) -``` - -### Unit Registry and Validation -```csharp -public static class Units -{ - // Length units - public static readonly ILinearUnit Meter = new LinearUnit("meter", "m", "Length", 1.0, UnitSystem.SI); - public static readonly ILinearUnit Kilometer = new LinearUnit("kilometer", "km", "Length", 1000.0, UnitSystem.SI); - public static readonly ILinearUnit Foot = new LinearUnit("foot", "ft", "Length", 0.3048, UnitSystem.Imperial); - - // Temperature units with offset - public static readonly ILinearUnit Kelvin = new LinearUnit("kelvin", "K", "Temperature", 1.0, UnitSystem.SI); - public static readonly IOffsetUnit Celsius = new OffsetUnit("celsius", "°C", "Temperature", 1.0, 273.15, UnitSystem.SI); - - // Compound units - public static readonly ICompoundUnit MetersPerSecond = new CompoundUnit("meters per second", "m/s", "Velocity", - [new(Meter, 1), new(Second, -1)], UnitSystem.SI); - - // Unit validation at registration - static Units() - { - ValidateUnitConsistency(); - } -} -``` - -## Core Design Principles - -### 1. Type Safety -- Compile-time dimensional analysis -- Prevent invalid operations (e.g., adding Length + Mass) -- Return correct derived types from operations - -### 2. Unit Conversion System -The system uses constants for conversion factors and offsets, internally storing values in SI units, with methods to convert to other applicable units of the same dimension, both metric and imperial. - -All calculations are performed in SI units for consistency. - -### 3. Physical Relationship Contracts -Physical relationships are defined through explicit operator interfaces that ensure type safety at compile time: - -```csharp -// Length can multiply with itself to create Area, and divide by Time to create Velocity -public readonly struct Length : IPhysicalQuantity, - IMultiplyOperator, Length, Area>, // Length × Length → Area - IDivideOperator, Time, Velocity>, // Length ÷ Time → Velocity - IAddOperator>, // Length + Length → Length - IComparisonOperator> // Length comparison operators - where T : struct, INumber -{ - // Implementation ensures compile-time type safety - public static Area operator *(Length left, Length right) - => new Area(left.Value * right.Value); - - public static Velocity operator /(Length left, Time right) - => new Velocity(left.Value / right.Value); -} - -// Force demonstrates multiple dimensional relationships -public readonly struct Force : IPhysicalQuantity, - IDivideOperator, Mass, Acceleration>, // Force ÷ Mass → Acceleration - IDivideOperator, Area, Pressure>, // Force ÷ Area → Pressure - IMultiplyOperator, Length, Energy>, // Force × Length → Energy - IAddOperator>, // Force + Force → Force - IComparisonOperator> // Force comparison operators - where T : struct, INumber -{ - // Only valid operations are available - compile-time safety guaranteed -} -``` - -### 4. Arithmetic Operations Within Same Dimension -```csharp -// Arithmetic within same dimension (generic storage type) -Length a = 10.Meters(); -Length b = 5.Meters(); -Length sum = a + b; // Addition -Length diff = a - b; // Subtraction -Length scaled = a * T.CreateChecked(2); // Scalar multiplication - -// Comparison operators -bool isGreater = length1 > length2; -bool isEqual = length1 == length2; -``` - -### 5. Fluent Extension Methods -```csharp -public static class NumericExtensions -{ - public static Length Meters(this T value) where T : struct, INumber - => new Length(value); - - public static Length Feet(this T value) where T : struct, INumber - => new Length(value * T.CreateChecked(0.3048)); - - public static Length Kilometers(this T value) where T : struct, INumber - => new Length(value * T.CreateChecked(1000)); - - public static Mass Kilograms(this T value) where T : struct, INumber - => new Mass(value); - - public static Mass Grams(this T value) where T : struct, INumber - => new Mass(value * T.CreateChecked(0.001)); - - public static Mass Pounds(this T value) where T : struct, INumber - => new Mass(value * T.CreateChecked(0.453592)); - - public static Time Seconds(this T value) where T : struct, INumber - => new Time(value); - - public static Time Minutes(this T value) where T : struct, INumber - => new Time(value * T.CreateChecked(60)); - - public static Time Hours(this T value) where T : struct, INumber - => new Time(value * T.CreateChecked(3600)); - - // Usage examples demonstrating relationship contracts: - // var area = 10.Meters() * 5.Meters(); // Returns Area via IIntegralOperators - // var velocity = 100.Meters() / 10.Seconds(); // Returns Velocity via IDerivativeOperators - // var force = 2.Kilograms() * 9.8m.MetersPerSecondSquared(); // Returns Force -} -``` - -## Enhanced Type Safety - -### Compile-Time Relationship Validation -```csharp -// Properly constrained operator implementations with compile-time validation -public readonly struct Length : IPhysicalQuantity, - IMultiplyOperator, Length, Area>, // Correct: TSelf, TOther, TResult - IDivideOperator, Time, Velocity>, // Correct: TSelf, TOther, TResult - IAddOperator>, // Same dimension operations - IComparisonOperator> // Comparison operations - where T : struct, INumber -{ - private readonly T _value; - - public Length(T value) => _value = ValidationHelper.ValidateFinite(value); - - public T Value => _value; - public string Dimension => "Length [L]"; - public IUnit BaseUnit => Units.Meter; - public bool IsPhysicallyValid => T.IsFinite(_value) && _value >= T.Zero; - - // Correctly typed operator implementations - public static Area operator *(Length left, Length right) - => new Area(left._value * right._value); - - public static Velocity operator /(Length left, Time right) - => new Velocity(left._value / right.Value); - - public static Length operator +(Length left, Length right) - => new Length(left._value + right._value); - - public static Length operator -(Length left, Length right) - => new Length(left._value - right._value); - - // Comparison operators - public static bool operator >(Length left, Length right) - => left._value > right._value; - - public static bool operator <(Length left, Length right) - => left._value < right._value; - - // Compile-time safety: Invalid operations like Length + Mass are impossible - // The type system prevents such operations from compiling -} -``` - -### Runtime Dimensional Validation -```csharp -public class DimensionalValidator : IDimensionalValidator -{ - private readonly Dictionary _knownDimensions; - - public bool ValidateOperation(string operation) - where TLeft : IPhysicalQuantity - where TRight : IPhysicalQuantity - where TResult : IPhysicalQuantity - { - var leftDim = ParseDimension(typeof(TLeft)); - var rightDim = ParseDimension(typeof(TRight)); - var resultDim = ParseDimension(typeof(TResult)); - - var expectedResult = operation switch - { - "*" => leftDim + rightDim, - "/" => leftDim - rightDim, - "+" or "-" => leftDim == rightDim ? leftDim : null, - _ => throw new ArgumentException($"Unknown operation: {operation}") - }; - - return expectedResult?.Equals(resultDim) == true; - } -} - -// Dimensional signature for validation -public record DimensionSignature( - int Length = 0, int Mass = 0, int Time = 0, int Current = 0, - int Temperature = 0, int Substance = 0, int Luminosity = 0) -{ - public static DimensionSignature operator +(DimensionSignature left, DimensionSignature right) - => new(left.Length + right.Length, left.Mass + right.Mass, /* ... */); - - public static DimensionSignature operator -(DimensionSignature left, DimensionSignature right) - => new(left.Length - right.Length, left.Mass - right.Mass, /* ... */); -} -``` - -## Comprehensive Error Handling - -### Robust Error Types -```csharp -public abstract class PhysicalQuantityException : Exception -{ - protected PhysicalQuantityException(string message) : base(message) { } - protected PhysicalQuantityException(string message, Exception innerException) : base(message, innerException) { } -} - -public class DimensionalMismatchException : PhysicalQuantityException -{ - public string LeftDimension { get; } - public string RightDimension { get; } - public string Operation { get; } - - public DimensionalMismatchException(string operation, string leftDim, string rightDim) - : base($"Cannot perform {operation} between {leftDim} and {rightDim}") - { - Operation = operation; - LeftDimension = leftDim; - RightDimension = rightDim; - } -} - -public class UnitConversionException : PhysicalQuantityException -{ - public IUnit SourceUnit { get; } - public IUnit TargetUnit { get; } - - public UnitConversionException(IUnit source, IUnit target, string reason) - : base($"Cannot convert from {source.Symbol} to {target.Symbol}: {reason}") - { - SourceUnit = source; - TargetUnit = target; - } -} - -public class PhysicalConstraintViolationException : PhysicalQuantityException -{ - public object Value { get; } - public string Constraint { get; } - - public PhysicalConstraintViolationException(object value, string constraint) - : base($"Value {value} violates physical constraint: {constraint}") - { - Value = value; - Constraint = constraint; - } -} -``` - -### Safe Operation Patterns -```csharp -// Result pattern for operations that might fail -public readonly struct OperationResult -{ - public bool IsSuccess { get; } - public T Value { get; } - public PhysicalQuantityException? Error { get; } - - private OperationResult(T value) - { - IsSuccess = true; - Value = value; - Error = null; - } - - private OperationResult(PhysicalQuantityException error) - { - IsSuccess = false; - Value = default!; - Error = error; - } - - public static OperationResult Success(T value) => new(value); - public static OperationResult Failure(PhysicalQuantityException error) => new(error); -} - -// Safe operations -public static class SafeOperations -{ - public static OperationResult> TryDivide(Length distance, Time time) - where T : struct, INumber - { - try - { - if (time.Value == T.Zero) - return OperationResult>.Failure( - new PhysicalConstraintViolationException(time.Value, "Time cannot be zero")); - - return OperationResult>.Success(distance / time); - } - catch (Exception ex) - { - return OperationResult>.Failure(new PhysicalQuantityException("Division failed", ex)); - } - } -} -``` - -## Performance Optimizations - -### Optimized Readonly Structs -```csharp -[StructLayout(LayoutKind.Auto)] -public readonly struct Length : IPhysicalQuantity - where T : struct, INumber -{ - private readonly T _value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Length(T value) => _value = value; - - public T Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Length operator +(Length left, Length right) - => new(left._value + right._value); - - // Ref return for zero-copy access in performance-critical scenarios - public ref readonly T ValueRef => ref _value; -} -``` - -### Bulk Operations Support -```csharp -public static class BulkOperations -{ - // SIMD-optimized bulk conversions - public static void ConvertUnits(ReadOnlySpan> source, - Span> destination, - T conversionFactor) - where T : struct, INumber - { - // Can be vectorized by JIT - for (int i = 0; i < source.Length; i++) - { - destination[i] = new Length(source[i].Value * conversionFactor); - } - } - - // Parallel processing for large datasets - public static async Task[]> CalculateAreasAsync(Length[] widths, Length[] heights) - where T : struct, INumber - { - return await Task.Run(() => - widths.AsParallel() - .Zip(heights.AsParallel()) - .Select(pair => pair.First * pair.Second) - .ToArray()); - } -} -``` - -## Testing Strategy - -### Comprehensive Test Categories -```csharp -[TestFixture] -public class LengthTests -{ - [Test] - [TestCase(10.0, ExpectedResult = 10.0)] - [TestCase(0.0, ExpectedResult = 0.0)] - [TestCase(double.MaxValue, ExpectedResult = double.MaxValue)] - public double Constructor_ValidValues_StoresCorrectly(double value) - { - var length = new Length(value); - return length.Value; - } - - [Test] - public void Constructor_NaN_ThrowsException() - { - Assert.Throws(() => new Length(double.NaN)); - } - - [Test] - public void Multiply_TwoLengths_ReturnsCorrectArea() - { - var length1 = new Length(5.0); - var length2 = new Length(4.0); - - var area = length1 * length2; - - Assert.AreEqual(20.0, area.Value); - Assert.AreEqual("Area [L²]", area.Dimension); - } - - [Test] - public void Conversion_MetersToFeet_WithinTolerance() - { - var meters = 10.0.Meters(); - var feet = meters.In(Units.Foot); - - Assert.AreEqual(32.8084, feet, 0.0001); - } - - [Test] - public void Addition_DifferentStorageTypes_CompileError() - { - // This should not compile - // var length1 = new Length(5.0); - // var length2 = new Length(3.0f); - // var sum = length1 + length2; // Compile error! - } -} - -[TestFixture] -public class DimensionalValidationTests -{ - [Test] - public void ValidateOperation_LengthTimesLength_ReturnsTrue() - { - var validator = new DimensionalValidator(); - - var isValid = validator.ValidateOperation, Length, Area>("*"); - - Assert.IsTrue(isValid); - } - - [Test] - public void ValidateOperation_LengthPlusMass_ReturnsFalse() - { - var validator = new DimensionalValidator(); - - // This would be a compile error anyway, but test the validator - var isValid = validator.ValidateOperation, Mass, object>("+" ); - - Assert.IsFalse(isValid); - } -} -``` - -## Improved Configuration System - -### Better Dependency Injection Pattern -```csharp -// Remove static service provider anti-pattern -public interface IPhysicalQuantityFactory -{ - ILength CreateLength(double value, IUnit? unit = null); - IMass CreateMass(double value, IUnit? unit = null); - ITime CreateTime(double value, IUnit? unit = null); - TQuantity Create(double value, IUnit? unit = null) - where TQuantity : IPhysicalQuantity; -} - -// Configuration through options pattern -public class PhysicalQuantityOptions -{ - public Type StorageType { get; set; } = typeof(double); - public bool ValidatePhysicalConstraints { get; set; } = true; - public bool EnableUncertaintyPropagation { get; set; } = false; - public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; - public IUnit DefaultLengthUnit { get; set; } = Units.Meter; - public IUnit DefaultMassUnit { get; set; } = Units.Kilogram; - // ... default units for each dimension -} - -// Clean service registration -public static class ServiceCollectionExtensions -{ - public static IServiceCollection AddPhysicalQuantities( - this IServiceCollection services, - Action? configure = null) - { - services.Configure(configure ?? (_ => { })); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - return services; - } -} -``` - -### Intuitive Extension Methods -```csharp -// Clean, type-safe extension methods with consistent API -public static class NumericExtensions -{ - // Primary API - uses configured storage type, clean syntax - public static ILength Meters(this double value) => CreateLength(value, Units.Meter); - public static ILength Feet(this double value) => CreateLength(value, Units.Foot); - public static ILength Kilometers(this double value) => CreateLength(value, Units.Kilometer); - - public static IMass Kilograms(this double value) => CreateMass(value, Units.Kilogram); - public static IMass Grams(this double value) => CreateMass(value, Units.Gram); - public static IMass Pounds(this double value) => CreateMass(value, Units.Pound); - - public static ITime Seconds(this double value) => CreateTime(value, Units.Second); - public static ITime Minutes(this double value) => CreateTime(value, Units.Minute); - public static ITime Hours(this double value) => CreateTime(value, Units.Hour); - - // Explicit generic versions for specific precision requirements - public static Length Meters(this T value) where T : struct, INumber - => new Length(ConvertToSI(value, Units.Meter)); - - public static Mass Kilograms(this T value) where T : struct, INumber - => new Mass(ConvertToSI(value, Units.Kilogram)); - - public static Time Seconds(this T value) where T : struct, INumber - => new Time(ConvertToSI(value, Units.Second)); - - // Type conversions for seamless API - public static ILength Meters(this float value) => ((double)value).Meters(); - public static ILength Meters(this decimal value) => ((double)value).Meters(); - public static ILength Meters(this int value) => ((double)value).Meters(); - - // Helper methods use DI when available, fallback to double - private static ILength CreateLength(double value, IUnit unit) => - GetFactory()?.CreateLength(value, unit) ?? new Length(ConvertToSI(value, unit)); - - private static IPhysicalQuantityFactory? GetFactory() => - // Resolve from DI container or return null for fallback - ServiceLocator.Current?.GetService(); -} - -// Clean usage with dependency injection -public class PhysicsCalculator -{ - private readonly IPhysicalQuantityFactory _factory; - - public PhysicsCalculator(IPhysicalQuantityFactory factory) - { - _factory = factory; - } - - public IEnergy CalculateKineticEnergy(IMass mass, IVelocity velocity) - { - // Clean API: E = ½mv² - var half = 0.5.Scalar(); // Uses scalar extension - return half * mass * velocity * velocity; // Type-safe operations - } - - public IPressure CalculatePressure(IForce force, IArea area) - { - // Clean API: P = F/A - return force / area; // Compile-time validated: Force ÷ Area → Pressure - } -} -``` - -## Configuration System - -An application using the library should be able to configure which data type to use as the storage type for the quantities, eliminating the need to specify the storage type at every call site. - -### Global Storage Type Configuration - -#### Primary Approach: Dependency Injection with Default Storage Type -```csharp -// Configuration options for physical quantities -public class PhysicalQuantityOptions -{ - public Type DefaultStorageType { get; set; } = typeof(double); // double is default - public bool ValidateRanges { get; set; } = true; - public bool EnableDebugMode { get; set; } = false; - public CultureInfo CultureInfo { get; set; } = CultureInfo.InvariantCulture; - public Dictionary UnitSystemPreferences { get; set; } = new(); -} - -// DI-aware context for creating quantities with configured storage type -public interface IPhysicalQuantityContext -{ - ILength CreateLength(double value); - IMass CreateMass(double value); - ITime CreateTime(double value); - IForce CreateForce(double value); - IArea CreateArea(double value); - IVelocity CreateVelocity(double value); - // ... all quantity types -} - -// Generic implementation that can be configured for any storage type T -public class PhysicalQuantityContext : IPhysicalQuantityContext - where T : struct, INumber -{ - public ILength CreateLength(double value) => new Length(T.CreateChecked(value)); - public IMass CreateMass(double value) => new Mass(T.CreateChecked(value)); - public ITime CreateTime(double value) => new Time(T.CreateChecked(value)); - public IForce CreateForce(double value) => new Force(T.CreateChecked(value)); - public IArea CreateArea(double value) => new Area(T.CreateChecked(value)); - public IVelocity CreateVelocity(double value) => new Velocity(T.CreateChecked(value)); - // ... implement all quantity types -} - -// Extension methods that use DI to get the configured context -public static class NumericExtensions -{ - private static IServiceProvider? _serviceProvider; - - // Called by the DI container during startup - internal static void SetServiceProvider(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - // Clean extension methods that use configured storage type - public static ILength Meters(this double value) - { - var context = GetContext(); - return context.CreateLength(value); - } - - public static ILength Meters(this float value) => ((double)value).Meters(); - public static ILength Meters(this decimal value) => ((double)value).Meters(); - public static ILength Meters(this int value) => ((double)value).Meters(); - - public static IMass Kilograms(this double value) - { - var context = GetContext(); - return context.CreateMass(value); - } - - public static ITime Seconds(this double value) - { - var context = GetContext(); - return context.CreateTime(value); - } - - // Still allow explicit generic usage for mixed scenarios - public static Length Meters(this T value) where T : struct, INumber - => new Length(value); - - public static Mass Kilograms(this T value) where T : struct, INumber - => new Mass(value); - - private static IPhysicalQuantityContext GetContext() - { - if (_serviceProvider == null) - { - // Fallback to double if DI not configured - return new PhysicalQuantityContext(); - } - - return _serviceProvider.GetRequiredService(); - } -} - -// Non-generic interfaces for clean API -public interface ILength -{ - string Dimension { get; } - double ValueAsDouble { get; } - T GetValue() where T : struct, INumber; - IArea Multiply(ILength other); - IVelocity Divide(ITime time); - T In() where T : IUnit, new(); -} - -public interface IMass -{ - string Dimension { get; } - double ValueAsDouble { get; } - T GetValue() where T : struct, INumber; - IForce Multiply(IAcceleration acceleration); -} - -public interface ITime -{ - string Dimension { get; } - double ValueAsDouble { get; } - T GetValue() where T : struct, INumber; -} - -// ... other interfaces for all quantity types -``` - - - -### Usage Examples - -#### Application Startup Configuration -```csharp -// Program.cs or Startup.cs - -// Option 1: Default registration (uses double storage) -services.AddPhysicalQuantities(); // Defaults to double - -// Option 2: Explicit storage type configuration -services.AddPhysicalQuantities(); // High precision for financial apps -services.AddPhysicalQuantities(); // Memory efficient for game engines -services.AddPhysicalQuantities(); // Explicit double (same as default) - -// The DI container will inject IPhysicalQuantityContext with the configured storage type -``` - -#### Clean Usage Without Generic Specification -```csharp -// After DI configuration, clean API without generics -var length1 = 10.Meters(); // Uses DI-configured storage type (ILength) -var length2 = 5.5.Feet(); // Uses DI-configured storage type (ILength) -var mass = 2.Kilograms(); // Uses DI-configured storage type (IMass) - -var area = length1 * length2; // Returns IArea with configured storage type -var velocity = length1 / 3.Seconds(); // Returns IVelocity with configured storage type - -// Convert to specific units -var distanceInKm = length1.In(); // Gets value in km using configured storage type -var speedInMph = velocity.In(); // Gets value in mph - -// Still allows explicit generic usage when needed -var preciseLength = 10.123456789m.Meters(); // Explicit decimal - Length -var fastLength = 10.5f.Meters(); // Explicit float - Length -``` - -#### Default Behavior Without DI Configuration -```csharp -// If AddPhysicalQuantities() is never called, extension methods fall back to double -var length = 10.Meters(); // Falls back to Length internally -var mass = 2.Kilograms(); // Falls back to Mass internally -var time = 5.Seconds(); // Falls back to Time internally - -// All operations work normally with double as the storage type -var area = length * 8.Meters(); // Returns IArea backed by Area -var velocity = length / time; // Returns IVelocity backed by Velocity -``` - -#### Mixed Storage Type Scenarios -```csharp -// Configure DI to use double as default -services.AddPhysicalQuantities(); // or services.AddPhysicalQuantities(); - -// Most calculations use default (double via DI) -var distance = 100.Meters(); // ILength backed by Length -var time = 10.Seconds(); // ITime backed by Time -var speed = distance / time; // IVelocity backed by Velocity - -// Specific high-precision calculations can still use explicit generics -var preciseDistance = 100.123456789m.Meters(); // Length -var preciseTime = 10.987654321m.Seconds(); // Time -var preciseSpeed = preciseDistance / preciseTime; // Velocity - -// Can access underlying storage type when needed -var doubleValue = distance.GetValue(); // Gets the double value -var decimalValue = preciseDistance.Value; // Gets the decimal value directly -``` - -## Migration Path from Legacy Design - -### Phase 1: Core Infrastructure -1. Implement simplified interfaces (`IPhysicalQuantity`) -2. Create basic quantity structs (Length, Mass, Time) -3. Implement unit system with validation -4. Add basic arithmetic operations - -### Phase 2: Advanced Features -1. Add compound units and conversions -2. Implement dimensional validation -3. Add uncertainty propagation -4. Create bulk operations support - -### Phase 3: Integration & Polish -1. Finalize DI integration patterns -2. Add comprehensive error handling -3. Performance optimization and benchmarking -4. Documentation and samples - -## Key Benefits of This Design - -1. **Simplified Architecture**: Removed complex generic self-referencing -2. **Better Error Handling**: Comprehensive exception hierarchy and safe operations -3. **Robust Unit System**: Support for compound units and proper validation -4. **Cleaner DI Pattern**: Removed static service provider anti-pattern -5. **Enhanced Type Safety**: Compile-time prevention of invalid operations -6. **Better Performance**: Optimized struct layout and bulk operations -7. **Improved Testability**: Dependency injection enables better unit testing -8. **Future-Proof**: Extensible architecture for advanced features - -## Future Enhancements - -1. **Complex Numbers Support** - For AC electrical calculations -2. **Vector Quantities** - Support for directional quantities -3. **Matrix Operations** - For advanced physics calculations -4. **Symbolic Math Integration** - Work with computer algebra systems -5. **Serialization Support** - JSON, XML, Binary serialization -6. **Localization** - Multi-language unit names and formatting -7. **GraphQL/OpenAPI Integration** - Schema generation for web APIs -8. **Database Integration** - Entity Framework value converters - ---- - -This design maintains the original vision while addressing architectural concerns and providing a more robust, maintainable foundation for the physical quantities library. diff --git a/docs/examples/ComprehensivePhysicsExamples.md b/docs/examples/ComprehensivePhysicsExamples.md index 844871a..085bd7f 100644 --- a/docs/examples/ComprehensivePhysicsExamples.md +++ b/docs/examples/ComprehensivePhysicsExamples.md @@ -22,8 +22,8 @@ public class EVMotorAnalysis public static void AnalyzeMotorPerformance() { // Motor specifications (Electrical domain) - var batteryVoltage = ElectricPotential.FromVolts(400.0); // 400V battery pack - var motorCurrent = ElectricCurrent.FromAmperes(300.0); // 300A peak current + var batteryVoltage = ElectricPotential.FromVolt(400.0); // 400V battery pack + var motorCurrent = ElectricCurrent.FromAmpere(300.0); // 300A peak current var motorEfficiency = 0.95; // 95% efficiency // Calculate electrical power @@ -31,7 +31,7 @@ public class EVMotorAnalysis // Mechanical output (Mechanical domain) var mechanicalPower = Power.Create(electricalPower.Value * motorEfficiency); // 114 kW - var wheelRadius = Length.FromMeters(0.35); // 35cm wheel radius + var wheelRadius = Length.FromMeter(0.35); // 35cm wheel radius var gearRatio = 10.0; // 10:1 reduction // Calculate torque and speed @@ -46,9 +46,9 @@ public class EVMotorAnalysis // Thermal analysis (Thermal domain) var powerLoss = Power.Create(electricalPower.Value * (1 - motorEfficiency)); // 6 kW loss - var motorMass = Mass.FromKilograms(50.0); // 50 kg motor + var motorMass = Mass.FromKilogram(50.0); // 50 kg motor var specificHeat = SpecificHeat.FromJoulesPerKilogramKelvin(900.0); // Aluminum - var operatingTime = Time.FromMinutes(10.0); // 10 minutes + var operatingTime = Time.FromMinute(10.0); // 10 minutes // Temperature rise calculation var energyLoss = Energy.Create(powerLoss.Value * operatingTime.Value); @@ -58,7 +58,7 @@ public class EVMotorAnalysis Console.WriteLine($" Electrical Power: {electricalPower.In(Units.Kilowatt):F1} kW"); Console.WriteLine($" Mechanical Power: {mechanicalPower.In(Units.Kilowatt):F1} kW"); Console.WriteLine($" Motor Torque: {motorTorque.In(Units.NewtonMeter):F0} N⋅m"); - Console.WriteLine($" Vehicle Speed: {vehicleSpeed.In(Units.KilometersPerHour):F1} km/h"); + Console.WriteLine($" Vehicle Speed: {vehicleSpeed.In(Units.KilometerPerHour):F1} km/h"); Console.WriteLine($" Temperature Rise: {tempRise.Value:F1} K"); } } @@ -79,7 +79,7 @@ public class OpticalFiberSystem laserWavelength); var laserPower = Power.FromMilliwatts(10.0); // 10 mW optical power - var fiberLength = Length.FromKilometers(100.0); // 100 km fiber + var fiberLength = Length.FromKilometer(100.0); // 100 km fiber var attenuationCoeff = 0.2; // 0.2 dB/km at 1550 nm // Calculate optical power after fiber transmission @@ -89,8 +89,8 @@ public class OpticalFiberSystem // Electrical domain - Photodiode characteristics var responsivity = 1.0; // 1.0 A/W responsivity - var photoCurrent = ElectricCurrent.FromAmperes(receivedPower.In(Units.Watt) * responsivity); - var loadResistance = ElectricResistance.FromOhms(50.0); // 50Ω load + var photoCurrent = ElectricCurrent.FromAmpere(receivedPower.In(Units.Watt) * responsivity); + var loadResistance = ElectricResistance.FromOhm(50.0); // 50Ω load var signalVoltage = photoCurrent * loadResistance; // V = I × R // Thermal domain - Laser thermal management @@ -130,8 +130,8 @@ public class ReactorPhysics // Neutron flux and cross-sections var neutronFlux = 1e14; // neutrons/(cm²·s) - var fissionCrossSection = NuclearCrossSection.FromBarns(582.0); // U-235 thermal fission - var absorptionCrossSection = NuclearCrossSection.FromBarns(99.0); // U-238 absorption + var fissionCrossSection = NuclearCrossSection.FromBarn(582.0); // U-235 thermal fission + var absorptionCrossSection = NuclearCrossSection.FromBarn(99.0); // U-238 absorption // Thermal domain - Heat removal var coolantInletTemp = Temperature.FromCelsius(290.0); // PWR inlet temp @@ -143,7 +143,7 @@ public class ReactorPhysics thermalPower.Value / (waterSpecificHeat.Value * tempRise.Value)); // Fluid dynamics domain - Coolant flow - var pipeRadius = Length.FromCentimeters(15.0); // 30 cm diameter + var pipeRadius = Length.FromCentimeter(15.0); // 30 cm diameter var pipeArea = Area.Create(Math.PI * Math.Pow(pipeRadius.Value, 2)); var waterDensity = Density.FromKilogramsPerCubicMeter(750.0); // Hot water density @@ -152,14 +152,14 @@ public class ReactorPhysics var flowVelocity = Velocity.Create(volumetricFlowRate.Value / pipeArea.Value); // Reynolds number for flow characterization - var waterViscosity = DynamicViscosity.FromPascalSeconds(0.0003); // Hot water viscosity + var waterViscosity = DynamicViscosity.FromPascalSecond(0.0003); // Hot water viscosity var reynolds = ReynoldsNumber.FromFluidProperties( waterDensity, flowVelocity, Length.Create(2 * pipeRadius.Value), waterViscosity); Console.WriteLine($"Nuclear Reactor Analysis:"); Console.WriteLine($" Thermal Power: {thermalPower.In(Units.Megawatt):F0} MW"); Console.WriteLine($" Required Flow Rate: {requiredMassFlowRate.Value:F0} kg/s"); - Console.WriteLine($" Coolant Velocity: {flowVelocity.In(Units.MetersPerSecond):F1} m/s"); + Console.WriteLine($" Coolant Velocity: {flowVelocity.In(Units.MeterPerSecond):F1} m/s"); Console.WriteLine($" Reynolds Number: {reynolds.Value:E2}"); Console.WriteLine($" Flow Regime: {(reynolds.Value > 4000 ? "Turbulent" : "Laminar")}"); } @@ -175,16 +175,16 @@ public class AcousticLab public static void AnalyzeSoundMeasurement() { // Acoustic domain - Sound measurement - var soundPressure = SoundPressure.FromPascals(0.1); // 0.1 Pa RMS + var soundPressure = SoundPressure.FromPascal(0.1); // 0.1 Pa RMS var frequency = Frequency.FromHertz(1000.0); // 1 kHz tone - var measurementDistance = Length.FromMeters(1.0); // 1 meter distance + var measurementDistance = Length.FromMeter(1.0); // 1 meter distance // Calculate sound pressure level var spl = SoundPressureLevel.FromSoundPressure(soundPressure); // dB SPL // Air properties for acoustic calculations var airDensity = Density.FromKilogramsPerCubicMeter(1.225); // Standard air - var soundSpeed = SoundSpeed.FromMetersPerSecond(343.0); // Sound speed in air + var soundSpeed = SoundSpeed.FromMeterPerSecond(343.0); // Sound speed in air var acousticImpedance = AcousticImpedance.FromDensityAndSoundSpeed(airDensity, soundSpeed); // Calculate acoustic intensity and power @@ -196,8 +196,8 @@ public class AcousticLab var wavelength = Wavelength.FromSpeedAndFrequency(soundSpeed, frequency); // Mechanical domain - Vibration source - var speakerMass = Mass.FromGrams(50.0); // 50g speaker cone - var displacement = Length.FromMillimeters(1.0); // 1mm peak displacement + var speakerMass = Mass.FromGram(50.0); // 50g speaker cone + var displacement = Length.FromMillimeter(1.0); // 1mm peak displacement var angularFreq = 2 * Math.PI * frequency.Value; var peakVelocity = Velocity.Create(angularFreq * displacement.Value); var peakAcceleration = Acceleration.Create(angularFreq * peakVelocity.Value); @@ -207,7 +207,7 @@ public class AcousticLab var mechanicalPower = Power.Create(mechanicalForce.Value * peakVelocity.Value / 2); // RMS // Electrical domain - Amplifier requirements - var speakerImpedance = ElectricResistance.FromOhms(8.0); // 8Ω speaker + var speakerImpedance = ElectricResistance.FromOhm(8.0); // 8Ω speaker var efficiency = 0.05; // 5% electro-acoustic efficiency var electricalPower = Power.Create(mechanicalPower.Value / efficiency); var driveCurrent = ElectricCurrent.Create(Math.Sqrt(electricalPower.Value / speakerImpedance.Value)); @@ -250,11 +250,11 @@ public class ChemicalProcess // Enzyme activity for bioreactor var enzymeActivity = EnzymeActivity.FromKatalPerLiter(0.5); // 0.5 kat/L - var substrateAmount = AmountOfSubstance.FromMoles(10.0); // 10 mol substrate + var substrateAmount = AmountOfSubstance.FromMole(10.0); // 10 mol substrate // Thermal domain - Heat management - var reactionHeat = Energy.FromKilojoules(150.0); // 150 kJ reaction enthalpy - var reactorMass = Mass.FromKilograms(500.0); // 500 kg reactor contents + var reactionHeat = Energy.FromKilojoule(150.0); // 150 kJ reaction enthalpy + var reactorMass = Mass.FromKilogram(500.0); // 500 kg reactor contents var specificHeat = SpecificHeat.FromJoulesPerKilogramKelvin(3500.0); // Solution specific heat var adiabaticTempRise = Temperature.Create( @@ -262,7 +262,7 @@ public class ChemicalProcess // Heat transfer coefficient for cooling var heatTransferCoeff = HeatTransferCoefficient.FromWattsPerSquareMeterKelvin(1000.0); - var coolingArea = Area.FromSquareMeters(20.0); // 20 m² cooling surface + var coolingArea = Area.FromSquareMeter(20.0); // 20 m² cooling surface var coolantTemp = Temperature.FromCelsius(20.0); // 20°C coolant var tempDifference = reactionTemp - coolantTemp; @@ -271,8 +271,8 @@ public class ChemicalProcess // Fluid dynamics domain - Mixing and flow var stirrerSpeed = AngularVelocity.FromRPM(200.0); // 200 RPM stirrer - var impellerDiameter = Length.FromCentimeters(30.0); // 30 cm impeller - var fluidViscosity = DynamicViscosity.FromPascalSeconds(0.001); // Water-like viscosity + var impellerDiameter = Length.FromCentimeter(30.0); // 30 cm impeller + var fluidViscosity = DynamicViscosity.FromPascalSecond(0.001); // Water-like viscosity var fluidDensity = Density.FromKilogramsPerCubicMeter(1100.0); // Solution density // Power number calculation for mixing @@ -307,34 +307,34 @@ public class EnvironmentalMonitoring public static void AnalyzeAirQuality() { // Chemical domain - Pollutant concentrations - var no2Concentration = Concentration.FromPartsPerMillion(0.05); // 50 ppb NO₂ + var no2Concentration = Concentration.FromPartPerMillion(0.05); // 50 ppb NO₂ var so2Concentration = Concentration.FromMilligramsPerCubicMeter(10.0); // 10 mg/m³ SO₂ - var coConcentration = Concentration.FromPartsPerMillion(1.0); // 1 ppm CO + var coConcentration = Concentration.FromPartPerMillion(1.0); // 1 ppm CO // Convert PPM to molar concentrations at STP var airDensity = Density.FromKilogramsPerCubicMeter(1.225); // Standard air density var standardTemp = Temperature.FromCelsius(25.0); - var standardPressure = Pressure.FromPascals(101325.0); + var standardPressure = Pressure.FromPascal(101325.0); // Molar volume at standard conditions var gasConstant = PhysicalConstants.Generic.GasConstant(); var molarVolume = Volume.Create(gasConstant * standardTemp.Value / standardPressure.Value); // Fluid dynamics domain - Atmospheric dispersion - var windSpeed = Velocity.FromMetersPerSecond(5.0); // 5 m/s wind - var mixingHeight = Length.FromMeters(1000.0); // 1 km mixing layer - var roughnessLength = Length.FromCentimeters(10.0); // Urban roughness + var windSpeed = Velocity.FromMeterPerSecond(5.0); // 5 m/s wind + var mixingHeight = Length.FromMeter(1000.0); // 1 km mixing layer + var roughnessLength = Length.FromCentimeter(10.0); // Urban roughness // Calculate atmospheric stability parameters - var airViscosity = DynamicViscosity.FromPascalSeconds(1.825e-5); // Air kinematic viscosity - var charakteristicLength = Length.FromKilometers(1.0); // 1 km scale + var airViscosity = DynamicViscosity.FromPascalSecond(1.825e-5); // Air kinematic viscosity + var charakteristicLength = Length.FromKilometer(1.0); // 1 km scale var atmosphericReynolds = ReynoldsNumber.FromFluidProperties( airDensity, windSpeed, charakteristicLength, airViscosity); // Dispersion coefficients (simplified Gaussian model) - var horizontalDispersion = Length.FromMeters(100.0); // σy - var verticalDispersion = Length.FromMeters(50.0); // σz + var horizontalDispersion = Length.FromMeter(100.0); // σy + var verticalDispersion = Length.FromMeter(50.0); // σz // Thermal domain - Temperature effects var surfaceTemp = Temperature.FromCelsius(25.0); // Surface temperature @@ -347,13 +347,13 @@ public class EnvironmentalMonitoring // Convective heat flux var convectiveHeatFlux = HeatTransferCoefficient.FromWattsPerSquareMeterKelvin(10.0); - var surfaceArea = Area.FromSquareKilometers(1.0); // 1 km² area + var surfaceArea = Area.FromSquareKilometer(1.0); // 1 km² area var heatFlux = Power.Create( convectiveHeatFlux.Value * surfaceArea.Value * Math.Abs(surfaceTemp - ambientTemp).Value); Console.WriteLine($"Environmental Monitoring Analysis:"); - Console.WriteLine($" NO₂ Concentration: {no2Concentration.InPartsPerMillion():F2} ppm"); - Console.WriteLine($" Wind Speed: {windSpeed.In(Units.MetersPerSecond):F1} m/s"); + Console.WriteLine($" NO₂ Concentration: {no2Concentration.InPartPerMillion():F2} ppm"); + Console.WriteLine($" Wind Speed: {windSpeed.In(Units.MeterPerSecond):F1} m/s"); Console.WriteLine($" Atmospheric Reynolds: {atmosphericReynolds.Value:E2}"); Console.WriteLine($" Temperature Gradient: {tempGradient * 1000:F2} K/km"); Console.WriteLine($" Surface Heat Flux: {heatFlux.In(Units.Megawatt):F1} MW"); @@ -374,8 +374,8 @@ public class PlasmaPhysics { // Electrical domain - Plasma electrical properties var electronDensity = 1e20; // electrons/m³ - var electronTemp = Temperature.FromElectronVolts(10.0); // 10 eV electron temperature - var ionTemp = Temperature.FromElectronVolts(5.0); // 5 eV ion temperature + var electronTemp = Temperature.FromElectronVolt(10.0); // 10 eV electron temperature + var ionTemp = Temperature.FromElectronVolt(5.0); // 5 eV ion temperature var plasmaConductivity = ElectricConductivity.FromSiemensPerMeter(1e6); // High conductivity var magneticField = 2.0; // 2 Tesla magnetic field @@ -387,11 +387,11 @@ public class PlasmaPhysics var plasmaFrequency = Math.Sqrt(electronDensity * Math.Pow(elementaryCharge, 2) / (electronMass * permittivity)); - var plasmaFreq = Frequency.FromRadiansPerSecond(plasmaFrequency); + var plasmaFreq = Frequency.FromRadianPerSecond(plasmaFrequency); // Optical domain - Plasma radiation var bremsstrahlung = 1e13; // W/m³ volume emission rate - var plasmaVolume = Volume.FromCubicMeters(1.0); // 1 m³ plasma volume + var plasmaVolume = Volume.FromCubicMeter(1.0); // 1 m³ plasma volume var radiatedPower = Power.Create(bremsstrahlung * plasmaVolume.Value); // Characteristic radiation wavelength @@ -406,7 +406,7 @@ public class PlasmaPhysics // Nuclear domain - Fusion reactions var deuteriumDensity = 5e19; // D nuclei/m³ var tritiumDensity = 5e19; // T nuclei/m³ - var fusionCrossSection = NuclearCrossSection.FromBarns(5.0); // D-T fusion cross section + var fusionCrossSection = NuclearCrossSection.FromBarn(5.0); // D-T fusion cross section var averageVelocity = Math.Sqrt(3 * kBoltzmann * ionTemp.InKelvin() / (2.5 * PhysicalConstants.Generic.AtomicMassUnit())); // D-T average @@ -416,7 +416,7 @@ public class PlasmaPhysics // Thermal domain - Heat balance var specificHeat = SpecificHeat.FromJoulesPerKilogramKelvin(5200.0); // Plasma specific heat var plasmaDensity = Density.FromKilogramsPerCubicMeter(1e-6); // Low density plasma - var confinementTime = Time.FromSeconds(1.0); // 1 second confinement + var confinementTime = Time.FromSecond(1.0); // 1 second confinement var thermalEnergy = Energy.Create( plasmaDensity.Value * plasmaVolume.Value * specificHeat.Value * electronTemp.InKelvin()); diff --git a/docs/examples/PhysicsRelationshipExamples.md b/docs/examples/PhysicsRelationshipExamples.md index 4afc45d..13cac3e 100644 --- a/docs/examples/PhysicsRelationshipExamples.md +++ b/docs/examples/PhysicsRelationshipExamples.md @@ -13,8 +13,8 @@ In physics, `Force × Length` can represent two different quantities: using ktsu.Semantics; // Create quantities -var force = Force.FromNewtons(10.0); // 10 N -var displacement = Length.FromMeters(5.0); // 5 m +var force = Force.FromNewton(10.0); // 10 N +var displacement = Length.FromMeter(5.0); // 5 m // Calculate work/energy using operator (force parallel to displacement) var workDone = force * displacement; // 50 J @@ -26,8 +26,8 @@ Console.WriteLine($"Work done: {workDone.In(Units.Joule)} J"); ### Torque Calculations (Use `CalculateTorque()` Method) ```csharp // Create quantities -var force = Force.FromNewtons(10.0); // 10 N -var momentArm = Length.FromMeters(0.5); // 0.5 m +var force = Force.FromNewton(10.0); // 10 N +var momentArm = Length.FromMeter(0.5); // 0.5 m // Calculate torque using explicit method (force perpendicular to moment arm) var torque = force.CalculateTorque(momentArm); // 5 N·m @@ -40,8 +40,8 @@ Console.WriteLine($"Torque: {torque.In(Units.NewtonMeter)} N·m"); ### Kinetic Energy (KE = ½mv²) ```csharp -var mass = Mass.FromKilograms(2.0); // 2 kg -var velocity = Velocity.FromMetersPerSecond(10.0); // 10 m/s +var mass = Mass.FromKilogram(2.0); // 2 kg +var velocity = Velocity.FromMeterPerSecond(10.0); // 10 m/s // Calculate kinetic energy using fractional constant var kineticEnergy = Energy.FromKineticEnergy(mass, velocity); // 100 J @@ -52,8 +52,8 @@ var calculatedVelocity = Energy.GetVelocityFromKineticEnergy(kineticEner ### Ideal Gas Law (PV = nRT) ```csharp -var pressure = Pressure.FromPascals(101325.0); // 1 atm -var volume = Volume.FromCubicMeters(0.0224); // 22.4 L +var pressure = Pressure.FromPascal(101325.0); // 1 atm +var volume = Volume.FromCubicMeter(0.0224); // 22.4 L var temperature = Temperature.FromKelvin(273.15); // 0°C // Calculate amount of substance using gas constant @@ -77,9 +77,9 @@ var calculatedFrequency = Frequency.FromPhotonEnergy(photonEnergy); ### Reynolds Number (Re = ρvL/μ) ```csharp var density = Density.FromKilogramsPerCubicMeter(1.225); // Air density -var velocity = Velocity.FromMetersPerSecond(10.0); // 10 m/s -var length = Length.FromMeters(1.0); // 1 m characteristic length -var viscosity = DynamicViscosity.FromPascalSeconds(1.825e-5); // Air viscosity +var velocity = Velocity.FromMeterPerSecond(10.0); // 10 m/s +var length = Length.FromMeter(1.0); // 1 m characteristic length +var viscosity = DynamicViscosity.FromPascalSecond(1.825e-5); // Air viscosity // Calculate Reynolds number var reynolds = ReynoldsNumber.FromFluidProperties(density, velocity, length, viscosity); @@ -94,7 +94,7 @@ var regime = reynolds.GetPipeFlowRegime(); // "Laminar", "Transitional", or "Tur ### Acoustic Impedance (Z = ρc) ```csharp var density = Density.FromKilogramsPerCubicMeter(1.225); // Air density -var soundSpeed = SoundSpeed.FromMetersPerSecond(343.0); // Sound speed in air +var soundSpeed = SoundSpeed.FromMeterPerSecond(343.0); // Sound speed in air // Calculate acoustic impedance var impedance = AcousticImpedance.FromDensityAndSoundSpeed(density, soundSpeed); @@ -107,8 +107,8 @@ var standardImpedance = AcousticImpedance.ForStandardAir(); ### Newton's Laws ```csharp -var mass = Mass.FromKilograms(10.0); // 10 kg -var acceleration = Acceleration.FromMetersPerSecondSquared(9.8); // 9.8 m/s² +var mass = Mass.FromKilogram(10.0); // 10 kg +var acceleration = Acceleration.FromMeterPerSecondSquared(9.8); // 9.8 m/s² // F = ma var force = mass * acceleration; // 98 N @@ -122,9 +122,9 @@ var calculatedMass = force / acceleration; // 10 kg ### Power Relationships ```csharp -var force = Force.FromNewtons(100.0); // 100 N -var velocity = Velocity.FromMetersPerSecond(5.0); // 5 m/s -var time = Time.FromSeconds(10.0); // 10 s +var force = Force.FromNewton(100.0); // 100 N +var velocity = Velocity.FromMeterPerSecond(5.0); // 5 m/s +var time = Time.FromSecond(10.0); // 10 s // P = F·v var mechanicalPower = force * velocity; // 500 W @@ -135,9 +135,9 @@ var energy = mechanicalPower * time; // 5000 J ### Electrical Relationships ```csharp -var current = ElectricCurrent.FromAmperes(2.0); // 2 A -var resistance = ElectricResistance.FromOhms(10.0); // 10 Ω -var time = Time.FromSeconds(60.0); // 1 minute +var current = ElectricCurrent.FromAmpere(2.0); // 2 A +var resistance = ElectricResistance.FromOhm(10.0); // 10 Ω +var time = Time.FromSecond(60.0); // 1 minute // V = I·R (Ohm's law) var voltage = current * resistance; // 20 V diff --git a/docs/examples/comprehensive-physics-examples.md b/docs/examples/comprehensive-physics-examples.md deleted file mode 100644 index 0519ecb..0000000 --- a/docs/examples/comprehensive-physics-examples.md +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/docs/examples/examples-index.md b/docs/examples/examples-index.md index f6ed039..b2e1ab8 100644 --- a/docs/examples/examples-index.md +++ b/docs/examples/examples-index.md @@ -47,7 +47,7 @@ If you're new to the Semantics library, start with [Getting Started](getting-sta ✅ **LINQ Support** - Natural collection operations ✅ **Performance** - Span-based operations and minimal allocations ✅ **Enterprise Integration** - ASP.NET Core and dependency injection -✅ **Physics Quantities** - 82 quantities across 8 scientific domains +✅ **Semantic Quantities** - 82 quantities across 8 scientific domains ✅ **Cross-Domain Physics** - Real-world engineering calculations ✅ **Physical Constants** - Type-safe access to fundamental constants ✅ **Unit Conversions** - Automatic dimensional analysis and safety diff --git a/docs/library-overview.md b/docs/library-overview.md deleted file mode 100644 index d8d0eb6..0000000 --- a/docs/library-overview.md +++ /dev/null @@ -1,570 +0,0 @@ -# Semantics Library - Complete Overview - -This document provides a comprehensive overview of all features and components in the ktsu.Semantics library. - -## Table of Contents - -- [Core Architecture](#core-architecture) -- [Semantic Strings](#semantic-strings) -- [Semantic Quantities](#semantic-quantities) -- [Path System](#path-system) -- [Validation System](#validation-system) -- [Factory Pattern](#factory-pattern) -- [Performance Utilities](#performance-utilities) -- [Integration Features](#integration-features) - -## Core Architecture - -The Semantics library is built around the principle of **semantic typing** - replacing primitive types with domain-specific types that carry meaning and validation. The library consists of five main components: - -1. **Semantic Strings** - Type-safe string wrappers -2. **Semantic Quantities** - Type-safe numeric values with units -3. **Path System** - Comprehensive file system path handling -4. **Validation System** - Extensive validation framework -5. **Utilities** - Performance optimization tools - -## Semantic Strings - -### Core Concept - -Semantic strings eliminate "primitive obsession" by wrapping raw strings in strongly-typed, validated containers. Each semantic string type carries its own validation rules and semantic meaning. - -### Base Classes and Interfaces - -```csharp -// Core interface -public interface ISemanticString - -// Abstract base class for all semantic strings -public abstract record SemanticString : ISemanticString - where TDerived : SemanticString - -// Factory interface for dependency injection -public interface ISemanticStringFactory where T : ISemanticString - -// Concrete factory implementation -public class SemanticStringFactory : ISemanticStringFactory -``` - -### Creating Custom Types - -```csharp -// Basic semantic string with validation -[IsEmail] -public sealed record EmailAddress : SemanticString { } - -// Multiple validation rules -[IsNotEmpty, HasLength(3, 50), RegexMatch(@"^[A-Z0-9_]+$")] -public sealed record ProductCode : SemanticString { } - -// Custom validation strategies -[ValidateAny] -[IsEmail, IsUri] -public sealed record ContactMethod : SemanticString { } -``` - -### String Compatibility - -Semantic strings maintain full compatibility with `System.String`: - -```csharp -var email = EmailAddress.FromString("user@example.com"); - -// Implicit conversion to string -string stringValue = email; - -// All string methods available -bool startsWithUser = email.StartsWith("user"); -string upperEmail = email.ToUpper(); -int length = email.Length; - -// LINQ operations -var emails = new[] { email1, email2, email3 }; -var domains = emails.Select(e => e.Split('@')[1]).ToArray(); -``` - -### Type Conversions - -The library provides safe cross-type conversions: - -```csharp -// Direct conversion with validation -var email = EmailAddress.FromString("user@example.com"); -var genericString = email.As(); - -// Factory-based conversion -var factory = new SemanticStringFactory(); -var userId = factory.Create(email.ToString()); -``` - -## Semantic Quantities - -### Purpose - -Semantic quantities provide type-safe numeric values with units, preventing unit confusion and enabling domain-specific arithmetic operations. - -### Base Classes - -```csharp -// Simple quantity storage -public record SemanticQuantity where TStorage : INumber - -// Full quantity with arithmetic operations -public record SemanticQuantity - : SemanticQuantity - where TSelf : SemanticQuantity, new() - where TStorage : INumber -``` - -### Creating Quantity Types - -```csharp -// Temperature with decimal precision -public sealed record Temperature : SemanticQuantity { } - -// Distance with double precision -public sealed record Distance : SemanticQuantity { } - -// Integer-based quantities -public sealed record Count : SemanticQuantity { } -``` - -### Arithmetic Operations - -```csharp -var temp1 = Temperature.Create(25.5m); -var temp2 = Temperature.Create(18.2m); - -// Basic arithmetic - results maintain type safety -var avgTemp = (temp1 + temp2) / 2m; // Temperature -var tempDiff = temp1 - temp2; // Temperature -var scaledTemp = temp1 * 1.5m; // Temperature - -// Division returns storage type when dividing quantities -decimal ratio = temp1 / temp2; // decimal - -// Prevents mixing incompatible units -// var invalid = temp1 + distance1; // Compiler error! -``` - -### Advanced Quantity Operations - -```csharp -// Cross-quantity calculations with explicit result types -public sealed record Area : SemanticQuantity { } -public sealed record Volume : SemanticQuantity { } - -public static Area CalculateArea(Distance length, Distance width) -{ - return SemanticQuantity.Multiply(length, width); -} - -public static Volume CalculateVolume(Area area, Distance height) -{ - return SemanticQuantity.Multiply(area, height); -} -``` - -## Path System - -### Overview - -The path system provides comprehensive file system path handling with a sophisticated polymorphic interface hierarchy. - -### Interface Hierarchy - -``` -IPath (base interface) -├── IAbsolutePath : IPath -├── IRelativePath : IPath -├── IFilePath : IPath -├── IDirectoryPath : IPath -├── IAbsoluteFilePath : IFilePath, IAbsolutePath -├── IRelativeFilePath : IFilePath, IRelativePath -├── IAbsoluteDirectoryPath : IDirectoryPath, IAbsolutePath -└── IRelativeDirectoryPath : IDirectoryPath, IRelativePath - -Separate hierarchies: -├── IFileName -└── IFileExtension -``` - -### Concrete Path Types - -```csharp -// Generic path types -public sealed record AbsolutePath : SemanticAbsolutePath, IAbsolutePath -public sealed record RelativePath : SemanticRelativePath, IRelativePath -public sealed record FilePath : SemanticFilePath, IFilePath -public sealed record DirectoryPath : SemanticDirectoryPath, IDirectoryPath - -// Specific path combinations -public sealed record AbsoluteFilePath : SemanticFilePath, IAbsoluteFilePath -public sealed record RelativeFilePath : SemanticFilePath, IRelativeFilePath -public sealed record AbsoluteDirectoryPath : SemanticDirectoryPath, IAbsoluteDirectoryPath -public sealed record RelativeDirectoryPath : SemanticDirectoryPath, IRelativeDirectoryPath - -// Path components -public sealed record FileName : SemanticString, IFileName -public sealed record FileExtension : SemanticString, IFileExtension -``` - -### Path Operations - -```csharp -var filePath = AbsoluteFilePath.FromString(@"C:\Projects\App\config.json"); - -// Rich path information -Console.WriteLine(filePath.FileName); // config.json -Console.WriteLine(filePath.FileExtension); // .json -Console.WriteLine(filePath.DirectoryPath); // C:\Projects\App -Console.WriteLine(filePath.FileNameWithoutExtension); // config - -// File system operations -Console.WriteLine(filePath.Exists); // True/False -Console.WriteLine(filePath.IsDirectory); // False -Console.WriteLine(filePath.IsFile); // True -Console.WriteLine(filePath.IsReadOnly); // True/False - -// Path manipulation -var newPath = filePath.ChangeExtension(".xml"); -var backupPath = filePath.AddSuffix("_backup"); -``` - -### Polymorphic Path Usage - -```csharp -// Method accepting any path type -public void LogPath(IPath path) -{ - Console.WriteLine($"Processing path: {path}"); -} - -// Method accepting only file paths -public void ProcessFile(IFilePath filePath) -{ - if (filePath.Exists) - { - // Process file - } -} - -// Method accepting absolute paths only -public void ProcessAbsolute(IAbsolutePath absolutePath) -{ - // Work with absolute paths -} - -// Collections of mixed path types -List allPaths = [ - AbsoluteFilePath.FromString(@"C:\data.txt"), - RelativeDirectoryPath.FromString(@"temp\logs"), - FilePath.FromString(@"config.json") -]; - -// Filter by interface -var files = allPaths.OfType().ToList(); -var directories = allPaths.OfType().ToList(); -var absolutePaths = allPaths.OfType().ToList(); -``` - -## Validation System - -### Architecture - -The validation system uses a layered approach: - -1. **Validation Attributes** - Declarative validation rules -2. **Validation Strategies** - How multiple rules are combined -3. **Validation Rules** - Individual validation logic implementations - -### Validation Categories - -#### Text Validation -- `IsEmailAddress` - RFC-compliant email validation -- `RegexMatch(pattern)` - Custom regular expression matching -- `StartsWith(prefix)` / `EndsWith(suffix)` - String prefix/suffix validation -- `Contains(substring)` - Substring presence validation -- `IsBase64` - Base64 encoding validation -- `PrefixAndSuffix(prefix, suffix)` - Combined prefix and suffix validation - -#### Format Validation -- `IsEmptyOrWhitespace` - Empty or whitespace-only strings -- `HasNonWhitespaceContent` - Must contain non-whitespace characters -- `IsSingleLine` / `IsMultiLine` - Line count validation -- `HasExactLines(count)` - Specific line count requirement -- `HasMinimumLines(min)` / `HasMaximumLines(max)` - Line count ranges - -#### First-Class Type Validation -- `IsBoolean` - Valid boolean representation (true/false, 1/0, yes/no) -- `IsDateTime` - Valid date/time parsing -- `IsDecimal` / `IsDouble` / `IsInt32` - Numeric type validation -- `IsGuid` - Valid GUID format validation -- `IsIpAddress` - IPv4/IPv6 address validation -- `IsTimeSpan` - Valid time span format -- `IsUri` - Valid URI format validation -- `IsVersion` - Version string validation (e.g., "1.2.3.4") - -#### Quantity Validation -- `IsPositive` / `IsNegative` - Sign validation for numeric values -- `IsInRange(min, max)` - Value range validation - -#### Path Validation -- `IsPath` - Valid file system path format -- `IsAbsolutePath` / `IsRelativePath` - Path type validation -- `IsFilePath` / `IsDirectoryPath` - Path category validation -- `DoesExist` - File system existence validation - -#### Casing Validation -- Various casing format validations (implementation-specific) - -### Validation Strategies - -```csharp -// All attributes must pass (default) -[ValidateAll] -[IsNotEmpty, IsEmail, HasLength(5, 100)] -public sealed record BusinessEmail : SemanticString { } - -// Any attribute can pass -[ValidateAny] -[IsEmail, IsUri] -public sealed record ContactMethod : SemanticString { } -``` - -### Custom Validation Rules - -```csharp -public class BusinessDomainRule : ValidationRuleBase -{ - public override string RuleName => "BusinessDomain"; - - protected override bool ValidateCore(SemanticStringValidationAttribute attribute, ISemanticString value) - { - if (attribute is not BusinessDomainAttribute businessAttr) - return false; - - var email = value.ToString(); - var domain = email.Split('@').LastOrDefault(); - - return businessAttr.AllowedDomains.Contains(domain); - } -} - -[BusinessDomain(AllowedDomains: ["company.com", "partner.com"])] -public sealed record CorporateEmail : SemanticString { } -``` - -## Factory Pattern - -### Purpose - -The factory pattern provides clean object creation, validation handling, and dependency injection support. - -### Factory Interface - -```csharp -public interface ISemanticStringFactory where T : ISemanticString -{ - T Create(string value); - bool TryCreate(string value, [NotNullWhen(true)] out T? result); - T CreateOrThrow(string value); -} -``` - -### Usage Patterns - -```csharp -// Basic factory usage -var factory = new SemanticStringFactory(); -var email = factory.Create("user@example.com"); - -// Safe creation with error handling -if (factory.TryCreate("invalid-email", out var result)) -{ - // Use result -} -else -{ - // Handle validation failure -} - -// Dependency injection -public class UserService -{ - private readonly ISemanticStringFactory _emailFactory; - - public UserService(ISemanticStringFactory emailFactory) - { - _emailFactory = emailFactory; - } - - public User CreateUser(string emailString) - { - var email = _emailFactory.Create(emailString); - return new User(email); - } -} -``` - -### DI Container Registration - -```csharp -// ASP.NET Core -services.AddTransient, SemanticStringFactory>(); -services.AddTransient, SemanticStringFactory>(); - -// Generic registration helper (if needed) -public static void RegisterSemanticFactory(this IServiceCollection services) - where T : class, ISemanticString -{ - services.AddTransient, SemanticStringFactory>(); -} -``` - -## Performance Utilities - -### Pooled String Builder - -Reusable StringBuilder instances for high-performance string operations: - -```csharp -// Internal utility - used automatically by the library -public class PooledStringBuilder : IDisposable -{ - public StringBuilder StringBuilder { get; } - - // Returns StringBuilder to pool when disposed - public void Dispose() { /* return to pool */ } -} -``` - -### Interned Path Strings - -Common path strings are interned to reduce memory usage: - -```csharp -// Internal utility - optimizes memory for common paths -public static class InternedPathStrings -{ - public static string GetInterned(string path) { /* implementation */ } -} -``` - -### Span-based Path Utilities - -High-performance path operations using Span: - -```csharp -// Internal utility - minimal allocation path processing -public static class SpanPathUtilities -{ - public static ReadOnlySpan GetFileName(ReadOnlySpan path) { /* implementation */ } - public static ReadOnlySpan GetExtension(ReadOnlySpan path) { /* implementation */ } - // ... other span-based operations -} -``` - -## Integration Features - -### ASP.NET Core Integration - -```csharp -// Model binding -public class CreateUserRequest -{ - public string Email { get; set; } - public string UserId { get; set; } -} - -[ApiController] -public class UserController : ControllerBase -{ - private readonly ISemanticStringFactory _emailFactory; - - [HttpPost] - public IActionResult CreateUser([FromBody] CreateUserRequest request) - { - if (!_emailFactory.TryCreate(request.Email, out var email)) - { - return BadRequest("Invalid email format"); - } - - // email is guaranteed valid - var user = new User(email); - return Ok(user); - } -} -``` - -### Entity Framework Integration - -```csharp -public class User -{ - public int Id { get; set; } - public EmailAddress Email { get; set; } - public UserId UserId { get; set; } -} - -// DbContext configuration -public class AppDbContext : DbContext -{ - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - // Semantic strings are stored as their string values - modelBuilder.Entity() - .Property(u => u.Email) - .HasConversion( - email => email.ToString(), - value => EmailAddress.FromString(value)); - - modelBuilder.Entity() - .Property(u => u.UserId) - .HasConversion( - userId => userId.ToString(), - value => UserId.FromString(value)); - } -} -``` - -### JSON Serialization - -```csharp -// Semantic strings serialize as their string values by default -var user = new User -{ - Email = EmailAddress.FromString("user@example.com"), - UserId = UserId.FromString("USER_123") -}; - -var json = JsonSerializer.Serialize(user); -// {"Email":"user@example.com","UserId":"USER_123"} - -var deserializedUser = JsonSerializer.Deserialize(json); -// Automatic conversion back to semantic types -``` - -### FluentValidation Integration - -The library provides integration with FluentValidation for complex validation scenarios: - -```csharp -public class UserValidator : AbstractValidator -{ - public UserValidator() - { - RuleFor(u => u.Email) - .Must(BeValidSemanticEmail) - .WithMessage("Invalid email format"); - } - - private bool BeValidSemanticEmail(string email) - { - var factory = new SemanticStringFactory(); - return factory.TryCreate(email, out _); - } -} -``` - -This comprehensive overview covers all major components of the Semantics library. Each component is designed to work together seamlessly while maintaining strong separation of concerns and following SOLID principles. diff --git a/docs/migration-guide-2.0.md b/docs/migration-guide-2.0.md new file mode 100644 index 0000000..7e2dfad --- /dev/null +++ b/docs/migration-guide-2.0.md @@ -0,0 +1,233 @@ +# Migrating from Semantics 1.x to 2.0 + +Semantics 2.0 replaces the hand-written physical-quantity library with a +metadata-driven source generator built around the **unified vector model**: +every quantity declares the dimensionality of its direction space in its type +(`Speed` is a magnitude, `Velocity3D` is a 3-vector), all values are stored in +SI base units, and cross-dimensional physics relationships are generated as +operators. The `Semantics.Strings` and `Semantics.Paths` packages are +unaffected apart from additions — this guide is about +`ktsu.Semantics.Quantities`. + +For the architecture behind these changes see +`docs/strategy-unified-vector-quantities.md`; for the generator workflow see +`docs/physics-generator.md`. + +## Quick checklist + +1. Update the namespace: quantities — including the audio-engineering types + (`Decibels`, `Gain`, `NormalizedParameter`, …) — moved from + `ktsu.Semantics` to `ktsu.Semantics.Quantities` (unit singletons live in + `ktsu.Semantics.Quantities.Units`). +2. Rename vector-capable quantities to their explicit form — `Force` + becomes `ForceMagnitude` (or `Force1D`/`Force2D`/`Force3D` if you + were tracking sign or direction). The full table is below. +3. Replace uses of the `Units`/`BootstrapUnits` registries and + `UnitExtensions` with the generated `From{Unit}` factories and the typed + `In(unit)` method. +4. Move logarithmic quantities (`SoundPressureLevel`, `pH`, …) to the new + self-contained companion types — they are no longer `PhysicalQuantity` + subclasses. +5. Audit any code that relies on negative magnitudes or non-SI storage — + Vector0 quantities now reject negative values, and `Concentration` / + `NuclearCrossSection` storage moved to true SI base units. + +## Namespace changes + +| 1.x | 2.0 | +|---|---| +| `ktsu.Semantics` (quantity types) | `ktsu.Semantics.Quantities` | +| `ktsu.Semantics` (unit registry) | `ktsu.Semantics.Quantities.Units` | +| `ktsu.Semantics` (audio engineering: `Decibels`, `Gain`, `Cents`, `Semitones`, `Percent`, `QFactor`, `NormalizedParameter`, `ParameterTaper`) | `ktsu.Semantics.Quantities` | +| `ktsu.Semantics.Strings`, `ktsu.Semantics.Paths` | unchanged | + +## Quantity type renames + +Quantities whose 1.x type covered both magnitude and direction are now split +by vector form. The magnitude (V0) form is always non-negative; the V1 form is +a signed scalar; V2/V3/V4 are per-component-signed vectors. + +| 1.x type | 2.0 magnitude (V0) | 2.0 signed / vector forms | +|---|---|---| +| `Time` | `Duration` | — | +| `Velocity` | `Speed` | `Velocity1D`/`2D`/`3D`/`4D` | +| `Acceleration` | `AccelerationMagnitude` | `Acceleration1D`/`2D`/`3D`/`4D` | +| `Force` | `ForceMagnitude` | `Force1D`/`2D`/`3D`/`4D` | +| `Momentum` | `MomentumMagnitude` | `Momentum1D`/`2D`/`3D`/`4D` | +| `Torque` | `TorqueMagnitude` | `Torque1D`/`3D` | +| `AngularVelocity` | `AngularSpeed` | `AngularVelocity1D`/`3D` | +| `AngularAcceleration` | `AngularAccelerationMagnitude` | `AngularAcceleration1D`/`3D` | +| `ElectricCurrent` | `CurrentMagnitude` | `Current1D`/`3D` | +| `ElectricCharge` | `ChargeMagnitude` | `Charge` (V1) | +| `ElectricPotential` | `VoltageMagnitude` | `Voltage` (V1) | +| `ElectricField` | `ElectricFieldMagnitude` | `ElectricField1D`/`2D`/`3D` | +| `ElectricResistance` | `Resistance` | — | +| `ElectricCapacitance` | `Capacitance` | — | +| `ImpedanceAC` | `Impedance` (overload of `Resistance`) | — | +| `ThermalExpansion` | `ThermalExpansionCoefficient` | — | + +`Magnitude()` on any V≥1 form returns the corresponding V0 type: +`velocity3d.Magnitude()` is a `Speed`. + +## Behavioral changes + +### Vector0 quantities are non-negative + +All V0 factories guard against negative values and throw `ArgumentException`. +This structurally enforces physical floors — Kelvin ≥ 0, non-negative +frequency, non-negative mass. A few quantities (`Wavelength`, `Period`, +`HalfLife`) additionally reject zero. + +`V0 - V0` returns the same V0 of the **absolute** difference. If you need a +signed difference, use the V1 form explicitly: + +```csharp +// 1.x: could go negative +Force diff = brake - thrust; + +// 2.0: magnitude subtraction stays non-negative… +ForceMagnitude diff = brake - thrust; // |brake − thrust| + +// …signed subtraction is the V1 form +Force1D net = thrustSigned - brakeSigned; +``` + +### Semantic overloads widen implicitly, narrow explicitly + +A `Weight` is implicitly a `ForceMagnitude`; converting back requires +`Weight.From(force)` or an explicit cast. Cross-dimensional operators are +defined on the base types and apply to overloads through widening — Ohm's law +works with an `Impedance` because it widens to `Resistance`. + +### Storage-unit fixes (silent value changes!) + +Three dimensions stored non-SI values in 1.x. If you read `.Value` directly or +serialized raw values, these are the places to audit: + +| Quantity | 1.x storage | 2.0 storage | +|---|---|---| +| `Concentration` | mol/L | mol/m³ (`FromMolar(1)` now stores `1000`) | +| `NuclearCrossSection` | barn | m² (`FromBarn(1)` now stores `1e-28`) | +| `MolarMass` via `Dalton` | 1.66×10⁻²⁷ (a per-particle mass — wrong) | 10⁻³ kg/mol (1 Da ≡ 1 g/mol) | + +`Temperature.FromFahrenheit` was inverted in early 2.0 previews and computed +the K→F transform; it now correctly maps 32 °F → 273.15 K, and `Rankine` is +supported. + +### Factory naming is the singular lemma + +`From{Unit}` factories use the singular unit name verbatim: +`Length.FromMeter(…)`, `Mass.FromKilogram(…)`, `Energy.FromBtu(…)`, +`Speed.FromKnot(…)`. Compounds are singular throughout, including the leading +noun: `Speed.FromMeterPerSecond(…)`, `Acceleration.FromMeterPerSecondSquared(…)`, +`AngularVelocity.FromRevolutionPerMinute(…)`. The rule is mechanical — +`From{Name}`, where `Name` is the unit's `name` in `units.json` — so the +generator never has to know English pluralisation. + +If you are upgrading from an earlier 2.0 preview that used plural factory +names, rename call sites by dropping the plural: `FromMeters` → `FromMeter`, +`FromMetersPerSecond` → `FromMeterPerSecond`, `FromFeet` → `FromFoot`. + +### Unit conversion is `In(unit)` + +`UnitExtensions` and `PhysicalDimensionExtensions` are gone. Conversion out of +SI storage is the dimension-typed `In` method, which fails at compile time for +a unit of the wrong dimension: + +```csharp +Length l = Length.FromMeter(10_000); +double km = l.In(Units.Kilometer); // 10.0 +double k = temperature.In(Units.Celsius); +// l.In(Units.Kilogram) — does not compile +``` + +`Units` (in `ktsu.Semantics.Quantities.Units`) exposes a generated singleton +per unit; `Unit`, `BootstrapUnit`, and `BootstrapUnits` no longer exist. + +## Logarithmic scales are generated companion types + +Decibel and pH scales don't obey linear arithmetic, so they are no longer +`PhysicalQuantity` dimensions. Each is a standalone `readonly record struct` +generated from `logarithmic.json` (with bespoke members like `PH.Neutral` in +hand-written partials), converting to and from its linear counterpart: + +| 1.x type | 2.0 companion | Linear counterpart | +|---|---|---| +| `SoundPressureLevel` | `SoundPressureLevel` | `SoundPressure` (Pa, overload of `Pressure`) | +| `SoundIntensityLevel` | `SoundIntensityLevel` | `SoundIntensity` (W/m²) | +| `SoundPowerLevel` | `SoundPowerLevel` | `SoundPower` (W, overload of `Power`) | +| `DirectionalityIndex` | `DirectionalityIndex` | intensity `Ratio` | +| `PH` | `PH` | hydrogen-ion `Concentration` | + +```csharp +SoundPressureLevel spl = + SoundPressureLevel.FromSoundPressure(SoundPressure.FromPascal(0.02)); // 60 dB +SoundPressure p = spl.ToSoundPressure(); + +PH ph = PH.FromHydrogenConcentration(Concentration.FromMolar(1e-7)); // pH 7 +``` + +Dropped with no replacement: `SoundPressureLevel.AWeighted()` (the 1.x +implementation was a no-op placeholder), `EquivalentLevel`, and the +`PH.CommonValues` novelty list (use `PH.Neutral` for 7.0). + +## Physical constants + +`PhysicalConstants` is generated from `domains.json`. Domain groupings were +renamed, and the conversion/math helper groups were removed because their +values are now baked into the generated unit factories: + +| 1.x group | 2.0 group | +|---|---| +| `Fundamental` | `Fundamental` | +| `Mechanical` | `ClassicalMechanics` | +| `Chemical` | `Chemistry` | +| `Acoustic` | `Acoustics` | +| `Optical` | `Optics` | +| `Nuclear` | `NuclearPhysics` | +| `FluidDynamics` | `FluidMechanics` | +| `Temperature`, `Time`, `Conversion`, `Mathematical` | removed — unit conversions are inside `From{Unit}`/`In(unit)`; use literals for math fractions | + +Values are stored as `PreciseNumber`; `PhysicalConstants.Generic.X()` +materializes any constant into your storage type. + +## Dimension metadata + +`PhysicalDimension` is replaced by the generated `DimensionInfo` record +(name, symbol, dimensional formula, and the quantity types belonging to it), +still exposed per-dimension on the static `PhysicalDimensions` class and per +quantity via `IPhysicalQuantity.Dimension`. Comparing quantities of +different dimensions throws `ArgumentException`; equality across dimensions is +`false`. + +## Removed APIs and their replacements + +| 1.x API | 2.0 replacement | +|---|---| +| `Units` static registry, `Unit`, `BootstrapUnit(s)` | generated `Units` singletons (`ktsu.Semantics.Quantities.Units`) | +| `UnitExtensions`, `PhysicalDimensionExtensions` | `quantity.In(unit)` | +| `PhysicalDimension` | `DimensionInfo` | +| `MolesPerSecond` unit on `ReactionRate` | none — `ReactionRate` is volumetric (mol/(m³·s)); the 1.x unit was mislabelled | +| `PhysicalConstants.Conversion.*` | conversions are baked into factories; raw factors live in `conversions.json` | +| `SoundPressureLevel.AWeighted()` / `EquivalentLevel` | none (1.x implementations were placeholders) | +| audio-engineering `Ratio` | the generated `Ratio` (Dimensionless V0; non-negative, `Ratio × Ratio` generated, same-type `/` yields the storage value) | +| audio-engineering `Percent` | the `Percent` **unit** on the Dimensionless dimension: `Ratio.FromPercent(50)` and `ratio.In(Units.Percent)` | +| audio-engineering `Gain` record struct | `Gain` is now a generated semantic overload of `Ratio` (a record class, non-negative, widens implicitly to `Ratio`); `Unity`, `Silence`, the `Decibels` conversions, and `*` live in a partial | + +## Target frameworks + +The out-of-support runtimes `net5.0`, `net6.0`, and `net7.0` were dropped — `System.Text.Json` 10 (and friends) no longer ship assets for them. `ktsu.Semantics.Quantities` now targets `net8.0`–`net10.0` (it requires `INumber`, so it has no `netstandard` target). `Semantics.Strings` and `Semantics.Paths` target `net8.0`–`net10.0` plus `netstandard2.0`/`netstandard2.1`, so consumers on older runtimes still resolve via `netstandard`. + +## What didn't change + +- `Semantics.Strings` and `Semantics.Paths` — same namespaces, same types; + 2.0 only adds validation attributes. +- The audio-engineering types added late in 1.x (`Decibels`, `Gain`, + `Cents`, `Semitones`, `NormalizedParameter`, `ParameterTaper`, `QFactor`) + keep their APIs, but move to `ktsu.Semantics.Quantities`, exchange ratios + through the generated `Ratio`, and (for the logarithmic ones) are + generated from `logarithmic.json`. `Percent` became a unit and `Gain` a + generated `Ratio` overload — see the removed-APIs table. +- Quantities remain generic over the numeric storage type + (`where T : struct, INumber`). +- `UnitSystem` and `UnitConversionException` keep their names and meaning. diff --git a/docs/physics-domains-guide.md b/docs/physics-domains-guide.md index 6e4886a..8344370 100644 --- a/docs/physics-domains-guide.md +++ b/docs/physics-domains-guide.md @@ -1,498 +1,211 @@ ---- -status: draft ---- +# Semantic Quantities by Domain -# Complete Physics Domains Guide +A user-facing tour of the semantic quantities currently declared in `Semantics.SourceGenerators/Metadata/dimensions.json` (62 dimensions, plus 83 semantic overloads). The architecture lives in `strategy-unified-vector-quantities.md`; this document just shows what's available and how it groups by physics area. -This guide provides comprehensive coverage of all physics domains in the ktsu.Semantics library, with real-world examples and advanced usage patterns. +> The dimension set evolves with the metadata. If a dimension is here but you can't find a generated type, run `dotnet build` and look under `Semantics.Quantities/Generated/Semantics.SourceGenerators/Semantics.SourceGenerators.QuantitiesGenerator/`. -## Overview +## Vector forms cheat sheet -The ktsu.Semantics library includes **80+ physics quantities** across **8 scientific domains**, each with: +| Form | Meaning | Generated type pattern | +|---|---|---| +| `IVector0` | non-negative magnitude | `Speed`, `Mass`, `Energy`, … | +| `IVector1` | signed 1D | `Velocity1D`, `Force1D`, `Temperature` | +| `IVector2` | 2D directional | `Velocity2D`, `Force2D`, `Acceleration2D` | +| `IVector3` | 3D directional | `Velocity3D`, `Force3D`, `Position3D` | +| `IVector4` | 4D directional | `Displacement4D` and similar (relativistic / spacetime) | -- Type-safe arithmetic with dimensional analysis -- Automatic unit conversions -- Physics relationships as operators -- Centralized physical constants -- Generic numeric type support (double, float, decimal) +Forms a dimension *doesn't* declare aren't generated. For example, `Mass` is V0 only; `Energy` is V0 only; `Velocity` declares all of V0..V4. -## Domain Reference +## Base SI -### 🔧 Mechanics Domain (15 Quantities) +Seven base dimensions: -**Core Quantities**: Position, Velocity, Acceleration, Force, Pressure, Energy, Power, Momentum, Torque, Angular Velocity, Angular Acceleration, Moment of Inertia, Density, Mass, Time - -#### Fundamental Relationships +- `Length` — V0..V4 with overloads `Width`, `Height`, `Depth`, `Radius`, `Diameter`, `Distance`, `Altitude`, `Wavelength`, `Thickness`, `Perimeter`, `Offset`, `Position3D`, `Translation3D`. Diameter ↔ Radius is a generated relationship. +- `Mass` — V0 with overload `AtomicMass`. +- `Time` — V0 with overloads `Duration`, `Period`, `Lifetime`, `RiseTime`, `FallTime`, `DelayTime`. +- `ElectricCurrent` — V0/V1/V3. +- `Temperature` — V0 with overloads `KineticTemperature`, `ColorTemperature`; V1 base for signed deltas. +- `AmountOfSubstance` — V0. +- `LuminousIntensity` — V0. ```csharp -// Newton's Laws -var mass = Mass.FromKilograms(10.0); -var acceleration = Acceleration.FromMetersPerSecondSquared(9.8); -var force = mass * acceleration; // F = ma (98 N) - -// Work and Energy -var distance = Length.FromMeters(5.0); -var work = force * distance; // W = F·d (490 J) -var time = Time.FromSeconds(2.0); -var power = work / time; // P = W/t (245 W) - -// Rotational Mechanics -var torque = Torque.FromNewtonMeters(50.0); -var angularVel = AngularVelocity.FromRadiansPerSecond(10.0); -var rotationalPower = torque * angularVel; // P = τω (500 W) +var height = Height.FromMeter(1.75); +var atomic = AtomicMass.FromKilogram(1.66e-27); +var lifetime = Lifetime.FromSecond(3600); +var temp = Temperature.FromKelvin(298.15); ``` -#### Real-World Examples +## Geometry and kinematics -**Automotive Engine Analysis** -```csharp -// Engine specifications -var engineTorque = Torque.FromNewtonMeters(300.0); // 300 N·m peak torque -var rpm = 3000.0; // RPM -var angularVelocity = AngularVelocity.FromRPM(rpm); - -// Calculate power output -var enginePower = engineTorque * angularVelocity; // ~94 kW -var horsePower = enginePower.ToHorsepower(); // ~126 HP - -// Vehicle dynamics -var vehicleMass = Mass.FromKilograms(1500.0); // 1.5 ton car -var acceleration = Acceleration.FromMetersPerSecondSquared(3.0); // 0-100 km/h in ~9s -var drivingForce = vehicleMass * acceleration; // 4.5 kN -``` +- `Area` — V0 with overloads `CrossSection`, `SurfaceArea`. +- `Volume` — V0 with overload `MolarVolume`. +- `Velocity` / `Acceleration` / `Jerk` / `Snap` — full V0..V4. +- `Frequency` — V0 with overloads `SamplingRate`, `BitRate`, `RefreshRate`, `RotationalSpeed`. -**Structural Engineering** ```csharp -// Beam loading analysis -var beamLength = Length.FromMeters(6.0); -var distributedLoad = Force.FromNewtonsPerMeter(5000.0); // 5 kN/m -var totalLoad = distributedLoad * beamLength; // 30 kN - -// Material properties -var steelDensity = Density.FromKilogramsPerCubicMeter(7850.0); -var beamVolume = Volume.FromCubicMeters(0.02); // 20L beam volume -var beamWeight = steelDensity * beamVolume * PhysicalConstants.Generic.StandardGravity(); +// V3 vectors use object-initializer construction (X / Y / Z components) +var velocity3d = new Velocity3D { X = 3.0, Y = 4.0, Z = 0.0 }; +var speed = velocity3d.Magnitude(); // 5.0 (Speed) +var sampling = SamplingRate.FromHertz(48_000); ``` -### ⚡ Electrical Domain (11 Quantities) - -**Core Quantities**: Electric Potential, Electric Current, Electric Resistance, Electric Charge, Electric Power Density, Electric Field, Electric Flux, Electric Capacitance, Permittivity, Electric Conductivity, Impedance AC +## Angular mechanics -#### Fundamental Relationships +- `AngularDisplacement` — V0/V1/V3 with overloads (`Phase`, `RotationAngle`, `Heading`, `Pitch`, `Roll`, `Yaw`). +- `AngularVelocity`, `AngularAcceleration`, `AngularJerk` — V0/V1/V3. +- `Torque` — V0/V1/V3. +- `AngularMomentum` — V0/V1/V3. +- `MomentOfInertia` — V0. ```csharp -// Ohm's Law -var voltage = ElectricPotential.FromVolts(12.0); -var resistance = ElectricResistance.FromOhms(6.0); -var current = voltage / resistance; // I = V/R (2 A) - -// Power relationships -var electricalPower = voltage * current; // P = VI (24 W) -var resistivePower = current.Squared() * resistance; // P = I²R (24 W) - -// Energy and charge -var time = Time.FromHours(1.0); -var energy = electricalPower * time; // E = Pt (24 Wh) -var charge = current * time; // Q = It (2 Ah) +var heading = Heading.FromRadian(Math.PI / 2); // V0 overload of AngularDisplacement +var omega3d = AngularVelocity3D.FromRadianPerSecond(0.0, 0.0, 1.5); ``` -#### Real-World Examples +## Dynamics -**Solar Panel System Design** -```csharp -// Solar panel specifications -var panelVoltage = ElectricPotential.FromVolts(36.0); // Open circuit voltage -var panelCurrent = ElectricCurrent.FromAmperes(8.5); // Short circuit current -var maxPowerPoint = panelVoltage * panelCurrent * 0.78; // ~238W (fill factor) - -// System sizing -var dailyEnergyNeed = Energy.FromKilowattHours(25.0); // 25 kWh/day -var sunHours = Time.FromHours(5.0); // 5 hours peak sun -var requiredPower = dailyEnergyNeed / sunHours; // 5 kW needed -var panelsNeeded = Math.Ceiling(requiredPower.Value / maxPowerPoint.Value); // 21 panels -``` +- `Force` — V0..V4 with overloads (`Weight`, `Drag`, `Thrust`, `Lift`, `Friction`, `Tension`, `Compression`, `Normal`, `Shear`). +- `Momentum` — V0..V4. +- `Pressure` — V0 with overloads (`AtmosphericPressure`, `GaugePressure`, `AbsolutePressure`, `VaporPressure`, `OsmoticPressure`, `Stress`). +- `Energy` — V0 with overloads (`KineticEnergy`, `PotentialEnergy`, `ThermalEnergy`, `ChemicalEnergy`, `WorkDone`). +- `Power` — V0 with overload `RadiantPower`. +- `Density` — V0. -**Motor Control Analysis** ```csharp -// 3-phase motor analysis -var lineVoltage = ElectricPotential.FromVolts(400.0); // 400V 3-phase -var motorPower = Power.FromKilowatts(15.0); // 15 kW motor -var efficiency = 0.92; // 92% efficient -var powerFactor = 0.85; // 0.85 PF - -var inputPower = Power.Create(motorPower.Value / efficiency); // 16.3 kW -var lineCurrent = inputPower / (lineVoltage * Math.Sqrt(3) * powerFactor); // ~28A per phase +var weight = Weight.From(ForceMagnitude.FromNewton(686.0)); +var drag = Drag.FromNewton(20.0); +var pe = PotentialEnergy.FromJoule(500.0); ``` -### 🌡️ Thermal Domain (10 Quantities) +## Thermal -**Core Quantities**: Temperature, Heat, Entropy, Thermal Conductivity, Heat Capacity, Specific Heat, Heat Transfer Coefficient, Thermal Expansion, Thermal Diffusivity, Thermal Resistance +- `Entropy` — V0 with overload `MolarEntropy`. +- `SpecificHeat` — V0 with overload `MolarHeatCapacity`. +- `ThermalConductivity` — V0. +- `HeatTransferCoefficient` — V0. +- `ThermalExpansion` — V0. -#### Fundamental Relationships +Heat itself is currently expressed via `Energy` (and its `ThermalEnergy` overload) rather than a separate dimension. -```csharp -// Heat transfer -var deltaT = Temperature.FromKelvin(50.0); // Temperature difference -var heatCapacity = HeatCapacity.FromJoulesPerKelvin(1000.0); // Thermal mass -var heatRequired = Heat.Create(heatCapacity.Value * deltaT.Value); // Q = CΔT - -// Conduction -var thermalConductivity = ThermalConductivity.FromWattsPerMeterKelvin(50.0); -var thickness = Length.FromMeters(0.1); -var area = Area.FromSquareMeters(2.0); -var heatTransferRate = (thermalConductivity * area * deltaT) / thickness; // Fourier's law -``` - -#### Real-World Examples +## Electrical and magnetic -**HVAC System Design** -```csharp -// Building thermal analysis -var roomVolume = Volume.FromCubicMeters(150.0); // 150 m³ room -var airDensity = Density.FromKilogramsPerCubicMeter(1.2); // Air density -var specificHeatAir = SpecificHeat.FromJoulesPerKilogramKelvin(1005.0); // Cp air - -var roomMass = roomVolume * airDensity; // Air mass in room -var indoorTemp = Temperature.FromCelsius(22.0); // Target temperature -var outdoorTemp = Temperature.FromCelsius(35.0); // Hot day -var tempDifference = outdoorTemp - indoorTemp; // 13°C difference - -// Cooling load calculation -var coolingPower = Power.Create( - roomMass.Value * specificHeatAir.Value * tempDifference.Value / 3600.0); // W -``` +- `ElectricCharge` — V0/V1. +- `ElectricPotential` — V0/V1 with overloads (`Voltage`, `EMF`). +- `ElectricField` — V0/V1/V2/V3. +- `ElectricResistance`, `ElectricConductance` (overload `Admittance`), `ElectricCapacitance`, `Inductance` — V0. +- `MagneticFluxDensity` — V0/V3. +- `MagneticFlux` — V0. -**Industrial Heat Exchanger** ```csharp -// Heat exchanger effectiveness -var hotFluidTemp = Temperature.FromCelsius(150.0); // Hot fluid in -var coldFluidTemp = Temperature.FromCelsius(20.0); // Cold fluid in -var massFlowRate = MassFlowRate.FromKilogramsPerSecond(2.0); // Fluid flow - -var waterSpecificHeat = SpecificHeat.FromJoulesPerKilogramKelvin(4186.0); -var maxHeatTransfer = massFlowRate * waterSpecificHeat * (hotFluidTemp - coldFluidTemp); - -// With 80% effectiveness -var actualHeatTransfer = Heat.Create(maxHeatTransfer.Value * 0.8); -var hotFluidOutTemp = Temperature.Create( - hotFluidTemp.Value - (actualHeatTransfer.Value / (massFlowRate.Value * waterSpecificHeat.Value))); +var v = Voltage.FromVolt(12.0); +var i = ElectricCurrent.FromAmpere(2.0); +var r = v / i; // ElectricResistance +var p = v * i; // Power ``` -### 🧪 Chemical Domain (10 Quantities) - -**Core Quantities**: Amount of Substance, Molarity, Molality, Molar Mass, Reaction Rate, Rate Constant, Activation Energy, pH, Solubility, Enzyme Activity - -#### Fundamental Relationships - -```csharp -// Concentration calculations -var moles = AmountOfSubstance.FromMoles(0.5); -var solutionVolume = Volume.FromLiters(2.0); -var molarity = moles / solutionVolume; // M = n/V (0.25 M) - -// Reaction kinetics -var rateConstant = RateConstant.FromPerSecond(0.1); // 1st order reaction -var concentration = Molarity.FromMolar(0.5); // Initial concentration -var reactionRate = rateConstant * concentration; // Rate = k[A] -``` - -#### Real-World Examples - -**Pharmaceutical Manufacturing** -```csharp -// Drug synthesis calculation -var molecularWeight = MolarMass.FromGramsPerMole(180.16); // Glucose MW -var targetYield = Mass.FromGrams(50.0); // 50g target -var requiredMoles = AmountOfSubstance.Create(targetYield.Value / molecularWeight.Value); - -// Reaction stoichiometry (2:1 ratio) -var startingMaterial = AmountOfSubstance.Create(requiredMoles.Value * 2.0); -var startingMass = Mass.Create(startingMaterial.Value * 150.0); // SM MW = 150 - -// Process conditions -var reactionTemp = Temperature.FromCelsius(80.0); -var activationEnergy = ActivationEnergy.Create(75000.0); // 75 kJ/mol -var reactionRate = RateConstant.FromArrheniusEquation(1e10, activationEnergy, reactionTemp); -``` - -**Water Treatment Analysis** -```csharp -// pH calculation and adjustment -var waterPH = pH.FrompH(8.2); // Alkaline water -var targetPH = pH.FrompH(7.0); // Neutral target -var pHDifference = waterPH - targetPH; // 1.2 pH units - -// Buffer capacity -var waterVolume = Volume.FromLiters(1000.0); // 1000L tank -var bufferCapacity = HeatCapacity.FromJoulesPerKelvin(50.0); // Buffer strength -var acidNeeded = AmountOfSubstance.Create( - Math.Pow(10, -targetPH.Value) * waterVolume.Value); // Simplified calculation -``` - -### 🔊 Acoustic Domain (20 Quantities) - -**Core Quantities**: Frequency, Wavelength, Sound Pressure, Sound Intensity, Sound Power, Acoustic Impedance, Sound Speed, Sound Absorption, Reverberation Time, plus 11 specialized acoustic metrics +## Optical and radiometric -#### Fundamental Relationships +- `LuminousFlux` — V0. +- `Illuminance` — V0 with overload `Luminance`. +- `OpticalPower` — V0. +- `Irradiance` — V0 with overloads `RadiantExitance`, `Radiance`, `RadiantIntensity`. ```csharp -// Wave relationships -var frequency = Frequency.FromHertz(1000.0); // 1 kHz tone -var soundSpeed = SoundSpeed.FromMetersPerSecond(343.0); // Speed in air -var wavelength = soundSpeed / frequency; // λ = v/f (0.343 m) - -// Acoustic power and intensity -var soundPower = SoundPower.FromWatts(0.001); // 1 mW source -var sphericalArea = Area.FromSquareMeters(4 * Math.PI * 100); // 10m radius sphere -var intensity = soundPower / sphericalArea; // I = P/A +var flux = LuminousFlux.FromLumen(800.0); +var lux = flux / Area.FromSquareMeter(4.0); // Illuminance ``` -#### Real-World Examples +## Acoustic -**Audio System Design** -```csharp -// Speaker system analysis -var speakerPower = Power.FromWatts(100.0); // 100W speaker -var efficiency = 0.02; // 2% efficiency (typical) -var acousticPower = SoundPower.Create(speakerPower.Value * efficiency); // 2W acoustic - -// Room acoustics -var roomVolume = Volume.FromCubicMeters(200.0); // 200 m³ room -var absorptionCoeff = SoundAbsorption.Create(0.3); // 30% absorption -var revTime = ReverbertainTime.FromSabineEquation(roomVolume, absorptionCoeff); - -// Sound level at distance -var distance = Length.FromMeters(3.0); // 3m from speaker -var sphericalSurface = Area.Create(4 * Math.PI * Math.Pow(distance.Value, 2)); -var soundIntensity = acousticPower / sphericalSurface; -var soundPressureLevel = SoundPressureLevel.FromIntensity(soundIntensity); -``` +- `AcousticImpedance` — V0. +- Most acoustic quantities reuse other dimensions: `Frequency` (with `SamplingRate`/`BitRate` overloads), `Pressure` for sound pressure, `Power` for acoustic power, `Length` for `Wavelength`, etc. -**Noise Control Engineering** ```csharp -// Industrial noise assessment -var machinePower = SoundPower.FromWatts(0.01); // 10 mW industrial machine -var workerDistance = Length.FromMeters(2.0); // 2m from machine -var roomConstant = 50.0; // Room acoustic constant - -// Near field vs far field -var nearFieldIntensity = machinePower / (Area.Create(4 * Math.PI * Math.Pow(workerDistance.Value, 2))); -var reverberantIntensity = SoundIntensity.Create(4 * machinePower.Value / roomConstant); -var totalIntensity = SoundIntensity.Create(nearFieldIntensity.Value + reverberantIntensity.Value); - -var exposureLevel = SoundPressureLevel.FromIntensity(totalIntensity); -var safetyMargin = 85.0 - exposureLevel.Value; // OSHA 85 dB limit +var f = Frequency.FromHertz(440.0); // A4 note +var T = f.Period(); // Time ``` -### ☢️ Nuclear Domain (5 Quantities) +## Fluid dynamics -**Core Quantities**: Radioactive Activity, Absorbed Dose, Equivalent Dose, Exposure, Nuclear Cross Section +- `KinematicViscosity` — V0 with overload `MomentumDiffusivity`. +- `DynamicViscosity` — V0. +- `VolumetricFlowRate` — V0. +- `MassFlowRate` — V0. +- `SurfaceTension` — V0. -#### Real-World Examples +`ReynoldsNumber`, `MachNumber`, `SpecificGravity`, `RefractiveIndex` are V0 overloads of `Dimensionless`. -**Medical Radiotherapy** -```csharp -// Radiation treatment planning -var sourceActivity = RadioactiveActivity.FromBecquerels(3.7e10); // 1 Ci Cs-137 source -var treatmentTime = Time.FromMinutes(10.0); // 10 minute treatment -var patientDistance = Length.FromMeters(1.0); // 1m from source - -// Dose calculation (simplified) -var exposureRate = Exposure.Create(sourceActivity.Value / (4 * Math.PI * Math.Pow(patientDistance.Value, 2))); -var totalExposure = Exposure.Create(exposureRate.Value * treatmentTime.Value); -var absorbedDose = AbsorbedDose.FromExposure(totalExposure); - -// Safety verification -var doseLimit = EquivalentDose.FromSieverts(0.02); // 20 mSv annual limit -var equivalentDose = EquivalentDose.Create(absorbedDose.Value * 1.0); // Quality factor = 1 for gamma -var safetyFactor = doseLimit.Value / equivalentDose.Value; -``` +## Chemical -### 💡 Optical Domain (6 Quantities) +- `Concentration` — V0. +- `MolarMass` — V0. +- `CatalyticActivity` — V0 with overload `EnzymeActivity`. +- `ReactionRate` — V0. +- `MolarEnergy` — V0 with overloads `ActivationEnergy`, `EnthalpyOfReaction`. -**Core Quantities**: Luminous Intensity, Luminous Flux, Illuminance, Luminance, Refractive Index, Optical Power - -#### Real-World Examples - -**Lighting Design** ```csharp -// LED lighting calculation -var ledFlux = LuminousFlux.FromLumens(3000.0); // 3000 lm LED array -var roomArea = Area.FromSquareMeters(25.0); // 25 m² room -var averageIlluminance = ledFlux / roomArea; // 120 lux average - -// Task lighting requirements -var requiredIlluminance = Illuminance.FromLux(500.0); // 500 lux for reading -var additionalFlux = LuminousFlux.Create( - (requiredIlluminance.Value - averageIlluminance.Value) * roomArea.Value); // Additional 9500 lm needed +var n = AmountOfSubstance.FromMole(0.5); +var V = Volume.FromCubicMeter(0.002); // 2 L +var M = n / V; // Concentration ``` -### 🌊 Fluid Dynamics Domain (5 Quantities) +## Nuclear and radiation -**Core Quantities**: Kinematic Viscosity, Bulk Modulus, Volumetric Flow Rate, Mass Flow Rate, Reynolds Number +- `RadioactiveActivity` — V0. +- `AbsorbedDose` — V0. +- `EquivalentDose` — V0. +- `Exposure` — V0. +- `NuclearCrossSection` — V0. -#### Real-World Examples +## Dimensionless -**Pipeline Design** -```csharp -// Water pipeline analysis -var pipeFlow = VolumetricFlowRate.FromLitersPerSecond(50.0); // 50 L/s flow -var pipeDiameter = Length.FromMeters(0.2); // 20 cm diameter -var waterViscosity = KinematicViscosity.ForWater(); // Water at 20°C - -var velocity = Velocity.Create(pipeFlow.Value / (Math.PI * Math.Pow(pipeDiameter.Value/2, 2))); -var reynolds = ReynoldsNumber.Calculate(velocity, pipeDiameter, waterViscosity); -var flowRegime = reynolds.GetPipeFlowRegime(); // "Turbulent" expected - -// Pressure drop calculation -var frictionFactor = reynolds.CalculateDarcyFriction(); -var pipeLength = Length.FromMeters(1000.0); // 1 km pipeline -var pressureDrop = Pressure.Create( - frictionFactor * (pipeLength.Value / pipeDiameter.Value) * - (waterDensity.Value * Math.Pow(velocity.Value, 2) / 2)); -``` +- `Dimensionless` — V0 base `Ratio` (with overloads `RefractiveIndex`, `ReynoldsNumber`, `SpecificGravity`, `MachNumber`); V1 base `SignedRatio` for signed quantities such as differential ratios. -## Advanced Integration Patterns +## Common patterns -### Multi-Domain Calculations +### Magnitude extraction -**Complete System Analysis** ```csharp -// Hydroelectric power plant analysis -public class HydroPlantAnalysis -{ - public Power CalculatePowerOutput( - VolumetricFlowRate waterFlow, - Length headHeight, - double turbineEfficiency = 0.9, - double generatorEfficiency = 0.95) - { - // Mechanical power from water - var waterDensity = Density.FromKilogramsPerCubicMeter(1000.0); - var gravity = PhysicalConstants.Generic.StandardGravity(); - - var massFlow = waterDensity * waterFlow; - var hydraulicPower = Power.Create( - massFlow.Value * gravity * headHeight.Value); - - // Electrical power output - var mechanicalPower = Power.Create(hydraulicPower.Value * turbineEfficiency); - var electricalPower = Power.Create(mechanicalPower.Value * generatorEfficiency); - - return electricalPower; - } -} - -// Usage -var plant = new HydroPlantAnalysis(); -var flow = VolumetricFlowRate.FromCubicMetersPerSecond(100.0); // 100 m³/s -var head = Length.FromMeters(50.0); // 50 m head -var output = plant.CalculatePowerOutput(flow, head); // ~41.6 MW +Velocity3D v = new() { X = 3.0, Y = 4.0, Z = 0.0 }; +Speed s = v.Magnitude(); // 5.0, always >= 0 ``` -### Performance Optimization Patterns +### Cross product (V3 only) -**Batch Processing** ```csharp -// Process multiple quantities efficiently -public static List> CalculateKineticEnergies( - List<(double mass, double velocity)> objects) -{ - return objects.Select(obj => { - var mass = Mass.FromKilograms(obj.mass); - var velocity = Velocity.FromMetersPerSecond(obj.velocity); - return Energy.FromKineticEnergy(mass, velocity); - }).ToList(); -} +Force3D F = new() { X = 0.0, Y = 10.0, Z = 0.0 }; +Displacement3D r = new() { X = 0.5, Y = 0.0, Z = 0.0 }; +Torque3D τ = F.Cross(r); ``` -### Unit Testing Patterns +### Dot product -**Physics Relationship Verification** ```csharp -[TestMethod] -public void VerifyEnergyConservation() -{ - // Initial kinetic energy - var mass = Mass.FromKilograms(10.0); - var velocity = Velocity.FromMetersPerSecond(20.0); - var kineticEnergy = Energy.FromKineticEnergy(mass, velocity); - - // Work done by friction - var frictionForce = Force.FromNewtons(50.0); - var distance = Length.FromMeters(30.0); - var workDone = frictionForce * distance; - - // Final energy should equal initial minus work - var finalEnergy = kineticEnergy - workDone; - var expectedFinalVelocity = Math.Sqrt(2 * finalEnergy.Value / mass.Value); - - Assert.AreEqual(10.0, expectedFinalVelocity, 0.01, "Energy conservation violated"); -} +Force3D F = new() { X = 10.0, Y = 0.0, Z = 0.0 }; +Displacement3D r = new() { X = 2.0, Y = 0.0, Z = 0.0 }; +Energy work = F.Dot(r); // 20 J ``` -## Best Practices +### Semantic overload narrowing -### 1. Use Type-Safe Constants ```csharp -// Good: Type-safe constant access -var gravity = PhysicalConstants.Generic.StandardGravity(); - -// Bad: Hard-coded values -var gravity = 9.80665; // No type safety, source unclear +ForceMagnitude raw = ForceMagnitude.FromNewton(686.0); +Weight weight = Weight.From(raw); // explicit narrow +ForceMagnitude back = weight; // implicit widen ``` -### 2. Leverage Dimensional Analysis -```csharp -// The compiler prevents dimensional errors -var force = mass * acceleration; // ✅ Dimensionally correct -// var invalid = mass + acceleration; // ❌ Compiler error -``` +### Physical constants in expressions -### 3. Use Appropriate Numeric Types ```csharp -// High precision calculations -var precise = Temperature.FromCelsius(25.000001m); - -// Performance-critical code -var fast = Temperature.FromCelsius(25.0f); +var R = PhysicalConstants.Generic.GasConstant(); // J/(mol·K) +var n = AmountOfSubstance.FromMole(1.0); +var T = Temperature.FromKelvin(273.15); +var P = Pressure.FromPascal(101_325.0); -// General purpose -var general = Temperature.FromCelsius(25.0); +// PV = nRT → V = nRT / P +// (constants flow into operators because everything stores SI base units) ``` -### 4. Handle Unit Conversions Explicitly -```csharp -// Clear intent with explicit conversions -var tempF = temperature.ToFahrenheit(); -var energyKwh = energy.ToKilowattHours(); - -// Use In() method for custom units -var pressureBar = pressure.In(Units.Bar); -``` - -## Migration Guide - -### From Other Libraries - -**From UnitsNet** -```csharp -// UnitsNet -var length = Length.FromMeters(5.0); -var area = length * length; - -// ktsu.Semantics -var length = Length.FromMeters(5.0); -var area = length * length; // Returns Area -``` - -**From Quantities** -```csharp -// Quantities -var force = new Force(100, Unit.Newton); - -// ktsu.Semantics -var force = Force.FromNewtons(100.0); -``` +## Adding a new dimension or relationship -This comprehensive guide demonstrates the full capabilities of the ktsu.Semantics physics domains, providing the foundation for scientific computing, engineering applications, and educational tools. +Edit `Semantics.SourceGenerators/Metadata/dimensions.json` and rebuild. The full schema and a worked example are in `physics-generator.md`. diff --git a/docs/physics-generator.md b/docs/physics-generator.md new file mode 100644 index 0000000..1feec2b --- /dev/null +++ b/docs/physics-generator.md @@ -0,0 +1,177 @@ +# 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`), 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` storage types the library opts into (`double`, `float`, `decimal`, …). | +| `PhysicalConstantsGenerator` | `PhysicalConstants.g.cs` | `PhysicalConstants.Generic.X()` and `PhysicalConstants.Conversion.X()` 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 struct`s with linear-quantity conversions, log-space arithmetic, and comparisons. | + +Outputs land under `Semantics.Quantities/Generated/Semantics.SourceGenerators//`. 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: + +```jsonc +{ + "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 : Length`. +- 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//`. +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[]`: + ```json + { "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`: + ```json + { "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 struct`s +around `scale = multiplier · log_base(linear / reference)`: + +```jsonc +{ + "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})` 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 +(`Cents`↔`Semitones`), 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/` | diff --git a/docs/strategy-unified-vector-quantities.md b/docs/strategy-unified-vector-quantities.md new file mode 100644 index 0000000..973e16e --- /dev/null +++ b/docs/strategy-unified-vector-quantities.md @@ -0,0 +1,1500 @@ +# Strategy: Unified Vector Representation for Quantities + +## Motivation + +The current quantity system draws a hard line between "scalar" quantities and "vector" quantities, treating them as fundamentally different types. This creates several problems: + +1. **Redundant type hierarchies** - Scalar quantities and vector quantities share the same dimensional relationships but have separate code paths for generation, operators, and conversions. +2. **Ambiguous "scalar" semantics** - The word "scalar" conflates two distinct concepts: a magnitude-only value (always non-negative, like speed) and a signed one-dimensional value (like velocity along a single axis). +3. **No clean magnitude extraction** - There's no unified way to go from a directional quantity to its magnitude. The relationship between `Velocity` (scalar) and `VelocityVector3` is implicit, not structural. +4. **Dimensional inconsistency** - A `Dimensionless` scalar and a magnitude-only quantity are represented identically, even though they have different physical meaning. + +## Core Idea + +Represent all quantities as vectors, eliminating the scalar/vector distinction. The dimensionality of the direction space becomes a property of the quantity rather than a categorical difference. + +| Type | Direction Dimensions | Value Space | Sign | Examples | +|------|---------------------|-------------|------|----------| +| **Vector0** | 0 (none) | Non-negative magnitude | Unsigned | Speed, Distance, Mass, Energy, Area, Volume | +| **Vector1** | 1 | Signed scalar | Signed | Displacement (1D), Velocity (1D), Force (1D), Temperature delta, Electric charge | +| **VectorN** | N (2, 3, 4...) | N-component | Per-component signed | Position, Velocity, Acceleration, Force in 2D/3D | + +### Key Insight + +This framing makes the distinction between **magnitude-only** and **signed dimensionless** quantities explicit at the type level: + +- `Speed` is a `Vector0` - a magnitude with no directional component, always >= 0 +- `Velocity1D` is a `Vector1` - a signed value along one axis, can be negative +- `Velocity3D` is a `Vector3` - three signed components + +The magnitude of any VectorN is always a Vector0 of the same physical dimension. This relationship is structural, not coincidental. + +## Type Hierarchy + +``` +IQuantity // Root: all quantities +├── IVector0 // Magnitude only (unsigned, >= 0) +│ ├── Speed +│ ├── Distance +│ ├── Mass +│ ├── Energy +│ ├── Area +│ └── ... +├── IVector1 // Signed 1D quantity +│ ├── Displacement1D +│ ├── Velocity1D +│ ├── Force1D +│ ├── Temperature // Signed because deltas can be negative +│ └── ... +├── IVector2 // 2D directional +│ ├── Position2D +│ ├── Velocity2D +│ └── ... +├── IVector3 // 3D directional +│ ├── Position3D +│ ├── Velocity3D +│ └── ... +└── IVector4 // 4D directional + └── ... +``` + +### Form Coverage Matrix + +Not every dimension declares every form. The coverage is intentional and follows three patterns; new dimensions should pick the matching one: + +| Dimension family | Forms | Rationale | +|---|---|---| +| Linear motion: `Length`, `Velocity`, `Acceleration`, `Jerk`, `Snap`, `Force`, `Momentum`, `Displacement`, `ElectricField` | V0–V4 | Pure linear vectors; every dimensionality is meaningful. | +| Angular motion: `AngularDisplacement`, `AngularVelocity`, `AngularAcceleration`, `AngularJerk`, `Torque`, `AngularMomentum` | V0, V1, V3 | Pseudovectors. 2D / 4D forms aren't physically meaningful; skip them. | +| Electric current: `ElectricCurrent` | V0, V1, V3 | Same as angular — no 2D current. | +| Scalar-only: `Mass`, `Energy`, `Area`, `Volume`, `Time`, `Frequency`, `RadioactiveActivity`, `MomentOfInertia`, etc. | V0 (sometimes + V1) | Inherently scalar. Add V1 only when a signed *delta* makes physical sense (e.g. `Temperature` (V0) + `TemperatureDelta` (V1), `ElectricCharge` is V1 because charge is signed). | +| Dimensionless / angular ratios: `Dimensionless` | V0 (`Ratio`) + V1 (`SignedRatio`) | Lets ratios that semantically must be non-negative (e.g. `MachNumber`, `RefractiveIndex`) be V0 overloads of `Ratio`, and signed ratios use `SignedRatio`. | + +When you declare a relationship (`integrals` / `derivatives` / `dotProducts` / `crossProducts`) the generator only emits operators for forms that exist on **both** sides — e.g. a dot product between two dimensions that both declare V3 emits the V3 dot product, while V2 is silently skipped if either side is missing. Keep this in mind when adding a new dimension: if you need it to participate in a particular relationship form, it must declare that form. + +## Semantic Relationships + +### Magnitude Extraction + +Every VectorN (N >= 1) has a corresponding Vector0 that represents its magnitude: + +```csharp +Velocity3D v = Velocity3D.Create(3.0, 4.0, 0.0); +Speed s = v.Magnitude(); // returns Speed with value 5.0 + +Force1D f = Force1D.FromNewton(-9.8); +ForceMagnitude m = f.Magnitude(); // returns 9.8 (always non-negative) +``` + +This is a fundamental structural relationship, not a convenience method. The magnitude of a vector quantity is always a well-defined quantity of the same physical dimension. + +### Vector0: Non-negative Magnitude + +Vector0 quantities enforce non-negativity as a type-level invariant: + +```csharp +Speed s = Speed.FromMeterPerSecond(5.0); // OK +Speed s = Speed.FromMeterPerSecond(-1.0); // throws: magnitude cannot be negative + +// Arithmetic respects the invariant +Speed a = Speed.FromMeterPerSecond(3.0); +Speed b = Speed.FromMeterPerSecond(5.0); +Speed sum = a + b; // 8.0 - OK +Speed diff = b - a; // 2.0 - OK; subtraction returns the same V0 of |a - b| +``` + +**Decision (locked)**: `V0 - V0` returns the same V0 of `T.Abs(a - b)`. Magnitude subtraction stays a magnitude — non-negative, same dimension, same overload type. If the consumer needs the signed difference, they must convert to the V1 form explicitly: + +```csharp +Velocity1D aSigned = Velocity1D.FromMeterPerSecond(a.Value); +Velocity1D bSigned = Velocity1D.FromMeterPerSecond(b.Value); +Velocity1D signed = aSigned - bSigned; // -2.0 +``` + +This rule is enforced by the generator; tests assert it for every V0 dimension. + +### Vector1: Signed One-Dimensional + +Vector1 quantities are signed values along a single axis: + +```csharp +Velocity1D v = Velocity1D.FromMeterPerSecond(-3.0); // negative = moving backward +Speed s = v.Magnitude(); // 3.0, always non-negative + +// Negation is natural +Velocity1D reversed = -v; // +3.0 +``` + +### VectorN: Multi-dimensional Direction + +VectorN quantities (N >= 2) have N components, each signed: + +```csharp +Velocity3D v = Velocity3D.Create(3.0, 4.0, 0.0); +Speed s = v.Magnitude(); // 5.0 +Velocity3D n = v.Normalize(); // (0.6, 0.8, 0.0) +``` + +## Cross-Dimensional Relationships + +This is the core of the type system: when you multiply or divide quantities of different physical dimensions, the result must be the correct semantic type at the correct vector form. + +### Three Kinds of Cross-Dimensional Operations + +**1. Scalar operations** (`*`, `/`) - one or both operands are V0: + +``` +V0 * V0 → V0 Speed * Duration = Length +V0 * VN → VN Duration * Acceleration3D = Velocity3D +VN * V0 → VN Velocity3D * Duration = Displacement3D +VN / V0 → VN Displacement3D / Duration = Velocity3D +V0 / V0 → V0 Length / Duration = Speed +``` + +**2. Dot product** (`.Dot()`) - two VN operands of the same N, result is always V0: + +``` +VN · VN → V0 Force3D · Displacement3D = Energy + Velocity3D · Velocity3D = Speed² (not a named type) +``` + +**3. Cross product** (`.Cross()`) - two V3 operands, result is V3: + +``` +V3 × V3 → V3 Displacement3D × Force3D = Torque3D + AngularVelocity3D × Displacement3D = Velocity3D +``` + +### Vector Form Propagation Rules + +When the source generator encounters a dimensional relationship like `Velocity * Time = Displacement`, it must generate operators for every valid combination of vector forms across both operands. + +**Rule: The result vector form equals the highest vector form among the operands, provided the result dimension supports that form.** + +| Self form | Other form | Result form | Condition | Example | +|-----------|-----------|-------------|-----------|---------| +| V0 | V0 | V0 | Always valid | `Speed * Duration = Length` | +| V1 | V0 | V1 | Result has V1 | `Velocity1D * Duration = Displacement1D` | +| V0 | V1 | V1 | Result has V1 | `Duration * Velocity1D = Displacement1D` | +| V2 | V0 | V2 | Result has V2 | `Velocity2D * Duration = Displacement2D` | +| V0 | V2 | V2 | Result has V2 | `Duration * Velocity2D = Displacement2D` | +| V3 | V0 | V3 | Result has V3 | `Velocity3D * Duration = Displacement3D` | +| V0 | V3 | V3 | Result has V3 | `Duration * Velocity3D = Displacement3D` | +| V4 | V0 | V4 | Result has V4 | `Velocity4D * Duration = Displacement4D` | +| V0 | V4 | V4 | Result has V4 | `Duration * Velocity4D = Displacement4D` | +| VN | VN | - | **Not a `*` operator** | Use `.Dot()` or `.Cross()` instead | + +**If the result dimension does not have the required vector form, the operator is not generated.** For example, `Force * Length = Energy` only generates `ForceMagnitude * Length = Energy` (V0 * V0 = V0), because Energy is scalar-only. `Force3D * Length` is not Energy3D - it's a scaled Force3D (same-dimension scalar multiplication, not a cross-dimensional relationship). + +### Relationship Types in dimensions.json + +The current `integrals` and `derivatives` lists are supplemented with `dotProducts` and `crossProducts`: + +```json +{ + "name": "Force", + "symbol": "M L T⁻²", + "dimensionalFormula": { "mass": 1, "length": 1, "time": -2 }, + "quantities": { + "vector0": { "base": "ForceMagnitude" }, + "vector1": { "base": "Force1D" }, + "vector2": { "base": "Force2D" }, + "vector3": { "base": "Force3D" }, + "vector4": { "base": "Force4D" } + }, + "integrals": [ + { "other": "Length", "result": "Energy" }, + { "other": "Time", "result": "Momentum" } + ], + "derivatives": [], + "dotProducts": [ + { "other": "Length", "result": "Energy" } + ], + "crossProducts": [ + { "other": "Length", "result": "Torque" } + ] +} +``` + +**How the generator uses each relationship type:** + +- **`integrals`** (`Self * Other = Result`): Generates `*` operators following the vector form propagation rules above. Only V0-V0 and VN-V0 combinations. +- **`derivatives`** (`Self / Other = Result`): Generates `/` operators following the same propagation rules. +- **`dotProducts`** (`Self · Other = Result`): Generates `.Dot()` methods on VN types (N >= 1) where both self and other have that VN form. Result is always V0 of the result dimension. +- **`crossProducts`** (`Self × Other = Result`): Generates `.Cross()` methods only on V3 types where both self and other have V3 forms. Result is V3 of the result dimension. + +Each entry may also declare an explicit `forms` array (e.g. `{ "other": "Length", "result": "Torque", "forms": [3] }`). When set, the generator only emits operators at those forms; if a listed form is missing on any participating dimension, it surfaces as the `SEM003` diagnostic instead of being silently dropped. When `forms` is omitted, the generator falls back to per-relationship defaults: `[0, 1, 2, 3, 4]` for integrals/derivatives, `[1, 2, 3, 4]` for dot products, `[3]` for cross products. + +### Complete Example: Velocity Dimension + +Given: + +```json +{ + "name": "Velocity", + "quantities": { + "vector0": { "base": "Speed" }, + "vector1": { "base": "Velocity1D" }, + "vector2": { "base": "Velocity2D" }, + "vector3": { "base": "Velocity3D" }, + "vector4": { "base": "Velocity4D" } + }, + "integrals": [ + { "other": "Time", "result": "Length" } + ], + "derivatives": [ + { "other": "Time", "result": "Acceleration" } + ] +} +``` + +The generator produces these `*` operators (from `integrals`): + +```csharp +// V0 * V0 → V0 +public static Length operator *(Speed left, Duration right); +public static Length operator *(Duration left, Speed right); + +// V1 * V0 → V1 (Length has V1 = Displacement1D) +public static Displacement1D operator *(Velocity1D left, Duration right); +public static Displacement1D operator *(Duration left, Velocity1D right); + +// V2 * V0 → V2 (Length has V2 = Displacement2D) +public static Displacement2D operator *(Velocity2D left, Duration right); +public static Displacement2D operator *(Duration left, Velocity2D right); + +// V3 * V0 → V3 (Length has V3 = Displacement3D) +public static Displacement3D operator *(Velocity3D left, Duration right); +public static Displacement3D operator *(Duration left, Velocity3D right); + +// V4 * V0 → V4 (Length has V4 = Displacement4D) +public static Displacement4D operator *(Velocity4D left, Duration right); +public static Displacement4D operator *(Duration left, Velocity4D right); +``` + +And these `/` operators (from `derivatives`): + +```csharp +// V0 / V0 → V0 +public static AccelerationMagnitude operator /(Speed left, Duration right); + +// V1 / V0 → V1 +public static Acceleration1D operator /(Velocity1D left, Duration right); + +// V2 / V0 → V2 +public static Acceleration2D operator /(Velocity2D left, Duration right); + +// V3 / V0 → V3 +public static Acceleration3D operator /(Velocity3D left, Duration right); + +// V4 / V0 → V4 +public static Acceleration4D operator /(Velocity4D left, Duration right); +``` + +### Complete Example: Force Dimension (with dot/cross) + +Given: + +```json +{ + "name": "Force", + "quantities": { + "vector0": { "base": "ForceMagnitude" }, + "vector1": { "base": "Force1D" }, + "vector3": { "base": "Force3D" } + }, + "integrals": [ + { "other": "Length", "result": "Energy" }, + { "other": "Time", "result": "Momentum" } + ], + "dotProducts": [ + { "other": "Length", "result": "Energy" } + ], + "crossProducts": [ + { "other": "Length", "result": "Torque" } + ] +} +``` + +**Scalar operators from `integrals`:** + +```csharp +// Force * Length = Energy (V0 * V0 → V0 only, because Energy is scalar-only) +public static Energy operator *(ForceMagnitude left, Length right); +public static Energy operator *(Length left, ForceMagnitude right); + +// Force * Time = Momentum (propagates across all matching forms) +public static MomentumMagnitude operator *(ForceMagnitude left, Duration right); // V0*V0→V0 +public static Momentum1D operator *(Force1D left, Duration right); // V1*V0→V1 +public static Momentum3D operator *(Force3D left, Duration right); // V3*V0→V3 +// (plus commutative versions) +``` + +Note: `Force1D * Length` does NOT generate an Energy1D operator because Energy has no V1 form. That combination is simply not available as a `*` operator. + +**Dot product methods from `dotProducts`:** + +```csharp +// Force · Length = Energy (dot product: VN · VN → V0) +public Energy Force1D.Dot(Displacement1D other); // V1·V1 → V0 +public Energy Force3D.Dot(Displacement3D other); // V3·V3 → V0 +``` + +This is how `Force3D * Displacement3D = Energy` is expressed: not as a `*` operator (which would be ambiguous) but as an explicit `.Dot()` call that always returns V0. + +**Cross product methods from `crossProducts`:** + +```csharp +// Force × Length = Torque (cross product: V3 × V3 → V3) +public Torque3D Force3D.Cross(Displacement3D other); +``` + +### How Semantic Overloads Participate + +Semantic overloads participate in cross-dimensional operations through implicit widening to their base type. The resolution chain: + +```csharp +// User writes: +Weight w = Weight.FromNewton(9.8); // V0 overload of ForceMagnitude +Height h = Height.FromMeter(10.0); // V0 overload of Length +Energy e = w * h; // How does this work? + +// Resolution: +// 1. Weight implicitly converts to ForceMagnitude (base) +// 2. Height implicitly converts to Length (base) +// 3. ForceMagnitude * Length = Energy (generated operator) +// 4. Result: Energy +``` + +```csharp +// Vector overloads: +WeightVector wv = WeightVector.Create(0, -9.8, 0); // V3 overload of Force3D +Displacement3D d = Displacement3D.Create(5, 0, 0); + +// Dot product: +Energy e = wv.Dot(d); +// 1. WeightVector widens to Force3D +// 2. Force3D.Dot(Displacement3D) = Energy +// 3. Result: Energy + +// Cross product: +Torque3D t = wv.Cross(d); +// 1. WeightVector widens to Force3D +// 2. Force3D.Cross(Displacement3D) = Torque3D +// 3. Result: Torque3D +``` + +**The result of a cross-dimensional operation is always a base type**, never a semantic overload. This is consistent with rule 5 from the semantic overloads section. If the user wants to assert the result is a specific overload, they use explicit narrowing: + +```csharp +Energy e = w * h; // base type result +Work work = Work.From(e); // explicit narrowing to semantic overload +// or: Work work = (Work)(w * h); // cast syntax +``` + +### Inverse Relationships + +Every integral generates a corresponding derivative, and vice versa. The generator automatically creates the inverse operators: + +``` +If: A * B = C (integral) +Then: C / B = A (auto-generated derivative) +And: C / A = B (auto-generated derivative) +``` + +Example: `Force * Time = Momentum` generates: + +```csharp +// Forward (integral): +MomentumMagnitude = ForceMagnitude * Duration; +Momentum3D = Force3D * Duration; + +// Inverse (auto-derived): +ForceMagnitude = MomentumMagnitude / Duration; +Force3D = Momentum3D / Duration; +Duration = MomentumMagnitude / ForceMagnitude; +// (Duration is V0-only, so the VN/VN case uses scalar division:) +// Momentum3D / Force3D is NOT generated as a Duration operator. +// Instead: Duration = Momentum3D.Magnitude() / Force3D.Magnitude() +``` + +**VN / VN across different dimensions is never generated as a `/` operator** because the result would be ambiguous (component-wise division is not physically meaningful). Users must either: + +- Use magnitudes: `Duration d = velocity3D.Magnitude() / acceleration3D.Magnitude()` +- Use dot product where appropriate +- Work at V0 level + +### Same-Dimension Operators vs Cross-Dimension Operators + +To avoid confusion, there are two distinct categories of operators: + +**Same-dimension operators** (always present, defined on the type itself): + +```csharp +// Addition/subtraction (same type) +Speed + Speed → Speed +Velocity3D + Velocity3D → Velocity3D + +// Scalar multiplication/division (by raw T, not another quantity) +Velocity3D * T → Velocity3D +Speed / T → Speed + +// Same-type division → raw T +Speed / Speed → T +``` + +**Cross-dimension operators** (generated from relationships): + +```csharp +// Integral: Velocity * Time → Displacement +Speed * Duration → Length +Velocity3D * Duration → Displacement3D + +// Derivative: Velocity / Time → Acceleration +Speed / Duration → AccelerationMagnitude +Velocity3D / Duration → Acceleration3D + +// Dot product: +Force3D.Dot(Displacement3D) → Energy + +// Cross product: +Force3D.Cross(Displacement3D) → Torque3D +``` + +These are distinct because same-dimension `*` by `T` is always available, while cross-dimension `*` by another quantity type is only available when a relationship is declared. + +### Rules for Operator Dimensionality (Summary) + +| Operation | Left | Right | Result form | Generated from | +|-----------|------|-------|-------------|----------------| +| `*` | V0(A) | V0(B) | V0(C) | `integrals` | +| `*` | VN(A) | V0(B) | VN(C) if C has VN | `integrals` | +| `*` | V0(A) | VN(B) | VN(C) if C has VN | `integrals` (commutative) | +| `/` | VN(A) | V0(B) | VN(C) if C has VN | `derivatives` | +| `/` | V0(A) | V0(B) | V0(C) | `derivatives` | +| `.Dot()` | VN(A) | VN(B) | V0(C) | `dotProducts` | +| `.Cross()` | V3(A) | V3(B) | V3(C) | `crossProducts` | +| `+` / `-` | VN(A) | VN(A) | VN(A) | Same dimension | +| `*` / `/` | VN(A) | T | VN(A) | Same dimension (scalar) | +| `Magnitude()` | VN(A) | - | V0(A) | Structural | + +## Definitive dimensions.json Schema + +This section consolidates all the schema changes into a single reference. The current per-quantity `"scalar"` / `"vectors"` booleans and per-quantity `"integrals"` / `"derivatives"` are replaced with a dimension-level structure. + +### Schema Definition + +```json +{ + "physicalDimensions": [ + { + "name": "string", + "symbol": "string", + "dimensionalFormula": { "length": 0, "mass": 0, "time": 0, "...": 0 }, + "availableUnits": ["UnitName", "..."], + "quantities": { + "vector0": { + "base": "BaseTypeName", + "overloads": [ + { + "name": "OverloadTypeName", + "description": "string", + "relationships": { "toOther": "expression", "...": "..." } + } + ] + }, + "vector1": { "base": "...", "overloads": [] }, + "vector2": { "base": "...", "overloads": [] }, + "vector3": { "base": "...", "overloads": [] }, + "vector4": { "base": "...", "overloads": [] } + }, + "integrals": [ + { "other": "DimensionName", "result": "DimensionName" } + ], + "derivatives": [ + { "other": "DimensionName", "result": "DimensionName" } + ], + "dotProducts": [ + { "other": "DimensionName", "result": "DimensionName" } + ], + "crossProducts": [ + { "other": "DimensionName", "result": "DimensionName" } + ] + } + ] +} +``` + +**Key changes from current format:** + +1. **`availableUnits` moves to dimension level** - all vector forms of the same dimension share units. +2. **`quantities` becomes a map of vector forms** - each form has a base type name and optional overloads. +3. **`integrals`/`derivatives` move to dimension level** - they reference dimension names, not quantity names. The source generator resolves the correct base type at each vector form. +4. **`dotProducts`/`crossProducts` are new** - separate from scalar `integrals` because they have different vector form propagation rules. +5. **Only present vector forms are generated** - if `vector2` is absent, no V2 type is generated for that dimension. + +### Full Example: Velocity Dimension + +```json +{ + "name": "Velocity", + "symbol": "L T⁻¹", + "dimensionalFormula": { "length": 1, "time": -1 }, + "availableUnits": ["MeterPerSecond", "KilometerPerHour", "MilePerHour"], + "quantities": { + "vector0": { "base": "Speed" }, + "vector1": { "base": "Velocity1D" }, + "vector2": { "base": "Velocity2D" }, + "vector3": { "base": "Velocity3D" }, + "vector4": { "base": "Velocity4D" } + }, + "integrals": [ + { "other": "Time", "result": "Length" } + ], + "derivatives": [ + { "other": "Time", "result": "Acceleration" } + ], + "dotProducts": [], + "crossProducts": [] +} +``` + +### Full Example: Force Dimension (with dot/cross) + +```json +{ + "name": "Force", + "symbol": "M L T⁻²", + "dimensionalFormula": { "mass": 1, "length": 1, "time": -2 }, + "availableUnits": ["Newton"], + "quantities": { + "vector0": { + "base": "ForceMagnitude", + "overloads": [ + { "name": "Weight", "description": "Gravitational force magnitude." }, + { "name": "Thrust", "description": "Propulsive force magnitude." }, + { "name": "Drag", "description": "Resistive force magnitude." }, + { "name": "Lift", "description": "Force magnitude perpendicular to flow." }, + { "name": "Tension", "description": "Pulling force along a line." } + ] + }, + "vector1": { "base": "Force1D" }, + "vector2": { "base": "Force2D" }, + "vector3": { + "base": "Force3D", + "overloads": [ + { "name": "WeightVector", "description": "Gravitational force vector." } + ] + }, + "vector4": { "base": "Force4D" } + }, + "integrals": [ + { "other": "Length", "result": "Energy" }, + { "other": "Time", "result": "Momentum" } + ], + "derivatives": [], + "dotProducts": [ + { "other": "Length", "result": "Energy" } + ], + "crossProducts": [ + { "other": "Length", "result": "Torque" } + ] +} +``` + +### Full Example: Energy Dimension (scalar-only) + +```json +{ + "name": "Energy", + "symbol": "M L² T⁻²", + "dimensionalFormula": { "mass": 1, "length": 2, "time": -2 }, + "availableUnits": ["Joule", "ElectronVolt", "Calorie", "KilowattHour"], + "quantities": { + "vector0": { + "base": "Energy", + "overloads": [ + { "name": "Work", "description": "Energy transferred by a force." }, + { "name": "Heat", "description": "Energy transferred due to temperature difference." }, + { "name": "KineticEnergy", "description": "Energy of motion." }, + { "name": "PotentialEnergy", "description": "Energy of position or configuration." } + ] + } + }, + "integrals": [], + "derivatives": [ + { "other": "Time", "result": "Power" }, + { "other": "Length", "result": "Force" } + ], + "dotProducts": [], + "crossProducts": [] +} +``` + +### Full Example: Length Dimension (with overloads) + +```json +{ + "name": "Length", + "symbol": "L", + "dimensionalFormula": { "length": 1 }, + "availableUnits": [ + "Meter", "Kilometer", "Centimeter", "Millimeter", + "Micrometer", "Nanometer", "Angstrom", + "Foot", "Inch", "Yard", "Mile" + ], + "quantities": { + "vector0": { + "base": "Length", + "overloads": [ + { "name": "Width", "description": "Horizontal extent." }, + { "name": "Height", "description": "Vertical extent." }, + { "name": "Depth", "description": "Extent into a surface." }, + { "name": "Radius", "description": "Distance from center to edge." }, + { + "name": "Diameter", + "description": "Distance across through center.", + "relationships": { "toRadius": "Value / 2", "fromRadius": "Value * 2" } + }, + { "name": "Distance", "description": "Separation between two points." }, + { "name": "Altitude", "description": "Height above reference level." }, + { "name": "Wavelength", "description": "Spatial period of a wave." }, + { "name": "Thickness", "description": "Extent through thinnest dimension." }, + { "name": "Perimeter", "description": "Boundary length of a 2D shape." }, + { + "name": "Circumference", + "description": "Perimeter of a circle.", + "relationships": { "toRadius": "Value / (2 * pi)", "toDiameter": "Value / pi" } + } + ] + }, + "vector1": { + "base": "Displacement1D", + "overloads": [ + { "name": "Offset", "description": "Signed distance from reference." } + ] + }, + "vector2": { "base": "Displacement2D" }, + "vector3": { + "base": "Displacement3D", + "overloads": [ + { "name": "Position3D", "description": "Location in 3D space." }, + { "name": "Translation3D", "description": "Movement applied in 3D." } + ] + }, + "vector4": { "base": "Displacement4D" } + }, + "integrals": [ + { "other": "Length", "result": "Area" } + ], + "derivatives": [], + "dotProducts": [], + "crossProducts": [] +} +``` + +## Source Generator Specification + +This section defines exactly what the `QuantitiesGenerator` produces for each element in the schema. + +### Generated Output Per Dimension + +For each dimension in `physicalDimensions`, the generator produces: + +#### 1. Base Types (one per vector form) + +For each entry in `quantities` (vector0 through vector4): + +```csharp +// vector0 → implements IVector0 +public record Speed : IVector0, T> + where T : struct, INumber +{ + public T Value { get; init; } + + // Static members + public static Speed Zero => new() { Value = T.Zero }; + + // Factory methods (from availableUnits) + public static Speed FromMeterPerSecond(T value) => new() { Value = value }; + public static Speed FromKilometerPerHour(T value) => new() { Value = value * ... }; + public static Speed FromMilePerHour(T value) => new() { Value = value * ... }; + + // Same-dimension arithmetic + public static Speed operator +(Speed left, Speed right) => ...; + public static Speed operator *(Speed left, T right) => ...; + public static Speed operator *(T left, Speed right) => ...; + public static Speed operator /(Speed left, T right) => ...; + public static T operator /(Speed left, Speed right) => ...; + + // Cross-dimension operators (from integrals/derivatives + propagation rules) + // See "Cross-Dimension Operator Generation" below +} +``` + +```csharp +// vector1 → implements IVector1 +public record Velocity1D : IVector1, T> + where T : struct, INumber +{ + public T Value { get; init; } + public static Velocity1D Zero => ...; + + // Factory methods (shared units) + public static Velocity1D FromMeterPerSecond(T value) => ...; + + // Same-dimension arithmetic (includes subtraction and negation) + public static Velocity1D operator +(Velocity1D left, Velocity1D right) => ...; + public static Velocity1D operator -(Velocity1D left, Velocity1D right) => ...; + public static Velocity1D operator -(Velocity1D value) => ...; + public static Velocity1D operator *(Velocity1D left, T right) => ...; + + // Magnitude extraction → returns V0 base of same dimension + public Speed Magnitude() => Speed.FromMeterPerSecond(T.Abs(Value)); + + // Dot product methods (from dotProducts) + // Cross-dimension operators (from integrals/derivatives) +} +``` + +```csharp +// vector3 → implements IVector3 +public record Velocity3D : IVector3, T> + where T : struct, INumber +{ + public T X { get; init; } + public T Y { get; init; } + public T Z { get; init; } + + public static Velocity3D Zero => ...; + public static Velocity3D UnitX => ...; + public static Velocity3D UnitY => ...; + public static Velocity3D UnitZ => ...; + + // Factory + public static Velocity3D Create(T x, T y, T z) => ...; + + // IVector3 methods + public Speed Magnitude() => ...; // Returns V0 base, NOT raw T + public T LengthSquared() => ...; + public Velocity3D Normalize() => ...; + public T Dot(Velocity3D other) => ...; // Same-type dot → raw T + + // Typed dot products (from dotProducts, returns V0 of result dimension) + // Typed cross products (from crossProducts, returns V3 of result dimension) + // Cross-dimension operators +} +``` + +#### 2. Semantic Overload Types (one per overload entry) + +For each overload in a vector form's `overloads` array: + +```csharp +public record Width : IVector0, T> + where T : struct, INumber +{ + public T Value { get; init; } + public static Width Zero => ...; + + // Factory methods (same units as base) + public static Width FromMeter(T value) => ...; + + // Same-overload arithmetic (preserves Width type) + public static Width operator +(Width left, Width right) => ...; + public static Width operator *(Width left, T right) => ...; + + // Implicit widening to base + public static implicit operator Length(Width w) => new() { Value = w.Value }; + + // Explicit narrowing from base + public static explicit operator Width(Length l) => new() { Value = l.Value }; + public static Width From(Length l) => new() { Value = l.Value }; + + // Relationship methods (if declared) + // e.g., Diameter has: public Radius ToRadius() => ...; + + // NO cross-dimension operators (participates via implicit widening) +} +``` + +#### 3. Cross-Dimension Operator Generation + +For each `integrals` entry `{ "other": "B", "result": "C" }` on dimension A: + +``` +For each vector form VN that A has: + Let otherForm = V0 of B (the "other" operand in VN * V0) + Let resultForm = VN of C (propagated form) + If C has VN: + Generate: VN(A) * V0(B) → VN(C) // forward + Generate: V0(B) * VN(A) → VN(C) // commutative + Generate: VN(C) / V0(B) → VN(A) // inverse + Generate: VN(C) / VN(A) → V0(B) // inverse (only if result is V0) +``` + +For each `derivatives` entry `{ "other": "B", "result": "C" }` on dimension A: + +``` +For each vector form VN that A has: + If C has VN: + Generate: VN(A) / V0(B) → VN(C) // forward + Generate: VN(C) * V0(B) → VN(A) // inverse integral + Generate: V0(B) * VN(C) → VN(A) // commutative inverse +``` + +For each `dotProducts` entry `{ "other": "B", "result": "C" }` on dimension A: + +``` +For each vector form VN (N >= 1) that both A and B have: + Generate: VN(A).Dot(VN(B)) → V0(C) // typed dot product method +``` + +For each `crossProducts` entry `{ "other": "B", "result": "C" }` on dimension A: + +``` +If A, B, and C all have V3: + Generate: V3(A).Cross(V3(B)) → V3(C) // typed cross product method +``` + +#### 4. Operator Deduplication + +Since inverse relationships are auto-generated, the same operator could be declared from both sides. For example: + +- Velocity declares integral `{ "other": "Time", "result": "Length" }` → generates `Speed * Duration = Length` +- Length declares derivative `{ "other": "Time", "result": "Velocity" }` → generates `Length / Duration = Speed` + +The generator must deduplicate: each unique operator signature is generated exactly once, regardless of how many relationship declarations produce it. + +### IVector0 Interface + +```csharp +public interface IVector0 + where TSelf : IVector0 + where T : struct, INumber +{ + /// Gets the magnitude value (always non-negative). + T Value { get; } + + /// Gets a quantity with value zero. + static abstract TSelf Zero { get; } + + // Same-type arithmetic + static abstract TSelf operator +(TSelf left, TSelf right); + static abstract TSelf operator *(TSelf quantity, T scalar); + static abstract TSelf operator *(T scalar, TSelf quantity); + static abstract TSelf operator /(TSelf quantity, T scalar); + static abstract T operator /(TSelf left, TSelf right); +} +``` + +### IVector1 Interface + +```csharp +public interface IVector1 + where TSelf : IVector1 + where T : struct, INumber +{ + /// Gets the signed value along the single axis. + T Value { get; } + + /// Gets a quantity with value zero. + static abstract TSelf Zero { get; } + + // Same-type arithmetic + static abstract TSelf operator +(TSelf left, TSelf right); + static abstract TSelf operator -(TSelf left, TSelf right); + static abstract TSelf operator *(TSelf quantity, T scalar); + static abstract TSelf operator *(T scalar, TSelf quantity); + static abstract TSelf operator /(TSelf quantity, T scalar); + static abstract T operator /(TSelf left, TSelf right); + static abstract TSelf operator -(TSelf value); // Negation +} +``` + +### IVector2, IVector3, IVector4 Interfaces + +These already exist in the codebase. The key change is that `Length()` is renamed/supplemented with a typed `Magnitude()` method that returns the V0 base type of the same dimension, rather than a raw `T`. The raw `T` length is still available for generic math but the typed version is preferred for dimensional safety. + +```csharp +// Addition to existing IVector3 interface +public interface IVector3 + where TVector : IVector3 + where T : struct, INumber +{ + // ... existing members ... + + // Existing: returns raw T (kept for generic math) + public T Length(); + + // New: typed magnitude returning V0 base of the same dimension + // (generated per-type, not on the interface, because return type varies) +} +``` + +The typed `Magnitude()` method cannot be on the interface itself because its return type differs per dimension (Speed, ForceMagnitude, etc.). It is generated directly on each concrete vector type. + +## Semantic Overloads + +### The Problem + +A single physical dimension at a single vector form often has multiple meaningful names depending on context. These aren't different physical quantities - they share the same dimension, units, and arithmetic - but they carry distinct semantic intent. + +**Length dimension, Vector0 (magnitude):** + +- Length, Width, Height, Depth, Thickness +- Radius, Diameter +- Distance, Range +- Altitude, Elevation +- Wavelength, Stride, Span +- Perimeter, Circumference + +**Length dimension, Vector1 (signed 1D):** + +- Displacement, Offset, Shift + +**Length dimension, Vector3 (3D):** + +- Position, Location +- Translation, Displacement +- Normal (direction, unit vector context) + +**Velocity dimension, Vector0:** + +- Speed, FlowRate (contextual) + +**Velocity dimension, Vector3:** + +- Velocity, WindVelocity, CurrentVelocity + +**Force dimension, Vector0:** + +- Weight, Thrust, Drag, Lift, Tension + +**Force dimension, Vector3:** + +- Force, Weight (directional), Thrust (directional) + +These semantic overloads exist across nearly every physical dimension. Ignoring them produces code where every length is just `Length`, losing domain context. But treating them as fully independent types creates a combinatorial explosion of operator definitions. + +### Design: Semantic Overloads as Typed Aliases With a Shared Base + +Each physical dimension + vector form combination has one **base quantity type** (the canonical type). Semantic overloads are distinct types that share the same physical behavior but carry their own name and optional validation. + +``` +Length (base - Vector0 of Length dimension) +├── Width (semantic overload) +├── Height (semantic overload) +├── Depth (semantic overload) +├── Radius (semantic overload) +├── Distance (semantic overload) +├── Altitude (semantic overload) +└── Wavelength(semantic overload) +``` + +#### Key Rules + +**1. Overloads implicitly convert to their base type (widening).** + +An overload IS-A base. A `Width` is always a valid `Length`. + +```csharp +Width w = Width.FromMeter(5.0); +Length l = w; // implicit - every Width is a Length +``` + +**2. Base types require explicit conversion to an overload (narrowing).** + +Not every Length is a Width. The user asserts the semantic meaning. + +```csharp +Length l = Length.FromMeter(5.0); +Width w = l.As>(); // explicit - asserting "this length is a width" +// or: +Width w = Width.From(l); // factory-style explicit conversion +``` + +**3. Operations between overloads of the same base return the base type.** + +When you mix different semantic names of the same dimension, the result loses the specific semantic and falls back to the base. + +```csharp +Width w = Width.FromMeter(3.0); +Height h = Height.FromMeter(4.0); +Length sum = w + h; // Width + Height = Length (base type) + +Width w2 = Width.FromMeter(2.0); +Width sum2 = w + w2; // Width + Width = Width (same overload preserved) +``` + +**4. Operations within the same overload preserve the overload type.** + +```csharp +Width w1 = Width.FromMeter(3.0); +Width w2 = Width.FromMeter(2.0); +Width sum = w1 + w2; // Width + Width = Width +Width scaled = w1 * 2; // Width * scalar = Width +``` + +**5. Cross-dimensional operations use the base types for their results.** + +```csharp +Width w = Width.FromMeter(3.0); +Height h = Height.FromMeter(4.0); +Area a = w * h; // Width * Height = Area (dimensional relationship uses bases) +``` + +**6. Overloads can add validation beyond the base.** + +```csharp +// Radius enforces non-negative (already guaranteed by Vector0) but Diameter +// has a structural relationship: +Diameter d = Diameter.FromMeter(10.0); +Radius r = d.ToRadius(); // r.Value == 5.0 (semantic conversion with logic) +``` + +### Implementation Approach + +Semantic overloads are generated as thin wrapper records around the base type: + +```csharp +// Generated base type +public record Length : IVector0, T> + where T : struct, INumber +{ + public T Value { get; init; } + // ... full operator suite, factory methods, etc. +} + +// Generated semantic overload +public record Width : IVector0, T> + where T : struct, INumber +{ + public T Value { get; init; } + + // Implicit widening to base + public static implicit operator Length(Width w) => Length.Create(w.Value); + + // Explicit narrowing from base + public static explicit operator Width(Length l) => new() { Value = l.Value }; + + // Same operators as base, but returning Width for same-type operations + public static Width operator +(Width left, Width right) + => new() { Value = left.Value + right.Value }; + + // Mixed-overload operations return the base type (handled via implicit conversion) +} +``` + +### Semantic Overloads in dimensions.json + +Semantic overloads are defined within the `vectorForms` structure: + +```json +{ + "name": "Length", + "symbol": "L", + "dimensionalFormula": { "length": 1 }, + "quantities": { + "vector0": { + "base": "Length", + "overloads": [ + { + "name": "Width", + "description": "Horizontal extent of an object or space." + }, + { + "name": "Height", + "description": "Vertical extent of an object or space." + }, + { + "name": "Depth", + "description": "Extent into a surface or volume, perpendicular to its face." + }, + { + "name": "Radius", + "description": "Distance from the center to the edge of a circle or sphere." + }, + { + "name": "Diameter", + "description": "Distance across a circle or sphere through its center.", + "relationships": { + "toRadius": "Value / 2", + "fromRadius": "Value * 2" + } + }, + { + "name": "Distance", + "description": "Separation between two points in space." + }, + { + "name": "Altitude", + "description": "Height above a reference level, typically sea level." + }, + { + "name": "Wavelength", + "description": "Spatial period of a periodic wave." + }, + { + "name": "Thickness", + "description": "Extent of an object through its thinnest dimension." + }, + { + "name": "Perimeter", + "description": "Total length of the boundary of a 2D shape." + }, + { + "name": "Circumference", + "description": "Perimeter of a circle.", + "relationships": { + "toRadius": "Value / (2 * pi)", + "toDiameter": "Value / pi" + } + } + ] + }, + "vector1": { + "base": "Displacement1D", + "overloads": [ + { + "name": "Offset", + "description": "Signed distance from a reference point along one axis." + } + ] + }, + "vector3": { + "base": "Position3D", + "overloads": [ + { + "name": "Displacement3D", + "description": "Change in position in 3D space." + }, + { + "name": "Translation3D", + "description": "Movement applied to an object in 3D space." + } + ] + } + } +} +``` + +### Another Example: Force + +```json +{ + "name": "Force", + "symbol": "M L T⁻²", + "dimensionalFormula": { "mass": 1, "length": 1, "time": -2 }, + "quantities": { + "vector0": { + "base": "ForceMagnitude", + "overloads": [ + { "name": "Weight", "description": "Gravitational force on an object." }, + { "name": "Thrust", "description": "Propulsive force." }, + { "name": "Drag", "description": "Resistive force opposing motion through a medium." }, + { "name": "Lift", "description": "Force perpendicular to flow direction." }, + { "name": "Tension", "description": "Pulling force along a rope, cable, or similar." } + ] + }, + "vector1": { + "base": "Force1D" + }, + "vector3": { + "base": "Force3D", + "overloads": [ + { "name": "WeightVector", "description": "Gravitational force vector (typically -Y or -Z)." } + ] + } + } +} +``` + +### Source Generator Impact + +The source generator treats overloads as lightweight types: + +1. **Base types** get the full operator suite, factory methods, unit conversions, and cross-dimensional operators (integrals/derivatives). +2. **Overload types** get: + - Same-type arithmetic operators (returning the overload type) + - Implicit conversion to the base type + - Explicit conversion from the base type + - Factory methods for the shared units + - Any declared `relationships` as conversion methods +3. **Cross-dimensional operators** are only generated between base types. Overloads participate via implicit conversion to their base. + +This keeps the generated code manageable: if a dimension has 10 overloads and participates in 5 cross-dimensional relationships, we generate 5 operator sets (not 50). + +### Comprehensive Semantic Overload Catalog + +Below is a non-exhaustive list of overloads to illustrate the breadth: + +| Dimension | Vector Form | Base | Overloads | +|-----------|-------------|------|-----------| +| Length | V0 | Length | Width, Height, Depth, Radius, Diameter, Distance, Altitude, Elevation, Wavelength, Thickness, Perimeter, Circumference, Stride, Span, Range, Focal Length | +| Length | V1 | Displacement1D | Offset, Shift | +| Length | V3 | Position3D | Displacement3D, Translation3D, Location3D | +| Velocity | V0 | Speed | FlowSpeed, WindSpeed, GroundSpeed, Airspeed | +| Velocity | V1 | Velocity1D | - | +| Velocity | V3 | Velocity3D | WindVelocity3D, CurrentVelocity3D | +| Force | V0 | ForceMagnitude | Weight, Thrust, Drag, Lift, Tension, NormalForce, Friction, SpringForce | +| Force | V3 | Force3D | WeightVector, ThrustVector | +| Acceleration | V0 | AccelerationMagnitude | GravitationalAcceleration | +| Acceleration | V3 | Acceleration3D | GravitationalField3D | +| Pressure | V0 | Pressure | Stress, AtmosphericPressure, GaugePressure, OsmoticPressure | +| Energy | V0 | Energy | Work, Heat, KineticEnergy, PotentialEnergy, ThermalEnergy, ElectricalEnergy | +| Power | V0 | Power | Wattage, Luminosity (radiant), HeatFlowRate | +| Mass | V0 | Mass | AtomicMass, MolarMass (different units but same dimension) | +| Time | V0 | Duration | Period, HalfLife, TimeConstant, Latency | +| Temperature | V0 | Temperature | - | +| Temperature | V1 | TemperatureDelta | TemperatureRise, TemperatureDrop | +| Angle | V0 | AngleMagnitude | FieldOfView, ApertureAngle | +| Angle | V1 | Angle | Rotation, Phase, Bearing, Heading | +| Area | V0 | Area | SurfaceArea, CrossSectionalArea, Footprint | +| Volume | V0 | Volume | Capacity, Displacement (engine) | +| Frequency | V0 | Frequency | SamplingRate, ClockSpeed, Bandwidth | +| ElectricPotential | V0 | Voltage | EMF, VoltageDrop, BackEMF | + +### Resolved Decisions (Semantic Overloads) + +1. **Overload depth — flat.** Overloads do not nest. `Weight` is an overload of `ForceMagnitude`, but `BodyWeight` is *not* an overload of `Weight`. If a domain wants `BodyWeight`, declare it as a sibling overload of `ForceMagnitude` directly. + +2. **Cross-overload arithmetic — narrowest-shared-base wins.** `Width + Width => Width`. `Width + Height => Length` (the shared base). The generator does not introduce new overload types from arithmetic; semantic promotion such as `Perimeter = 2·Width + 2·Height` is application logic, not a generator concern. + +3. **Conversions — implicit widen, explicit narrow.** A `Weight` is implicitly a `ForceMagnitude`; the reverse requires `Weight.From(forceMagnitude)` or an explicit cast. This is the rule for every overload, regardless of vector form. + +4. **Unit scoping — display hint only.** All overloads accept the full unit set of their dimension. Display preferences (e.g. `Wavelength` reading out in nm) are a presentation-layer concern. + +5. **Overload-specific validation — via per-dimension metadata.** Constraints stronger than the form's structural rules (V0 non-negativity is automatic; absolute zero on `Temperature` is not) are declared via `physicalConstraints` on the dimension entry in `dimensions.json`. The generator emits guards inside `Create`/`From*` that throw `ArgumentException`. Overloads inherit their dimension's constraints. + +## Proposed Base Types + +This section defines the base type name for every physical dimension at each supported vector form. These are the canonical types that carry the full operator suite and cross-dimensional relationships. Semantic overloads (Width, Weight, etc.) derive from these. + +### Naming Patterns + +Three patterns emerge naturally based on whether the dimension has an established magnitude name: + +**Pattern A** - Distinct magnitude name exists in physics: + +| V0 | V1 | V2 | V3 | V4 | +|----|----|----|----|----| +| Speed | Velocity1D | Velocity2D | Velocity3D | Velocity4D | + +**Pattern B** - Dimension is inherently non-directional (V0 only): + +| V0 | +|----| +| Mass | + +**Pattern C** - Directional but no distinct magnitude name: + +| V0 | V1 | V2 | V3 | V4 | +|----|----|----|----|----| +| ForceMagnitude | Force1D | Force2D | Force3D | Force4D | + +### Vector Form Applicability + +Not all dimensions support all vector forms. Three categories: + +- **Scalar-only** (V0): Quantities with no meaningful directional interpretation. Mass, Time, Energy, Pressure, etc. +- **Linear** (V0, V1, V2, V3, V4): Quantities that can point in any spatial direction. Velocity, Force, Acceleration, etc. +- **Rotational** (V0, V1, V3): Pseudovectors that are scalar in 2D and axial vectors in 3D. Angular velocity, Torque, etc. No V2 or V4 forms because angular quantities in 2D reduce to a signed scalar (V1), not a 2-component vector. + +### Complete Base Type Catalog + +#### Base SI Dimensions + +| Dimension | Formula | V0 | V1 | V2 | V3 | V4 | Category | +|-----------|---------|----|----|----|----|-----|----------| +| Dimensionless | 1 | Ratio | SignedRatio | - | - | - | Scalar + signed | +| Length | L | Length | Displacement1D | Displacement2D | Displacement3D | Displacement4D | Linear | +| Mass | M | Mass | - | - | - | - | Scalar-only | +| Time | T | Duration | - | - | - | - | Scalar-only | +| ElectricCurrent | I | CurrentMagnitude | Current1D | - | Current3D | - | Linear (partial) | +| Temperature | Θ | Temperature | TemperatureDelta | - | - | - | Scalar + signed | +| AmountOfSubstance | N | AmountOfSubstance | - | - | - | - | Scalar-only | +| LuminousIntensity | J | LuminousIntensity | - | - | - | - | Scalar-only | + +#### Geometry + +| Dimension | Formula | V0 | V1 | V2 | V3 | V4 | Category | +|-----------|---------|----|----|----|----|-----|----------| +| Area | L² | Area | - | - | - | - | Scalar-only | +| Volume | L³ | Volume | - | - | - | - | Scalar-only | +| NuclearCrossSection | L² | NuclearCrossSection | - | - | - | - | Scalar-only | + +#### Linear Motion + +| Dimension | Formula | V0 | V1 | V2 | V3 | V4 | Category | +|-----------|---------|----|----|----|----|-----|----------| +| Velocity | L T⁻¹ | Speed | Velocity1D | Velocity2D | Velocity3D | Velocity4D | Linear | +| Acceleration | L T⁻² | AccelerationMagnitude | Acceleration1D | Acceleration2D | Acceleration3D | Acceleration4D | Linear | +| Jerk | L T⁻³ | JerkMagnitude | Jerk1D | Jerk2D | Jerk3D | Jerk4D | Linear | +| Snap | L T⁻⁴ | SnapMagnitude | Snap1D | Snap2D | Snap3D | Snap4D | Linear | + +#### Rotational Motion + +| Dimension | Formula | V0 | V1 | V3 | Category | +|-----------|---------|----|----|-----|----------| +| AngularDisplacement | 1 | Angle | SignedAngle | AngularDisplacement3D | Rotational | +| AngularVelocity | T⁻¹ | AngularSpeed | AngularVelocity1D | AngularVelocity3D | Rotational | +| AngularAcceleration | T⁻² | AngularAccelerationMagnitude | AngularAcceleration1D | AngularAcceleration3D | Rotational | +| AngularJerk | T⁻³ | AngularJerkMagnitude | AngularJerk1D | AngularJerk3D | Rotational | +| Torque | M L² T⁻² | TorqueMagnitude | Torque1D | Torque3D | Rotational | +| AngularMomentum | M L² T⁻¹ | AngularMomentumMagnitude | AngularMomentum1D | AngularMomentum3D | Rotational | +| MomentOfInertia | M L² | MomentOfInertia | - | - | Scalar-only | + +#### Mechanics + +| Dimension | Formula | V0 | V1 | V2 | V3 | V4 | Category | +|-----------|---------|----|----|----|----|-----|----------| +| Force | M L T⁻² | ForceMagnitude | Force1D | Force2D | Force3D | Force4D | Linear | +| Momentum | M L T⁻¹ | MomentumMagnitude | Momentum1D | Momentum2D | Momentum3D | Momentum4D | Linear | +| Pressure | M L⁻¹ T⁻² | Pressure | - | - | - | - | Scalar-only | +| Energy | M L² T⁻² | Energy | - | - | - | - | Scalar-only | +| Power | M L² T⁻³ | Power | - | - | - | - | Scalar-only | +| Density | M L⁻³ | Density | - | - | - | - | Scalar-only | + +#### Frequency and Rates + +| Dimension | Formula | V0 | V1 | V2 | V3 | V4 | Category | +|-----------|---------|----|----|----|----|-----|----------| +| Frequency | T⁻¹ | Frequency | - | - | - | - | Scalar-only | +| RadioactiveActivity | T⁻¹ | RadioactiveActivity | - | - | - | - | Scalar-only | + +#### Electrical + +| Dimension | Formula | V0 | V1 | V2 | V3 | V4 | Category | +|-----------|---------|----|----|----|----|-----|----------| +| ElectricCharge | I T | ChargeMagnitude | Charge | - | - | - | Scalar + signed | +| ElectricPotential | M L² T⁻³ I⁻¹ | VoltageMagnitude | Voltage | - | - | - | Scalar + signed | +| ElectricField | M L T⁻³ I⁻¹ | ElectricFieldMagnitude | ElectricField1D | ElectricField2D | ElectricField3D | - | Linear (partial) | +| ElectricResistance | M L² T⁻³ I⁻² | Resistance | - | - | - | - | Scalar-only | +| ElectricCapacitance | M⁻¹ L⁻² T⁴ I² | Capacitance | - | - | - | - | Scalar-only | + +#### Radiation + +| Dimension | Formula | V0 | V1 | V2 | V3 | V4 | Category | +|-----------|---------|----|----|----|----|-----|----------| +| AbsorbedDose | L² T⁻² | AbsorbedDose | - | - | - | - | Scalar-only | +| EquivalentDose | L² T⁻² | EquivalentDose | - | - | - | - | Scalar-only | + +#### Optical + +| Dimension | Formula | V0 | V1 | V2 | V3 | V4 | Category | +|-----------|---------|----|----|----|----|-----|----------| +| LuminousFlux | J | LuminousFlux | - | - | - | - | Scalar-only | +| Illuminance | J L⁻² | Illuminance | - | - | - | - | Scalar-only | +| OpticalPower | L⁻¹ | OpticalPower | - | - | - | - | Scalar-only | + +#### Chemical + +| Dimension | Formula | V0 | V1 | V2 | V3 | V4 | Category | +|-----------|---------|----|----|----|----|-----|----------| +| Concentration | N L⁻³ | Concentration | - | - | - | - | Scalar-only | + +### Naming Rationale + +**Names chosen over alternatives:** + +| Chosen | Alternative considered | Rationale | +|--------|----------------------|-----------| +| `Duration` (V0 of Time) | `Time` | "Time" is the dimension name; "Duration" is the magnitude concept (a span of time, always >= 0). Avoids ambiguity when the dimension and the type share a name. | +| `Ratio` (V0 of Dimensionless) | `Dimensionless` | Clearer intent. A ratio is always a non-negative magnitude. The dimension is "Dimensionless"; the V0 base type is "Ratio". | +| `SignedRatio` (V1 of Dimensionless) | `Dimensionless1D` | More readable than appending "1D" to "Dimensionless". Conveys that it's a signed value. | +| `Angle` (V0 of AngularDisplacement) | `AngularDisplacementMagnitude` | "Angle" is the universally understood magnitude name. No one says "angular displacement magnitude". | +| `SignedAngle` (V1 of AngularDisplacement) | `AngularDisplacement1D` | Idiomatic. Matches how game engines and graphics APIs name the signed variant. | +| `AngularSpeed` (V0 of AngularVelocity) | `AngularVelocityMagnitude` | Parallels Speed/Velocity. "Angular speed" is the established physics term for the magnitude. | +| `Length` (V0 of Length dimension) | `Distance` | "Length" is the most general term. "Distance" is a semantic overload (separation between two points). | +| `CurrentMagnitude` (V0 of ElectricCurrent) | `ElectricCurrentMagnitude` | Shorter while unambiguous. "Current" alone is too common a word. | +| `VoltageMagnitude` (V0 of ElectricPotential) | `ElectricPotentialMagnitude` | "Voltage" is far more commonly used than "electric potential" in practice. | +| `Voltage` (V1 of ElectricPotential) | `ElectricPotential` | Same reasoning. V1 because voltage/potential difference is naturally signed (above or below reference). | +| `Charge` (V1 of ElectricCharge) | `ElectricCharge` | Shorter. V1 because charge is naturally signed (positive/negative). | +| `Resistance` (V0 of ElectricResistance) | `ElectricResistance` | Shorter while unambiguous in context. | +| `Capacitance` (V0 of ElectricCapacitance) | `ElectricCapacitance` | Same reasoning. | + +**The `{Name}Magnitude` pattern** is used when: +- The dimension is fundamentally directional (Force, Acceleration, Momentum) +- No distinct magnitude name exists in common physics terminology +- The unqualified name is more naturally associated with the directional form + +**Type counts by vector form:** + +| Form | Count | Notes | +|------|-------|-------| +| V0 | 39 | Every dimension has a V0 form | +| V1 | 17 | Signed scalars and 1D directional | +| V2 | 8 | 2D linear quantities only | +| V3 | 16 | 3D linear + rotational pseudovectors | +| V4 | 8 | 4D linear quantities only | +| **Total** | **88** | Base types only, before semantic overloads | + +### Magnitude Relationships + +Every V1+ type has a structural `Magnitude()` method returning its V0 base: + +| From (VN base) | Magnitude() returns (V0 base) | +|-----------------|-------------------------------| +| Velocity1D/2D/3D/4D | Speed | +| Acceleration1D/2D/3D/4D | AccelerationMagnitude | +| Force1D/2D/3D/4D | ForceMagnitude | +| Momentum1D/2D/3D/4D | MomentumMagnitude | +| Displacement1D/2D/3D/4D | Length | +| Jerk1D/2D/3D/4D | JerkMagnitude | +| Snap1D/2D/3D/4D | SnapMagnitude | +| SignedRatio | Ratio | +| TemperatureDelta | Temperature | +| Charge | ChargeMagnitude | +| Voltage | VoltageMagnitude | +| ElectricField1D/2D/3D | ElectricFieldMagnitude | +| Current1D/3D | CurrentMagnitude | +| SignedAngle | Angle | +| AngularVelocity1D/3D | AngularSpeed | +| AngularAcceleration1D/3D | AngularAccelerationMagnitude | +| AngularJerk1D/3D | AngularJerkMagnitude | +| Torque1D/3D | TorqueMagnitude | +| AngularMomentum1D/3D | AngularMomentumMagnitude | + +## Migration Strategy + +### Phase 1: Add IVector0 and IVector1 interfaces +- Define `IVector0` alongside existing `IVector2`, `IVector3`, `IVector4` +- Define `IVector1` +- Both interfaces coexist with the current `SemanticQuantity` base + +### Phase 2: Update dimensions.json schema +- Replace `"scalar"` / `"vectors"` booleans with `"vectorForms"` object +- Populate with appropriate names for each quantity at each dimensionality + +### Phase 3: Update source generator +- Generate Vector0 types where configured (replacing current scalar generation) +- Generate Vector1 types where configured (new) +- Keep Vector2/3/4 generation as-is but implement `Magnitude()` returning the Vector0 type +- Generate cross-dimensionality operators (VectorN * Vector0, etc.) + +### Phase 4: Update SemanticQuantity base +- Evaluate whether `SemanticQuantity` becomes the base for Vector0/Vector1 +- Or whether the IVector interfaces stand alone with generated record types + +## Resolved Decisions + +These were originally tracked as open questions; they are now locked. Reopening requires an architecture discussion. + +1. **`V0 - V0` returns the same V0 of `T.Abs(a - b)`.** Magnitude subtraction stays a magnitude. Signed subtraction must use the V1 form explicitly. + +2. **Implicit conversions across vector forms — none.** `Speed` does not implicitly convert to `Velocity1D`, and vice versa. Magnitude extraction is `vN.Magnitude() => V0`; constructing a V1 from a V0 must be explicit. + +3. **`{Dimension}Magnitude` is the canonical V0 name when no established magnitude term exists.** `JerkMagnitude`, `ForceMagnitude`, `TorqueMagnitude`. Dimensions with a vernacular magnitude name use it directly (`Speed` for `Velocity`, `Distance`/`Length` for displacement, etc.). + +4. **Vector types inherit from `PhysicalQuantity` and implement the matching `IVector{N}`.** `IVector0`..`IVector4` are interfaces; the concrete generated records share a common base for arithmetic infrastructure. + +5. **`Dimensionless` has both V0 (`Ratio`) and V1 (`SignedRatio`) bases.** Non-negative dimensionless quantities (`RefractiveIndex`, `MachNumber`, `SpecificGravity`, `ReynoldsNumber`) are V0 overloads of `Ratio`. Signed differential ratios use `SignedRatio`. + +6. **Angular quantities have V0/V1/V3 forms.** V0 (`AngularDisplacement`, etc.) for magnitudes (also covering 2D scalar angles), V1 for signed scalar angles, V3 for axial vectors. There is no V2 form for angular quantities — in 2D they reduce to a signed scalar. + +7. **Physical constraints come from `physicalConstraints` metadata.** Floors stronger than V0's structural non-negativity (e.g. absolute zero, non-negative frequency) are declared per-dimension in `dimensions.json` and emitted as `ArgumentException`-throwing guards inside `Create`/`From*`. + +--- + +*This document is the architecture spec for the unified vector model. The metadata schema and generator workflow live in `docs/physics-generator.md`.* diff --git a/docs/validation-reference.md b/docs/validation-reference.md index 8ac158a..1dc5c8f 100644 --- a/docs/validation-reference.md +++ b/docs/validation-reference.md @@ -1,463 +1,224 @@ # Validation Reference -This document provides a comprehensive reference for all built-in validation attributes and validation strategies available in the Semantics library. +A complete reference of the built-in validation attributes shipped with the library, plus how validation strategies and custom rules fit together. -## Table of Contents +> Validation runs at construction time. A failed validation throws `ArgumentException` (not `FormatException`) — see CLAUDE.md. -- [Overview](#overview) -- [Built-in Validation Attributes](#built-in-validation-attributes) -- [Built-in Types](#built-in-types) -- [Validation Strategies](#validation-strategies) -- [Custom Validation](#custom-validation) +For the architecture (attribute → strategy → rule → factory pipeline), see `architecture.md`. For practical patterns including custom rules, see `advanced-usage.md`. -## Overview +## At a glance -The Semantics library provides a robust validation system with multiple layers: +| Category | Where | Count | +|---|---|---| +| [Text](#text) | `Semantics.Strings/Validation/Attributes/Text/` | 7 | +| [Format](#format) | `Semantics.Strings/Validation/Attributes/Format/` | 7 | +| [Casing](#casing) | `Semantics.Strings/Validation/Attributes/Casing/` | 9 | +| [First-class .NET types](#first-class-net-types) | `Semantics.Strings/Validation/Attributes/FirstClassTypes/` | 10 | +| [Path](#path) | `Semantics.Paths/Validation/Attributes/Path/` | 10 | +| [Strategies](#strategies) | `Semantics.Strings/Validation/Strategies/` | 2 | -- **Validation Attributes**: Decorative attributes that define validation rules -- **Validation Strategies**: Control how multiple validation rules are processed -- **Validation Rules**: The actual implementation of validation logic -- **Built-in Types**: Pre-configured semantic string types with common validations +There is **no** quantity validation in this list — semantic quantities enforce their own invariants at the type level (see `strategy-unified-vector-quantities.md`). -## Built-in Validation Attributes +## Text -### String Validation - -#### `IsEmailAttribute` - -Validates email address format using standard email regex patterns. +### `[IsEmailAddress]` +Validates that the value parses as an email address. ```csharp -[IsEmail] +[IsEmailAddress] public sealed record EmailAddress : SemanticString { } -var factory = new SemanticStringFactory(); -var email = factory.Create("user@example.com"); // ✅ Valid -// factory.Create("invalid-email"); // ❌ Throws FormatException -``` - -#### `IsUrlAttribute` - -Validates URL format for both HTTP and HTTPS URLs. - -```csharp -[IsUrl] -public sealed record WebUrl : SemanticString { } - -var factory = new SemanticStringFactory(); -var url = factory.Create("https://example.com"); // ✅ Valid -// factory.Create("not-a-url"); // ❌ Throws FormatException +EmailAddress.Create("user@example.com"); // ✅ +EmailAddress.Create("not-an-email"); // ❌ ArgumentException ``` -#### `IsNotEmptyAttribute` - -Prevents empty, null, or whitespace-only strings. +### `[IsBase64]` +Validates that the value is well-formed Base64. ```csharp -[IsNotEmpty] -public sealed record NonEmptyString : SemanticString { } - -var factory = new SemanticStringFactory(); -var text = factory.Create("Hello World"); // ✅ Valid -// factory.Create(""); // ❌ Throws FormatException -// factory.Create(" "); // ❌ Throws FormatException -``` - -#### `HasLengthAttribute` - -Constrains string length to specified minimum and maximum values. - -```csharp -[HasLength(5, 20)] // Min 5, Max 20 characters -public sealed record Username : SemanticString { } - -var factory = new SemanticStringFactory(); -var username = factory.Create("johndoe"); // ✅ Valid (7 characters) -// factory.Create("abc"); // ❌ Throws FormatException (too short) -// factory.Create("verylongusernamethatexceedslimit"); // ❌ Throws FormatException (too long) -``` - -### Path Validation - -#### `IsPathAttribute` - -Validates that the string represents a valid path with legal characters and appropriate length. - -```csharp -[IsPath] -public sealed record GenericPath : SemanticString { } - -var factory = new SemanticStringFactory(); -var path = factory.Create(@"C:\temp\file.txt"); // ✅ Valid -// factory.Create("C:\\invalid<>path"); // ❌ Throws FormatException -``` - -#### `IsAbsolutePathAttribute` - -Validates fully qualified, absolute paths. - -```csharp -[IsAbsolutePath] -public sealed record AbsolutePath : SemanticString { } - -var factory = new SemanticStringFactory(); -var absPath = factory.Create(@"C:\Projects\App"); // ✅ Valid -// factory.Create("relative\path"); // ❌ Throws FormatException -``` - -#### `IsRelativePathAttribute` - -Validates relative paths (not starting from root). - -```csharp -[IsRelativePath] -public sealed record RelativePath : SemanticString { } - -var factory = new SemanticStringFactory(); -var relPath = factory.Create(@"subfolder\file.txt"); // ✅ Valid -// factory.Create(@"C:\absolute\path"); // ❌ Throws FormatException +[IsBase64] +public sealed record ApiToken : SemanticString { } ``` -#### `IsFilePathAttribute` - -Validates paths that point to files (not directories). +### `[StartsWith(prefix)]`, `[EndsWith(suffix)]`, `[Contains(substring)]` +Self-explanatory substring constraints. ```csharp -[IsFilePath] -public sealed record FilePath : SemanticString { } - -var factory = new SemanticStringFactory(); -var filePath = factory.Create(@"C:\temp\document.pdf"); // ✅ Valid -// factory.Create(@"C:\temp\"); // ❌ Throws FormatException (directory) +[StartsWith("https://"), Contains(".example.com")] +public sealed record SecureApiUrl : SemanticString { } ``` -#### `IsDirectoryPathAttribute` - -Validates paths that point to directories. +### `[PrefixAndSuffix(prefix, suffix)]` +Convenience for "must start with X and end with Y". ```csharp -[IsDirectoryPath] -public sealed record DirectoryPath : SemanticString { } - -var factory = new SemanticStringFactory(); -var dirPath = factory.Create(@"C:\Projects\"); // ✅ Valid -// factory.Create(@"C:\file.txt"); // ❌ Throws FormatException (file) +[PrefixAndSuffix("Bearer ", "==")] +public sealed record BearerToken : SemanticString { } ``` -#### `IsFileNameAttribute` - -Validates filenames without path separators. +### `[RegexMatch(pattern[, options])]` +Arbitrary regex constraint. ```csharp -[IsFileName] -public sealed record FileName : SemanticString { } - -var factory = new SemanticStringFactory(); -var fileName = factory.Create("document.pdf"); // ✅ Valid -// factory.Create("folder\\file.txt"); // ❌ Throws FormatException (contains path) +[RegexMatch(@"^[a-z0-9]+(-[a-z0-9]+)*$")] +public sealed record BlogSlug : SemanticString { } ``` -#### `IsExtensionAttribute` +## Format -Validates file extensions (with period). +### `[IsEmptyOrWhitespace]` / `[HasNonWhitespaceContent]` +Mutually exclusive — pick one. -```csharp -[IsExtension] -public sealed record FileExtension : SemanticString { } - -var factory = new SemanticStringFactory(); -var extension = factory.Create(".pdf"); // ✅ Valid -// factory.Create("pdf"); // ❌ Throws FormatException (no period) -``` +### `[IsSingleLine]` / `[IsMultiLine]` +Constrain whether the string contains line breaks. -#### `DoesExistAttribute` - -Validates that the path exists in the file system. +### `[HasExactLines(n)]`, `[HasMinimumLines(n)]`, `[HasMaximumLines(n)]` +Constrain the line count. ```csharp -[IsPath, DoesExist] -public sealed record ExistingPath : SemanticString { } - -var factory = new SemanticStringFactory(); -var existingPath = factory.Create(@"C:\Windows"); // ✅ Valid (if exists) -// factory.Create(@"C:\NonExistent"); // ❌ Throws FormatException +[HasMaximumLines(10), HasNonWhitespaceContent] +public sealed record CommitMessageHeader : SemanticString { } ``` -### Quantity Validation - -#### `IsPositiveAttribute` +## Casing -Validates that numeric values are positive (> 0). +| Attribute | Style | Example | +|---|---|---| +| `[IsCamelCase]` | `myVariable` | `httpRequest` | +| `[IsPascalCase]` | `MyClass` | `HttpRequest` | +| `[IsKebabCase]` | `lower-with-dashes` | `http-request` | +| `[IsSnakeCase]` | `lower_with_underscores` | `http_request` | +| `[IsMacroCase]` | `UPPER_WITH_UNDERSCORES` | `HTTP_REQUEST` | +| `[IsLowerCase]` | all lowercase | `httprequest` | +| `[IsUpperCase]` | all uppercase | `HTTPREQUEST` | +| `[IsSentenceCase]` | first letter upper, rest lower | `Http request` | +| `[IsTitleCase]` | first letter of each word upper | `Http Request` | -```csharp -[IsPositive] -public sealed record PositiveNumber : SemanticString { } - -var factory = new SemanticStringFactory(); -var positive = factory.Create("42"); // ✅ Valid -// factory.Create("-5"); // ❌ Throws FormatException -// factory.Create("0"); // ❌ Throws FormatException -``` +## First-class .NET types -#### `IsNegativeAttribute` +These attributes assert that the string parses to a particular .NET type. -Validates that numeric values are negative (< 0). +| Attribute | Parses as | +|---|---| +| `[IsBoolean]` | `bool` | +| `[IsDateTime]` | `DateTime` | +| `[IsDecimal]` | `decimal` | +| `[IsDouble]` | `double` | +| `[IsGuid]` | `Guid` | +| `[IsInt32]` | `int` | +| `[IsIpAddress]` | `IPAddress` | +| `[IsTimeSpan]` | `TimeSpan` | +| `[IsUri]` | `Uri` | +| `[IsVersion]` | `Version` | ```csharp -[IsNegative] -public sealed record NegativeNumber : SemanticString { } +[IsGuid] +public sealed record TransactionId : SemanticString { } -var factory = new SemanticStringFactory(); -var negative = factory.Create("-42"); // ✅ Valid -// factory.Create("5"); // ❌ Throws FormatException -// factory.Create("0"); // ❌ Throws FormatException +[IsUri] +public sealed record WebsiteUrl : SemanticString { } ``` -#### `IsInRangeAttribute` - -Validates that numeric values fall within a specified range. +> When the value will be used as the parsed type rather than as a string, prefer wrapping the .NET type directly (e.g. `record TransactionId(Guid Value)`). Use these attributes when the value lives inside a wider string-validation pipeline. -```csharp -[IsInRange(1, 100)] // Between 1 and 100 inclusive -public sealed record Percentage : SemanticString { } +## Path -var factory = new SemanticStringFactory(); -var percentage = factory.Create("75"); // ✅ Valid -// factory.Create("150"); // ❌ Throws FormatException -// factory.Create("0"); // ❌ Throws FormatException -``` +These live in `Semantics.Paths` and require `using ktsu.Semantics.Paths;`. -## Built-in Types - -The library provides pre-configured semantic string types with appropriate validations: - -### Path Types +| Attribute | Validates | +|---|---| +| `[IsPath]` | Legal path characters and length. | +| `[IsValidPath]` | Stricter: also rejects reserved names. | +| `[IsAbsolutePath]` | Fully qualified path. | +| `[IsRelativePath]` | Not absolute. | +| `[IsFilePath]` | Refers to a file (not a directory). | +| `[IsDirectoryPath]` | Refers to a directory. | +| `[IsFileName]` | Filename without separators. | +| `[IsValidFileName]` | Stricter filename validation. | +| `[IsExtension]` | File extension including the leading dot. | +| `[DoesExist]` | The path exists at validation time. Use sparingly — couples the type to the file system. | ```csharp -// Pre-configured path types - no additional attributes needed -var pathFactory = new SemanticStringFactory(); -var absoluteFactory = new SemanticStringFactory(); -var relativeFactory = new SemanticStringFactory(); -var fileFactory = new SemanticStringFactory(); -var directoryFactory = new SemanticStringFactory(); -var nameFactory = new SemanticStringFactory(); -var extensionFactory = new SemanticStringFactory(); - -// Each type has built-in validation and specialized properties -var filePath = fileFactory.Create(@"C:\temp\data.json"); -Console.WriteLine(filePath.FileName); // data.json -Console.WriteLine(filePath.FileExtension); // .json -Console.WriteLine(filePath.DirectoryPath); // C:\temp +[IsAbsolutePath, DoesExist] +public sealed record ConfigFilePath : SemanticString { } ``` -## Validation Strategies +For most use cases, prefer the dedicated path types (`AbsoluteFilePath`, `RelativeDirectoryPath`, etc.) from `Semantics.Paths` — they bundle these attributes and provide rich path operations. -Control how multiple validation attributes are processed: +## Strategies -### `ValidateAllAttribute` (Default) +By default, all attributes on a type must pass (`ValidateAll` semantics). Strategies override that behaviour. -All validation attributes must pass for the value to be considered valid. +### `[ValidateAll]` (default) +Every attribute must pass. Equivalent to leaving the strategy attribute off. -```csharp -[ValidateAll] // Explicit, but this is the default behavior -[IsNotEmpty, IsEmail, HasLength(5, 50)] -public sealed record StrictEmail : SemanticString { } - -// All three validations must pass: -// 1. Must not be empty -// 2. Must be valid email format -// 3. Must be between 5-50 characters -``` - -### `ValidateAnyAttribute` - -At least one validation attribute must pass for the value to be considered valid. +### `[ValidateAny]` +At least one attribute must pass. ```csharp [ValidateAny] -[IsEmail, IsUrl] -public sealed record ContactInfo : SemanticString { } - -// Either validation can pass: -// 1. Valid email address, OR -// 2. Valid URL -var factory = new SemanticStringFactory(); -var email = factory.Create("user@example.com"); // ✅ Valid (email) -var url = factory.Create("https://example.com"); // ✅ Valid (URL) +[IsEmailAddress, IsUri] +public sealed record ContactMethod : SemanticString { } ``` -### `ValidateWithAttribute` +Need richer logic (e.g. "all critical attributes must pass and at least one secondary attribute must pass")? Implement `IValidationStrategy` and register it via `ValidationStrategyFactory`. See `advanced-usage.md` for the worked example. -Use a custom validation strategy for complex business rules. +## Custom validation attributes -```csharp -[ValidateWith(typeof(BusinessRuleValidationStrategy))] -[IsNotEmpty, IsEmail] // Critical validations -[IsCompanyEmail, IsInternalDomain] // Non-critical validations -public sealed record BusinessEmail : SemanticString { } -``` - -## Custom Validation - -### Creating Custom Validation Attributes +Subclass `SemanticStringValidationAttribute`: ```csharp -// Custom validation attribute -public class IsProductCodeAttribute : SemanticStringValidationAttribute +public sealed class IsProductCodeAttribute : SemanticStringValidationAttribute { - public override bool Validate(ISemanticString semanticString) - { - string value = semanticString.ToString(); - // Product codes: letter + 5 digits - return Regex.IsMatch(value, @"^[A-Z][0-9]{5}$"); - } + private static readonly Regex Pattern = new(@"^[A-Z][0-9]{5}$", RegexOptions.Compiled); + + public override bool Validate(ISemanticString semanticString) => + Pattern.IsMatch(semanticString.ToString()); } -// Apply to semantic string type [IsProductCode] public sealed record ProductCode : SemanticString { } ``` -### Combining Multiple Validations +Validation runs through the attribute → strategy → rule pipeline regardless of whether the attribute is built-in or custom. -```csharp -// Complex validation combining multiple attributes -[IsNotEmpty, IsEmail, HasLength(5, 100)] -public sealed record ProfessionalEmail : SemanticString { } +## Practical patterns -// Path with existence checking -[IsAbsolutePath, DoesExist] -public sealed record ExistingAbsolutePath : SemanticPath { } - -// Flexible contact information -[ValidateAny] -[IsEmail, IsUrl, HasLength(10, 15)] // Email, URL, or phone number length -public sealed record ContactMethod : SemanticString { } -``` - -This validation reference provides the foundation for creating robust, type-safe string types with comprehensive validation rules. - -## Practical Examples - -### Domain-Specific Types +### Domain-specific types ```csharp -// API token that must be Base64-encoded -[IsBase64] -public sealed record ApiToken : SemanticString; -// Usage: var token = "SGVsbG9Xb3JsZA==".As(); - -// User's email address for account management [IsEmailAddress] -public sealed record UserEmail : SemanticString; -// Usage: var email = "user@company.com".As(); - -// Blog post URL slug that's SEO-friendly -[RegexMatch(@"^[a-z0-9]+(-[a-z0-9]+)*$")] -public sealed record BlogSlug : SemanticString; -// Usage: var slug = "my-awesome-blog-post".As(); +public sealed record UserEmail : SemanticString { } -// Hexadecimal color code for UI theming [RegexMatch(@"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$")] -public sealed record ThemeColor : SemanticString; -// Usage: var color = "#FF5733".As(); +public sealed record ThemeColor : SemanticString { } -// JWT authentication token [RegexMatch(@"^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$")] -public sealed record AuthToken : SemanticString; -// Usage: var jwt = "header.payload.signature".As(); +public sealed record JwtToken : SemanticString { } ``` -### Combining Multiple Validations +### Combined constraints ```csharp -// Domain name that must end with .com or .org (ValidateAny strategy) +// Default ValidateAll +[StartsWith("https://"), Contains(".example.com"), HasNonWhitespaceContent] +public sealed record SecureApiUrl : SemanticString { } + +// Either-or [ValidateAny] -[EndsWith(".com")] -[EndsWith(".org")] -public sealed record TrustedDomain : SemanticString; -// Usage: var domain = "example.com".As(); - -// Secure URL with multiple requirements (ValidateAll strategy - default) -[ValidateAll] -[StartsWith("https://")] -[Contains(".example.com")] -public sealed record SecureApiUrl : SemanticString; -// Usage: var url = "https://secure.example.com/api".As(); +[EndsWith(".com"), EndsWith(".org")] +public sealed record TrustedDomain : SemanticString { } ``` -## Migration from Obsolete Attributes - -Some validation attributes validate types that have first-class .NET representations. While these attributes still provide accurate validation, you should prefer the first-class types for better performance, type safety, and API richness. - -### ❌ Discouraged: String-based validation -```csharp -// These work but are discouraged -[IsVersion] -public sealed record SoftwareVersion : SemanticString; - -[IsGuid] -public sealed record UniqueId : SemanticString; - -[IsIpAddress] -public sealed record ServerIpAddress : SemanticString; - -[IsDateTime] -public sealed record EventTimestamp : SemanticString; - -[IsTimeSpan] -public sealed record Duration : SemanticString; - -[IsUri] -public sealed record WebsiteUrl : SemanticString; - -[IsDecimal] -public sealed record Price : SemanticString; - -[IsDouble] -public sealed record Measurement : SemanticString; +### When to prefer first-class .NET types -[IsInt32] -public sealed record Count : SemanticString; - -[IsBoolean] -public sealed record Flag : SemanticString; -``` +For values whose consumer cares about the parsed object (Guid, IPAddress, Uri, …), wrap the .NET type directly instead of validating the string: -### ✅ Recommended: First-class .NET types ```csharp -// Use these instead for better type safety and performance -public class SoftwareVersion -{ - public Version Value { get; } - public SoftwareVersion(string version) => Value = Version.Parse(version); - // Rich API: Major, Minor, Build, Revision, comparison operators -} - -public class UniqueId +public sealed record TransactionId(Guid Value) { - public Guid Value { get; } - public UniqueId() => Value = Guid.NewGuid(); - public UniqueId(string guid) => Value = Guid.Parse(guid); - // Built-in: NewGuid(), equality, efficient 16-byte storage + public static TransactionId New() => new(Guid.NewGuid()); } - -public class ServerIpAddress -{ - public IPAddress Value { get; } - public ServerIpAddress(string ip) => Value = IPAddress.Parse(ip); - // Rich API: IPv4/IPv6 support, network operations, parsing -} - -// For other types, use System.DateTime, System.TimeSpan, System.Uri, -// System.Decimal, System.Double, System.Int32, System.Boolean directly ``` -### When to Still Use String-based Validation - -Use the obsolete validation attributes only when you specifically need: -- String-based semantic validation in a larger semantic string system -- Validation as part of a string processing pipeline -- Compatibility with existing semantic string infrastructure - -For new code, prefer the first-class .NET types directly. +Use the `[Is*]` attributes when the value belongs in a string-shaped pipeline (logs, configs, serialised payloads) and the parsed object is incidental. diff --git a/icon.png b/icon.png index 91246f6..4372ef9 100644 Binary files a/icon.png and b/icon.png differ diff --git a/scripts/Generate-AliasProps.ps1 b/scripts/Generate-AliasProps.ps1 new file mode 100644 index 0000000..f5e55c7 --- /dev/null +++ b/scripts/Generate-AliasProps.ps1 @@ -0,0 +1,77 @@ +<# +.SYNOPSIS + Generates the per-storage-type alias .props files for the + ktsu.Semantics.Quantities. satellite packages. + +.DESCRIPTION + Each satellite package ships a buildTransitive/.props that NuGet + auto-imports into consumers. The props inject MSBuild items, which the + SDK turns into `global using Mass = ktsu.Semantics.Quantities.Mass;` + (and so on for every quantity type), so a project that references the package + can write `Mass` instead of `Mass`. + + The catalog of quantity types is the set of committed source-generator outputs + (every quantity is emitted as a `.g.cs`), so this script stays in sync + with the generator automatically. It writes nothing but the .props files and is + verified in CI (see .github/workflows/verify-generated.yml). +#> +[CmdletBinding()] +param() + +$ErrorActionPreference = 'Stop' + +$repoRoot = Split-Path -Parent $PSScriptRoot +$generatedRoot = Join-Path $repoRoot 'Semantics.Quantities/Generated/Semantics.SourceGenerators' + +# Every generic quantity type is emitted by one of these generators as .g.cs. +$catalogDirs = @( + 'Semantics.SourceGenerators.QuantitiesGenerator', + 'Semantics.SourceGenerators.LogarithmicScalesGenerator' +) + +$typeNames = foreach ($dir in $catalogDirs) { + $path = Join-Path $generatedRoot $dir + if (Test-Path $path) { + Get-ChildItem -Path $path -Filter '*.g.cs' -File | + ForEach-Object { $_.Name -replace '\.g\.cs$', '' } + } +} + +$typeNames = $typeNames | Sort-Object -Unique -Culture ([System.Globalization.CultureInfo]::InvariantCulture) + +if ($typeNames.Count -eq 0) { + throw "No quantity types found under $generatedRoot. Build Semantics.Quantities first so the generated files exist." +} + +# PascalCase project/package suffix => C# storage keyword. +$storageTypes = [ordered]@{ + 'Double' = 'double' + 'Float' = 'float' + 'Decimal' = 'decimal' +} + +foreach ($entry in $storageTypes.GetEnumerator()) { + $suffix = $entry.Key + $keyword = $entry.Value + $packageId = "ktsu.Semantics.Quantities.$suffix" + $projectDir = Join-Path $repoRoot "Semantics.Quantities.$suffix" + $buildTransitive = Join-Path $projectDir 'buildTransitive' + New-Item -ItemType Directory -Path $buildTransitive -Force | Out-Null + + $sb = [System.Text.StringBuilder]::new() + [void]$sb.AppendLine('') + [void]$sb.AppendLine("`t") + [void]$sb.AppendLine("`t") + [void]$sb.AppendLine("`t") + foreach ($name in $typeNames) { + [void]$sb.AppendLine("`t`t") + } + [void]$sb.AppendLine("`t") + [void]$sb.AppendLine('') + + $propsPath = Join-Path $buildTransitive "$packageId.props" + # CRLF to match the repo's line-ending convention. + $content = ($sb.ToString() -replace "`r?`n", "`r`n") + [System.IO.File]::WriteAllText($propsPath, $content, [System.Text.UTF8Encoding]::new($false)) + Write-Host "Wrote $propsPath ($($typeNames.Count) aliases)" +} diff --git a/scripts/LICENSE.template b/scripts/LICENSE.template deleted file mode 100644 index ccf9e79..0000000 --- a/scripts/LICENSE.template +++ /dev/null @@ -1,23 +0,0 @@ -MIT License - -{PROJECT_URL} - -{COPYRIGHT} - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/scripts/PSBuild.psd1 b/scripts/PSBuild.psd1 deleted file mode 100644 index ff51087..0000000 --- a/scripts/PSBuild.psd1 +++ /dev/null @@ -1,103 +0,0 @@ -@{ - # Module information - RootModule = 'PSBuild.psm1' - ModuleVersion = '1.1.0' - GUID = '15dd2bfc-0f11-4c8a-b98a-f2529558f423' - Author = 'ktsu.dev' - CompanyName = 'ktsu.dev' - Copyright = '(c) 2023-2025 ktsu.dev. All rights reserved.' - Description = 'A comprehensive PowerShell module for automating the build, test, package, and release process for .NET applications using Git-based versioning.' - - # PowerShell version required - PowerShellVersion = '5.1' - - # Functions to export - FunctionsToExport = @( - # Core build and environment functions - 'Initialize-BuildEnvironment', - 'Get-BuildConfiguration', - - # Version management functions - 'Get-GitTags', - 'Get-VersionType', - 'Get-VersionInfoFromGit', - 'New-Version', - - # Version comparison and conversion functions - 'ConvertTo-FourComponentVersion', - 'Get-VersionNotes', - - # Metadata and documentation functions - 'New-Changelog', - 'Update-ProjectMetadata', - 'New-License', - - # .NET SDK operations - 'Invoke-DotNetRestore', - 'Invoke-DotNetBuild', - 'Invoke-DotNetTest', - 'Invoke-DotNetPack', - 'Invoke-DotNetPublish', - - # Release and publishing functions - 'Invoke-NuGetPublish', - 'New-GitHubRelease', - - # Utility functions - 'Assert-LastExitCode', - 'Write-StepHeader', - 'Test-AnyFiles', - 'Get-GitLineEnding', - 'Set-GitIdentity', - 'Write-InformationStream', - 'Invoke-ExpressionWithLogging', - - # High-level workflow functions - 'Invoke-BuildWorkflow', - 'Invoke-ReleaseWorkflow', - 'Invoke-CIPipeline' - ) - - # Variables to export - VariablesToExport = @() - - # Aliases to export - AliasesToExport = @() - - # Tags for PowerShell Gallery - PrivateData = @{ - PSData = @{ - Tags = @( - 'build', - 'dotnet', - 'ci', - 'cd', - 'nuget', - 'github', - 'versioning', - 'release', - 'automation' - ) - LicenseUri = 'https://github.com/ktsu-dev/PSBuild/blob/main/LICENSE.md' - ProjectUri = 'https://github.com/ktsu-dev/PSBuild' - ReleaseNotes = @' -v1.1.0: -- Improved object model using PSCustomObjects instead of hashtables -- Enhanced git status detection and commit handling -- Fixed logging and variable capture issues -- Added comprehensive help comments to all functions -- Added utility functions to the exported functions list - -v1.0.0: -- Initial release of PSBuild module featuring: -- Semantic versioning based on git history -- Automatic version calculation from commit analysis -- Metadata file generation and management -- Comprehensive build, test, and package pipeline -- NuGet package creation and publishing -- GitHub release creation with assets -- Proper line ending handling based on git config -'@ - } - } -} diff --git a/scripts/PSBuild.psm1 b/scripts/PSBuild.psm1 deleted file mode 100644 index 5f05f9e..0000000 --- a/scripts/PSBuild.psm1 +++ /dev/null @@ -1,2500 +0,0 @@ -# PSBuild Module for .NET CI/CD -# Author: ktsu.dev -# License: MIT -# -# A comprehensive PowerShell module for automating the build, test, package, -# and release process for .NET applications using Git-based versioning. -# See README.md for detailed documentation and usage examples. - -# Set Strict Mode -Set-StrictMode -Version Latest - -#region Environment and Configuration - -function Initialize-BuildEnvironment { - <# - .SYNOPSIS - Initializes the build environment with standard settings. - .DESCRIPTION - Sets up environment variables for .NET SDK and initializes other required build settings. - #> - [CmdletBinding()] - param() - - $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = '1' - $env:DOTNET_CLI_TELEMETRY_OPTOUT = '1' - $env:DOTNET_NOLOGO = 'true' - - Write-Information "Build environment initialized" -Tags "Initialize-BuildEnvironment" -} - -function Get-BuildConfiguration { - <# - .SYNOPSIS - Gets the build configuration based on Git status and environment. - .DESCRIPTION - Determines if this is a release build, checks Git status, and sets up build paths. - Returns a configuration object containing all necessary build settings and paths. - .PARAMETER ServerUrl - The server URL to use for the build. - .PARAMETER GitRef - The Git reference (branch/tag) being built. - .PARAMETER GitSha - The Git commit SHA being built. - .PARAMETER GitHubOwner - The GitHub owner of the repository. - .PARAMETER GitHubRepo - The GitHub repository name. - .PARAMETER GithubToken - The GitHub token for API operations. - .PARAMETER NuGetApiKey - The NuGet API key for package publishing. Optional - if not provided or empty, NuGet publishing will be skipped. - .PARAMETER KtsuPackageKey - The Ktsu package key for package publishing. Optional - if not provided or empty, Ktsu publishing will be skipped. - .PARAMETER WorkspacePath - The path to the workspace/repository root. - .PARAMETER ExpectedOwner - The expected owner/organization of the official repository. - .PARAMETER ChangelogFile - The path to the changelog file. - .PARAMETER LatestChangelogFile - The path to the file containing only the latest version's changelog. Defaults to "LATEST_CHANGELOG.md". - .PARAMETER AssetPatterns - Array of glob patterns for release assets. - .OUTPUTS - PSCustomObject containing build configuration data with Success, Error, and Data properties. - #> - [CmdletBinding()] - [OutputType([PSCustomObject])] - param ( - [Parameter(Mandatory=$true)] - [string]$ServerUrl, - [Parameter(Mandatory=$true)] - [string]$GitRef, - [Parameter(Mandatory=$true)] - [string]$GitSha, - [Parameter(Mandatory=$true)] - [string]$GitHubOwner, - [Parameter(Mandatory=$true)] - [string]$GitHubRepo, - [Parameter(Mandatory=$true)] - [string]$GithubToken, - [Parameter(Mandatory=$false)] - [AllowEmptyString()] - [string]$NuGetApiKey = "", - [Parameter(Mandatory=$false)] - [AllowEmptyString()] - [string]$KtsuPackageKey = "", - [Parameter(Mandatory=$true)] - [string]$WorkspacePath, - [Parameter(Mandatory=$true)] - [string]$ExpectedOwner, - [Parameter(Mandatory=$true)] - [string]$ChangelogFile, - [Parameter(Mandatory=$false)] - [string]$LatestChangelogFile = "LATEST_CHANGELOG.md", - [Parameter(Mandatory=$true)] - [string[]]$AssetPatterns - ) - - # Determine if this is an official repo (verify owner and ensure it's not a fork) - $IS_OFFICIAL = $false - if ($GithubToken) { - try { - $env:GH_TOKEN = $GithubToken - $repoInfo = "gh repo view --json owner,nameWithOwner,isFork 2>`$null" | Invoke-ExpressionWithLogging -Tags "Get-BuildConfiguration" | ConvertFrom-Json - if ($repoInfo) { - # Consider it official only if it's not a fork AND belongs to the expected owner - $IS_OFFICIAL = (-not $repoInfo.isFork) -and ($repoInfo.owner.login -eq $ExpectedOwner) - Write-Information "Repository: $($repoInfo.nameWithOwner), Is Fork: $($repoInfo.isFork), Owner: $($repoInfo.owner.login)" -Tags "Get-BuildConfiguration" - } else { - Write-Information "Could not retrieve repository information. Assuming unofficial build." -Tags "Get-BuildConfiguration" - } - } - catch { - Write-Information "Failed to check repository status: $_. Assuming unofficial build." -Tags "Get-BuildConfiguration" - } - } - - Write-Information "Is Official: $IS_OFFICIAL" -Tags "Get-BuildConfiguration" - - # Determine if this is main branch and not tagged - $IS_MAIN = $GitRef -eq "refs/heads/main" - $IS_TAGGED = "(git show-ref --tags -d | Out-String).Contains(`"$GitSha`")" | Invoke-ExpressionWithLogging -Tags "Get-BuildConfiguration" - $SHOULD_RELEASE = ($IS_MAIN -AND -NOT $IS_TAGGED -AND $IS_OFFICIAL) - - # Check for .csx files (dotnet-script) - $csx = @(Get-ChildItem -Path $WorkspacePath -Recurse -Filter *.csx -ErrorAction SilentlyContinue) - $USE_DOTNET_SCRIPT = $csx.Count -gt 0 - - # Setup paths - $OUTPUT_PATH = Join-Path $WorkspacePath 'output' - $STAGING_PATH = Join-Path $WorkspacePath 'staging' - - # Setup artifact patterns - $PACKAGE_PATTERN = Join-Path $STAGING_PATH "*.nupkg" - $SYMBOLS_PATTERN = Join-Path $STAGING_PATH "*.snupkg" - $APPLICATION_PATTERN = Join-Path $STAGING_PATH "*.zip" - - # Set build arguments - $BUILD_ARGS = "" - if ($USE_DOTNET_SCRIPT) { - $BUILD_ARGS = "-maxCpuCount:1" - } - - # Create configuration object with standard format - $config = [PSCustomObject]@{ - Success = $true - Error = "" - Data = @{ - IsOfficial = $IS_OFFICIAL - IsMain = $IS_MAIN - IsTagged = $IS_TAGGED - ShouldRelease = $SHOULD_RELEASE - UseDotnetScript = $USE_DOTNET_SCRIPT - OutputPath = $OUTPUT_PATH - StagingPath = $STAGING_PATH - PackagePattern = $PACKAGE_PATTERN - SymbolsPattern = $SYMBOLS_PATTERN - ApplicationPattern = $APPLICATION_PATTERN - BuildArgs = $BUILD_ARGS - WorkspacePath = $WorkspacePath - DotnetVersion = $script:DOTNET_VERSION - ServerUrl = $ServerUrl - GitRef = $GitRef - GitSha = $GitSha - GitHubOwner = $GitHubOwner - GitHubRepo = $GitHubRepo - GithubToken = $GithubToken - NuGetApiKey = $NuGetApiKey - KtsuPackageKey = $KtsuPackageKey - ExpectedOwner = $ExpectedOwner - Version = "1.0.0-pre.0" - ReleaseHash = $GitSha - ChangelogFile = $ChangelogFile - LatestChangelogFile = $LatestChangelogFile - AssetPatterns = $AssetPatterns - } - } - - return $config -} - -#endregion - -#region Version Management - -function Get-GitTags { - <# - .SYNOPSIS - Gets sorted git tags from the repository. - .DESCRIPTION - Retrieves a list of git tags sorted by version in descending order. - Returns a default tag if no tags exist. - #> - [CmdletBinding()] - [OutputType([string[]])] - param () - - # Configure git versionsort to correctly handle prereleases - $suffixes = @('-alpha', '-beta', '-rc', '-pre') - foreach ($suffix in $suffixes) { - "git config versionsort.suffix `"$suffix`"" | Invoke-ExpressionWithLogging -Tags "Get-GitTags" | Write-InformationStream -Tags "Get-GitTags" - } - - Write-Information "Getting sorted tags..." -Tags "Get-GitTags" - # Get tags - $output = "git tag --list --sort=-v:refname" | Invoke-ExpressionWithLogging -Tags "Get-GitTags" - - # Ensure we always return an array - if ($null -eq $output) { - Write-Information "No tags found, returning empty array" -Tags "Get-GitTags" - return @() - } - - # Convert to array if it's not already - if ($output -isnot [array]) { - if ([string]::IsNullOrWhiteSpace($output)) { - Write-Information "No tags found, returning empty array" -Tags "Get-GitTags" - return @() - } - $output = @($output) - } - - if ($output.Count -eq 0) { - Write-Information "No tags found, returning empty array" -Tags "Get-GitTags" - return @() - } - - Write-Information "Found $($output.Count) tags" -Tags "Get-GitTags" - return $output -} - -function Get-VersionType { - <# - .SYNOPSIS - Determines the type of version bump needed based on commit history and public API changes - .DESCRIPTION - Analyzes commit messages and code changes to determine whether the next version should be: - - Major (1.0.0 → 2.0.0): Breaking changes, indicated by [major] tags in commits - - Minor (1.0.0 → 1.1.0): Non-breaking public API changes (additions, modifications, removals) - - Patch (1.0.0 → 1.0.1): Bug fixes and changes that don't modify the public API - - Prerelease (1.0.0 → 1.0.1-pre.1): Small changes or no significant changes - - Skip: Only [skip ci] commits or no significant changes requiring a version bump - - Version bump determination follows these rules in order: - 1. Explicit tags in commit messages: [major], [minor], [patch], [pre] - 2. Public API changes detection via regex patterns (triggers minor bump) - 3. Code changes that don't modify public API (triggers patch bump) - 4. Default to prerelease bump for minimal changes - 5. If only [skip ci] commits are found, suggest skipping the release - .PARAMETER Range - The git commit range to analyze (e.g., "v1.0.0...HEAD" or a specific commit range) - .OUTPUTS - Returns a PSCustomObject with 'Type' and 'Reason' properties explaining the version increment decision. - #> - [CmdletBinding()] - [OutputType([PSCustomObject])] - param ( - [Parameter(Mandatory=$true)] - [string]$Range - ) - - # Initialize to the most conservative version bump - $versionType = "prerelease" - $reason = "No significant changes detected" - - # Bot and PR patterns to exclude - $EXCLUDE_BOTS = '^(?!.*(\[bot\]|github|ProjectDirector|SyncFileContents)).*$' - $EXCLUDE_PRS = '^.*(Merge pull request|Merge branch ''main''|Updated packages in|Update.*package version).*$' - - # First check for explicit version markers in commit messages - $messages = "git log --format=format:%s `"$Range`"" | Invoke-ExpressionWithLogging -Tags "Get-VersionType" - - # Ensure messages is always an array - if ($null -eq $messages) { - $messages = @() - } elseif ($messages -isnot [array]) { - $messages = @($messages) - } - - # Check if we have any commits at all - if (@($messages).Count -eq 0) { - return [PSCustomObject]@{ - Type = "skip" - Reason = "No commits found in the specified range" - } - } - - # Check if all commits are skip ci commits - $skipCiPattern = '\[skip ci\]|\[ci skip\]' - $skipCiCommits = $messages | Where-Object { $_ -match $skipCiPattern } - - if (@($skipCiCommits).Count -eq @($messages).Count -and @($messages).Count -gt 0) { - return [PSCustomObject]@{ - Type = "skip" - Reason = "All commits contain [skip ci] tag, skipping release" - } - } - - foreach ($message in $messages) { - if ($message.Contains('[major]')) { - $versionType = 'major' - $reason = "Explicit [major] tag found in commit message: $message" - # Return immediately for major version bumps - return [PSCustomObject]@{ - Type = $versionType - Reason = $reason - } - } elseif ($message.Contains('[minor]') -and $versionType -ne 'major') { - $versionType = 'minor' - $reason = "Explicit [minor] tag found in commit message: $message" - } elseif ($message.Contains('[patch]') -and $versionType -notin @('major', 'minor')) { - $versionType = 'patch' - $reason = "Explicit [patch] tag found in commit message: $message" - } elseif ($message.Contains('[pre]') -and $versionType -eq 'prerelease') { - # Keep as prerelease, but update reason - $reason = "Explicit [pre] tag found in commit message: $message" - } - } - - # If no explicit version markers, check for code changes - if ($versionType -eq "prerelease") { - # Check for any commits that would warrant at least a patch version - $patchCommits = "git log -n 1 --topo-order --perl-regexp --regexp-ignore-case --format=format:%H --committer=`"$EXCLUDE_BOTS`" --author=`"$EXCLUDE_BOTS`" --grep=`"$EXCLUDE_PRS`" --invert-grep `"$Range`"" | Invoke-ExpressionWithLogging -Tags "Get-VersionType" - - if ($patchCommits) { - $versionType = "patch" - $reason = "Found changes warranting at least a patch version" - - # Check for public API changes that would warrant a minor version - - # First, check if we can detect public API changes via git diff - $apiChangePatterns = @( - # C# public API patterns - '^\+\s*(public|protected)\s+(class|interface|enum|struct|record)\s+\w+', # Added public types - '^\+\s*(public|protected)\s+\w+\s+\w+\s*\(', # Added public methods - '^\+\s*(public|protected)\s+\w+(\s+\w+)*\s*{', # Added public properties - '^\-\s*(public|protected)\s+(class|interface|enum|struct|record)\s+\w+', # Removed public types - '^\-\s*(public|protected)\s+\w+\s+\w+\s*\(', # Removed public methods - '^\-\s*(public|protected)\s+\w+(\s+\w+)*\s*{', # Removed public properties - '^\+\s*public\s+const\s', # Added public constants - '^\-\s*public\s+const\s' # Removed public constants - ) - - # Combine patterns for git diff - $apiChangePattern = "(" + ($apiChangePatterns -join ")|(") + ")" - - # Search for API changes - $apiDiffCmd = "git diff `"$Range`" -- `"*.cs`" | Select-String -Pattern `"$apiChangePattern`" -SimpleMatch" - $apiChanges = Invoke-Expression $apiDiffCmd - - if ($apiChanges) { - $versionType = "minor" - $reason = "Public API changes detected (additions, removals, or modifications)" - return [PSCustomObject]@{ - Type = $versionType - Reason = $reason - } - } - } - } - - return [PSCustomObject]@{ - Type = $versionType - Reason = $reason - } -} - -function Get-VersionInfoFromGit { - <# - .SYNOPSIS - Gets comprehensive version information based on Git tags and commit analysis. - .DESCRIPTION - Finds the most recent version tag, analyzes commit history, and determines the next version - following semantic versioning principles. Returns a rich object with all version components. - .PARAMETER CommitHash - The Git commit hash being built. - .PARAMETER InitialVersion - The version to use if no tags exist. Defaults to "1.0.0". - #> - [CmdletBinding()] - [OutputType([PSCustomObject])] - param ( - [Parameter(Mandatory=$true)] - [string]$CommitHash, - [string]$InitialVersion = "1.0.0" - ) - - Write-StepHeader "Analyzing Version Information" -Tags "Get-VersionInfoFromGit" - Write-Information "Analyzing repository for version information..." -Tags "Get-VersionInfoFromGit" - Write-Information "Commit hash: $CommitHash" -Tags "Get-VersionInfoFromGit" - - # Get all tags - $tags = Get-GitTags - - # Ensure tags is always an array - if ($null -eq $tags) { - $tags = @() - } elseif ($tags -isnot [array]) { - $tags = @($tags) - } - - Write-Information "Found $(@($tags).Count) tag(s)" -Tags "Get-VersionInfoFromGit" - - # Get the last tag and its commit - $usingFallbackTag = $false - $lastTag = "" - - if (@($tags).Count -eq 0) { - $lastTag = "v$InitialVersion-pre.0" - $usingFallbackTag = $true - Write-Information "No tags found. Using fallback: $lastTag" -Tags "Get-VersionInfoFromGit" - } else { - $lastTag = $tags[0] - Write-Information "Using last tag: $lastTag" -Tags "Get-VersionInfoFromGit" - } - - # Extract the version without 'v' prefix - $lastVersion = $lastTag -replace 'v', '' - Write-Information "Last version: $lastVersion" -Tags "Get-VersionInfoFromGit" - - # Parse previous version - $wasPrerelease = $lastVersion.Contains('-') - $cleanVersion = $lastVersion -replace '-alpha.*$', '' -replace '-beta.*$', '' -replace '-rc.*$', '' -replace '-pre.*$', '' - - $parts = $cleanVersion -split '\.' - $lastMajor = [int]$parts[0] - $lastMinor = [int]$parts[1] - $lastPatch = [int]$parts[2] - $lastPrereleaseNum = 0 - - # Extract prerelease number if applicable - if ($wasPrerelease -and $lastVersion -match '-(?:pre|alpha|beta|rc)\.(\d+)') { - $lastPrereleaseNum = [int]$Matches[1] - } - - # Determine version increment type based on commit range - Write-Information "$($script:lineEnding)Getting commits to analyze..." -Tags "Get-VersionInfoFromGit" - - # Get the first commit in repo for fallback - $firstCommit = "git rev-list HEAD" | Invoke-ExpressionWithLogging -Tags "Get-VersionInfoFromGit" - if ($firstCommit -is [array] -and @($firstCommit).Count -gt 0) { - $firstCommit = $firstCommit[-1] - } - Write-Information "First commit: $firstCommit" -Tags "Get-VersionInfoFromGit" - - # Find the last tag's commit - $lastTagCommit = "" - if ($usingFallbackTag) { - $lastTagCommit = $firstCommit - Write-Information "Using first commit as starting point: $firstCommit" -Tags "Get-VersionInfoFromGit" - } else { - $lastTagCommit = "git rev-list -n 1 $lastTag" | Invoke-ExpressionWithLogging -Tags "Get-VersionInfoFromGit" - Write-Information "Last tag commit: $lastTagCommit" -Tags "Get-VersionInfoFromGit" - } - - # Define the commit range to analyze - $commitRange = "$lastTagCommit..$CommitHash" - Write-Information "Analyzing commit range: $commitRange" -Tags "Get-VersionInfoFromGit" - - # Get the increment type - $incrementInfo = Get-VersionType -Range $commitRange - $incrementType = $incrementInfo.Type - $incrementReason = $incrementInfo.Reason - - # If type is "skip", return the current version without bumping - if ($incrementType -eq "skip") { - Write-Information "Version increment type: $incrementType" -Tags "Get-VersionInfoFromGit" - Write-Information "Reason: $incrementReason" -Tags "Get-VersionInfoFromGit" - - # Use the same version, don't increment - $newVersion = $lastVersion - - return [PSCustomObject]@{ - Success = $true - Error = "" - Data = [PSCustomObject]@{ - Version = $newVersion - Major = $lastMajor - Minor = $lastMinor - Patch = $lastPatch - IsPrerelease = $wasPrerelease - PrereleaseNumber = $lastPrereleaseNum - PrereleaseLabel = if ($wasPrerelease) { ($lastVersion -split '-')[1].Split('.')[0] } else { "pre" } - LastTag = $lastTag - LastVersion = $lastVersion - LastVersionMajor = $lastMajor - LastVersionMinor = $lastMinor - LastVersionPatch = $lastPatch - WasPrerelease = $wasPrerelease - LastVersionPrereleaseNumber = $lastPrereleaseNum - VersionIncrement = $incrementType - IncrementReason = $incrementReason - FirstCommit = $firstCommit - LastCommit = $CommitHash - LastTagCommit = $lastTagCommit - UsingFallbackTag = $usingFallbackTag - CommitRange = $commitRange - } - } - } - - # Initialize new version with current values - $newMajor = $lastMajor - $newMinor = $lastMinor - $newPatch = $lastPatch - $newPrereleaseNum = 0 - $isPrerelease = $false - $prereleaseLabel = "pre" - - Write-Information "$($script:lineEnding)Calculating new version..." -Tags "Get-VersionInfoFromGit" - - # Calculate new version based on increment type - switch ($incrementType) { - 'major' { - $newMajor = $lastMajor + 1 - $newMinor = 0 - $newPatch = 0 - Write-Information "Incrementing major version: $lastMajor.$lastMinor.$lastPatch -> $newMajor.0.0" -Tags "Get-VersionInfoFromGit" - } - 'minor' { - $newMinor = $lastMinor + 1 - $newPatch = 0 - Write-Information "Incrementing minor version: $lastMajor.$lastMinor.$lastPatch -> $lastMajor.$newMinor.0" -Tags "Get-VersionInfoFromGit" - } - 'patch' { - if (-not $wasPrerelease) { - $newPatch = $lastPatch + 1 - Write-Information "Incrementing patch version: $lastMajor.$lastMinor.$lastPatch -> $lastMajor.$lastMinor.$newPatch" -Tags "Get-VersionInfoFromGit" - } else { - Write-Information "Converting prerelease to stable version: $lastVersion -> $lastMajor.$lastMinor.$lastPatch" -Tags "Get-VersionInfoFromGit" - } - } - 'prerelease' { - if ($wasPrerelease) { - # Bump prerelease number - $newPrereleaseNum = $lastPrereleaseNum + 1 - $isPrerelease = $true - Write-Information "Incrementing prerelease: $lastVersion -> $lastMajor.$lastMinor.$lastPatch-$prereleaseLabel.$newPrereleaseNum" -Tags "Get-VersionInfoFromGit" - } else { - # Start new prerelease series - $newPatch = $lastPatch + 1 - $newPrereleaseNum = 1 - $isPrerelease = $true - Write-Information "Starting new prerelease: $lastVersion -> $lastMajor.$lastMinor.$newPatch-$prereleaseLabel.1" -Tags "Get-VersionInfoFromGit" - } - } - } - - # Build version string - $newVersion = "$newMajor.$newMinor.$newPatch" - if ($isPrerelease) { - $newVersion += "-$prereleaseLabel.$newPrereleaseNum" - } - - Write-Information "$($script:lineEnding)Version decision:" -Tags "Get-VersionInfoFromGit" - Write-Information "Previous version: $lastVersion" -Tags "Get-VersionInfoFromGit" - Write-Information "New version: $newVersion" -Tags "Get-VersionInfoFromGit" - Write-Information "Reason: $incrementReason" -Tags "Get-VersionInfoFromGit" - - try { - # Return comprehensive object with standard format - return [PSCustomObject]@{ - Success = $true - Error = "" - Data = [PSCustomObject]@{ - Version = $newVersion - Major = $newMajor - Minor = $newMinor - Patch = $newPatch - IsPrerelease = $isPrerelease - PrereleaseNumber = $newPrereleaseNum - PrereleaseLabel = $prereleaseLabel - LastTag = $lastTag - LastVersion = $lastVersion - LastVersionMajor = $lastMajor - LastVersionMinor = $lastMinor - LastVersionPatch = $lastPatch - WasPrerelease = $wasPrerelease - LastVersionPrereleaseNumber = $lastPrereleaseNum - VersionIncrement = $incrementType - IncrementReason = $incrementReason - FirstCommit = $firstCommit - LastCommit = $CommitHash - LastTagCommit = $lastTagCommit - UsingFallbackTag = $usingFallbackTag - CommitRange = $commitRange - } - } - } - catch { - return [PSCustomObject]@{ - Success = $false - Error = $_.ToString() - Data = [PSCustomObject]@{ - ErrorDetails = $_.Exception.Message - StackTrace = $_.ScriptStackTrace - } - StackTrace = $_.ScriptStackTrace - } - } -} - -function New-Version { - <# - .SYNOPSIS - Creates a new version file and sets environment variables. - .DESCRIPTION - Generates a new version number based on git history, writes it to version files, - and optionally sets GitHub environment variables for use in Actions. - .PARAMETER CommitHash - The Git commit hash being built. - .PARAMETER OutputPath - Optional path to write the version file to. Defaults to workspace root. - #> - [CmdletBinding()] - [OutputType([string])] - param ( - [Parameter(Mandatory=$true)] - [string]$CommitHash, - [string]$OutputPath = "" - ) - - # Get complete version information object - $versionInfo = Get-VersionInfoFromGit -CommitHash $CommitHash - - # Write version file with correct line ending - $filePath = if ($OutputPath) { Join-Path $OutputPath "VERSION.md" } else { "VERSION.md" } - $version = $versionInfo.Data.Version.Trim() - [System.IO.File]::WriteAllText($filePath, $version + $script:lineEnding, [System.Text.UTF8Encoding]::new($false)) | Write-InformationStream -Tags "New-Version" - - Write-Information "Previous version: $($versionInfo.Data.LastVersion), New version: $($versionInfo.Data.Version)" -Tags "New-Version" - - return $versionInfo.Data.Version -} - -#endregion - -#region License Management - -function New-License { - <# - .SYNOPSIS - Creates a license file from template. - .DESCRIPTION - Generates a LICENSE.md file using the template and repository information. - .PARAMETER ServerUrl - The GitHub server URL. - .PARAMETER Owner - The repository owner/organization. - .PARAMETER Repository - The repository name. - .PARAMETER OutputPath - Optional path to write the license file to. Defaults to workspace root. - #> - [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] - [string]$ServerUrl, - [Parameter(Mandatory=$true)] - [string]$Owner, - [Parameter(Mandatory=$true)] - [string]$Repository, - [string]$OutputPath = "" - ) - - if (-not (Test-Path $script:LICENSE_TEMPLATE)) { - throw "License template not found at: $script:LICENSE_TEMPLATE" - } - - $year = (Get-Date).Year - $content = Get-Content $script:LICENSE_TEMPLATE -Raw - - # Project URL - $projectUrl = "$ServerUrl/$Repository" - $content = $content.Replace('{PROJECT_URL}', $projectUrl) - - # Copyright line - $copyright = "Copyright (c) 2023-$year $Owner contributors" - $content = $content.Replace('{COPYRIGHT}', $copyright) - - # Normalize line endings - $content = $content.ReplaceLineEndings($script:lineEnding) - - $copyrightFilePath = if ($OutputPath) { Join-Path $OutputPath "COPYRIGHT.md" } else { "COPYRIGHT.md" } - [System.IO.File]::WriteAllText($copyrightFilePath, $copyright + $script:lineEnding, [System.Text.UTF8Encoding]::new($false)) | Write-InformationStream -Tags "New-License" - - $filePath = if ($OutputPath) { Join-Path $OutputPath "LICENSE.md" } else { "LICENSE.md" } - [System.IO.File]::WriteAllText($filePath, $content, [System.Text.UTF8Encoding]::new($false)) | Write-InformationStream -Tags "New-License" - - Write-Information "License file created at: $filePath" -Tags "New-License" -} - -#endregion - -#region Changelog Management - -function ConvertTo-FourComponentVersion { - <# - .SYNOPSIS - Converts a version tag to a four-component version for comparison. - .DESCRIPTION - Standardizes version tags to a four-component version (major.minor.patch.prerelease) for easier comparison. - .PARAMETER VersionTag - The version tag to convert. - #> - [CmdletBinding()] - [OutputType([string])] - param ( - [Parameter(Mandatory=$true)] - [string]$VersionTag - ) - - $version = $VersionTag -replace 'v', '' - $version = $version -replace '-alpha', '' -replace '-beta', '' -replace '-rc', '' -replace '-pre', '' - $versionComponents = $version -split '\.' - $versionMajor = [int]$versionComponents[0] - $versionMinor = [int]$versionComponents[1] - $versionPatch = [int]$versionComponents[2] - $versionPrerelease = 0 - - if (@($versionComponents).Count -gt 3) { - $versionPrerelease = [int]$versionComponents[3] - } - - return "$versionMajor.$versionMinor.$versionPatch.$versionPrerelease" -} - -function Get-VersionNotes { - <# - .SYNOPSIS - Generates changelog notes for a specific version range. - .DESCRIPTION - Creates formatted changelog entries for commits between two version tags. - .PARAMETER Tags - All available tags in the repository. - .PARAMETER FromTag - The starting tag of the range. - .PARAMETER ToTag - The ending tag of the range. - .PARAMETER ToSha - Optional specific commit SHA to use as the range end. - #> - [CmdletBinding()] - [OutputType([string])] - param ( - [Parameter(Mandatory=$true)] - [AllowEmptyCollection()] - [string[]]$Tags, - [Parameter(Mandatory=$true)] - [string]$FromTag, - [Parameter(Mandatory=$true)] - [string]$ToTag, - [Parameter()] - [string]$ToSha = "" - ) - - # Define common patterns used for filtering commits - $EXCLUDE_BOTS = '^(?!.*(\[bot\]|github|ProjectDirector|SyncFileContents)).*$' - $EXCLUDE_PRS = '^.*(Merge pull request|Merge branch ''main''|Updated packages in|Update.*package version).*$' - - # Convert tags to comparable versions - $toVersion = ConvertTo-FourComponentVersion -VersionTag $ToTag - $fromVersion = ConvertTo-FourComponentVersion -VersionTag $FromTag - - # Parse components for comparison - $toVersionComponents = $toVersion -split '\.' - $toVersionMajor = [int]$toVersionComponents[0] - $toVersionMinor = [int]$toVersionComponents[1] - $toVersionPatch = [int]$toVersionComponents[2] - $toVersionPrerelease = [int]$toVersionComponents[3] - - $fromVersionComponents = $fromVersion -split '\.' - $fromVersionMajor = [int]$fromVersionComponents[0] - $fromVersionMinor = [int]$fromVersionComponents[1] - $fromVersionPatch = [int]$fromVersionComponents[2] - $fromVersionPrerelease = [int]$fromVersionComponents[3] - - # Calculate previous version numbers for finding the correct tag - $fromMajorVersionNumber = $toVersionMajor - 1 - $fromMinorVersionNumber = $toVersionMinor - 1 - $fromPatchVersionNumber = $toVersionPatch - 1 - $fromPrereleaseVersionNumber = $toVersionPrerelease - 1 - - # Determine version type and search tag - $searchTag = $FromTag - $versionType = "unknown" - - if ($toVersionPrerelease -ne 0) { - $versionType = "prerelease" - $searchTag = "$toVersionMajor.$toVersionMinor.$toVersionPatch.$fromPrereleaseVersionNumber" - } - else { - if ($toVersionPatch -gt $fromVersionPatch) { - $versionType = "patch" - $searchTag = "$toVersionMajor.$toVersionMinor.$fromPatchVersionNumber.0" - } - if ($toVersionMinor -gt $fromVersionMinor) { - $versionType = "minor" - $searchTag = "$toVersionMajor.$fromMinorVersionNumber.0.0" - } - if ($toVersionMajor -gt $fromVersionMajor) { - $versionType = "major" - $searchTag = "$fromMajorVersionNumber.0.0.0" - } - } - - # Handle case where version is same but prerelease was dropped - if ($toVersionMajor -eq $fromVersionMajor -and - $toVersionMinor -eq $fromVersionMinor -and - $toVersionPatch -eq $fromVersionPatch -and - $toVersionPrerelease -eq 0 -and - $fromVersionPrerelease -ne 0) { - $versionType = "patch" - $searchTag = "$toVersionMajor.$toVersionMinor.$fromPatchVersionNumber.0" - } - - if ($searchTag.Contains("-")) { - $searchTag = $FromTag - } - - $searchVersion = ConvertTo-FourComponentVersion -VersionTag $searchTag - - if ($FromTag -ne "v0.0.0") { - $foundSearchTag = $false - $Tags | ForEach-Object { - if (-not $foundSearchTag) { - $otherTag = $_ - $otherVersion = ConvertTo-FourComponentVersion -VersionTag $otherTag - if ($searchVersion -eq $otherVersion) { - $foundSearchTag = $true - $searchTag = $otherTag - } - } - } - - if (-not $foundSearchTag) { - $searchTag = $FromTag - } - } - - $rangeFrom = $searchTag - if ($rangeFrom -eq "v0.0.0" -or $rangeFrom -eq "0.0.0.0" -or $rangeFrom -eq "1.0.0.0") { - $rangeFrom = "" - } - - $rangeTo = $ToSha - if ($rangeTo -eq "") { - $rangeTo = $ToTag - } - - # Determine proper commit range - $isNewestVersion = $false - if ($ToSha -ne "") { - # If ToSha is provided, this is likely the newest version being generated - $isNewestVersion = $true - } - - # Get the actual commit SHA for the from tag if it exists - $range = "" - $fromSha = "" - $gitSuccess = $true - - if ($rangeFrom -ne "") { - try { - # Try to get the SHA for the from tag, but don't error if it doesn't exist - $fromSha = "git rev-list -n 1 $rangeFrom 2>`$null" | Invoke-ExpressionWithLogging -ErrorAction SilentlyContinue - if ($LASTEXITCODE -ne 0) { - Write-Information "Warning: Could not find SHA for tag $rangeFrom. Using fallback range." -Tags "Get-VersionNotes" - $gitSuccess = $false - $fromSha = "" - } - - # For the newest version with SHA provided (not yet tagged): - if ($isNewestVersion -and $ToSha -ne "" -and $gitSuccess) { - $range = "$fromSha..$ToSha" - } elseif ($gitSuccess) { - # For already tagged versions, get the SHA for the to tag - $toShaResolved = "git rev-list -n 1 $rangeTo 2>`$null" | Invoke-ExpressionWithLogging -ErrorAction SilentlyContinue - if ($LASTEXITCODE -ne 0) { - Write-Information "Warning: Could not find SHA for tag $rangeTo. Using fallback range." -Tags "Get-VersionNotes" - $gitSuccess = $false - } - else { - $range = "$fromSha..$toShaResolved" - } - } - } - catch { - Write-Information "Error getting commit SHAs: $_" -Tags "Get-VersionNotes" - $gitSuccess = $false - } - } - - # Handle case with no FROM tag (first version) or failed git commands - if ($rangeFrom -eq "" -or -not $gitSuccess) { - if ($ToSha -ne "") { - $range = $ToSha - } else { - try { - $toShaResolved = "git rev-list -n 1 $rangeTo 2>`$null" | Invoke-ExpressionWithLogging -ErrorAction SilentlyContinue - if ($LASTEXITCODE -eq 0) { - $range = $toShaResolved - } else { - # If we can't resolve either tag, use HEAD as fallback - $range = "HEAD" - } - } - catch { - Write-Information "Error resolving tag SHA: $_. Using HEAD instead." -Tags "Get-VersionNotes" - $range = "HEAD" - } - } - } - - # Debug output - Write-Information "Processing range: $range (From: $rangeFrom, To: $rangeTo)" -Tags "Get-VersionNotes" - - # For repositories with no valid tags or no commits between tags, handle gracefully - if ([string]::IsNullOrWhiteSpace($range) -or $range -eq ".." -or $range -match '^\s*$') { - Write-Information "No valid commit range found. Creating a placeholder entry." -Tags "Get-VersionNotes" - $versionType = "initial" # Mark as initial release - $versionChangelog = "## $ToTag (initial release)$script:lineEnding$script:lineEnding" - $versionChangelog += "Initial version.$script:lineEnding$script:lineEnding" - return ($versionChangelog.Trim() + $script:lineEnding) - } - - # Try with progressively more relaxed filtering to ensure we show commits - $rawCommits = @() - - try { - # Get full commit info with hash to ensure uniqueness - $format = '%h|%s|%aN' - - # First try with standard filters - $rawCommitsResult = "git log --pretty=format:`"$format`" --perl-regexp --regexp-ignore-case --grep=`"$EXCLUDE_PRS`" --invert-grep --committer=`"$EXCLUDE_BOTS`" --author=`"$EXCLUDE_BOTS`" `"$range`"" | Invoke-ExpressionWithLogging -ErrorAction SilentlyContinue - - # Safely convert to array and handle any errors - $rawCommits = ConvertTo-ArraySafe -InputObject $rawCommitsResult - - # Additional safety check - ensure we have a valid array with Count property - if ($null -eq $rawCommits) { - Write-Information "rawCommits is null, creating empty array" -Tags "Get-VersionNotes" - $rawCommits = @() - } - - # Use @() subexpression to safely get count - $rawCommitsCount = @($rawCommits).Count - - # If no commits found, try with just PR exclusion but no author filtering - if ($rawCommitsCount -eq 0) { - Write-Information "No commits found with standard filters, trying with relaxed author/committer filters..." -Tags "Get-VersionNotes" - $rawCommitsResult = "git log --pretty=format:`"$format`" --perl-regexp --regexp-ignore-case --grep=`"$EXCLUDE_PRS`" --invert-grep `"$range`"" | Invoke-ExpressionWithLogging -ErrorAction SilentlyContinue - - # Safely convert to array and handle any errors - $rawCommits = ConvertTo-ArraySafe -InputObject $rawCommitsResult - - # Additional safety check - if ($null -eq $rawCommits) { - Write-Information "rawCommits is null, creating empty array" -Tags "Get-VersionNotes" - $rawCommits = @() - } - } - - # Use @() subexpression to safely get count - $rawCommitsCount = @($rawCommits).Count - - # If still no commits, try with no filtering at all - show everything in the range - if ($rawCommitsCount -eq 0) { - Write-Information "Still no commits found, trying with no filters..." -Tags "Get-VersionNotes" - $rawCommitsResult = "git log --pretty=format:`"$format`" `"$range`"" | Invoke-ExpressionWithLogging -ErrorAction SilentlyContinue - - # Safely convert to array and handle any errors - $rawCommits = ConvertTo-ArraySafe -InputObject $rawCommitsResult - - # Additional safety check - if ($null -eq $rawCommits) { - Write-Information "rawCommits is null, creating empty array" -Tags "Get-VersionNotes" - $rawCommits = @() - } - - # Use @() subexpression to safely get count - $rawCommitsCount = @($rawCommits).Count - - # If it's a prerelease version, include also version update commits - if ($versionType -eq "prerelease" -and $rawCommitsCount -eq 0) { - Write-Information "Looking for version update commits for prerelease..." -Tags "Get-VersionNotes" - $rawCommitsResult = "git log --pretty=format:`"$format`" --grep=`"Update VERSION to`" `"$range`"" | Invoke-ExpressionWithLogging -ErrorAction SilentlyContinue - - # Safely convert to array and handle any errors - $rawCommits = ConvertTo-ArraySafe -InputObject $rawCommitsResult - - # Additional safety check - if ($null -eq $rawCommits) { - Write-Information "rawCommits is null, creating empty array" -Tags "Get-VersionNotes" - $rawCommits = @() - } - } - } - } - catch { - Write-Information "Error during git log operations: $_" -Tags "Get-VersionNotes" - $rawCommits = @() - } - - # Process raw commits into structured format - $structuredCommits = @() - foreach ($commit in $rawCommits) { - $parts = $commit -split '\|' - # Use @() subexpression to safely get count - if (@($parts).Count -ge 3) { - $structuredCommits += [PSCustomObject]@{ - Hash = $parts[0] - Subject = $parts[1] - Author = $parts[2] - FormattedEntry = "$($parts[1]) ([@$($parts[2])](https://github.com/$($parts[2])))" - } - } - } - - # Get unique commits based on hash (ensures unique commits) - $uniqueCommits = ConvertTo-ArraySafe -InputObject ($structuredCommits | Sort-Object -Property Hash -Unique | ForEach-Object { $_.FormattedEntry }) - - # Use @() subexpression to safely get count - $uniqueCommitsCount = @($uniqueCommits).Count - Write-Information "Found $uniqueCommitsCount commits for $ToTag" -Tags "Get-VersionNotes" - - # Format changelog entry - $versionChangelog = "" - if ($uniqueCommitsCount -gt 0) { - $versionChangelog = "## $ToTag" - if ($versionType -ne "unknown") { - $versionChangelog += " ($versionType)" - } - $versionChangelog += "$script:lineEnding$script:lineEnding" - - if ($rangeFrom -ne "") { - $versionChangelog += "Changes since ${rangeFrom}:$script:lineEnding$script:lineEnding" - } - - # Only filter out version updates for non-prerelease versions - if ($versionType -ne "prerelease") { - $filteredCommits = $uniqueCommits | Where-Object { -not $_.Contains("Update VERSION to") -and -not $_.Contains("[skip ci]") } - } else { - $filteredCommits = $uniqueCommits | Where-Object { -not $_.Contains("[skip ci]") } - } - - foreach ($commit in $filteredCommits) { - $versionChangelog += "- $commit$script:lineEnding" - } - $versionChangelog += "$script:lineEnding" - } elseif ($versionType -eq "prerelease") { - # For prerelease versions with no detected commits, include a placeholder entry - $versionChangelog = "## $ToTag (prerelease)$script:lineEnding$script:lineEnding" - $versionChangelog += "Incremental prerelease update.$script:lineEnding$script:lineEnding" - } else { - # For all other versions with no commits, create a placeholder message - $versionChangelog = "## $ToTag" - if ($versionType -ne "unknown") { - $versionChangelog += " ($versionType)" - } - $versionChangelog += "$script:lineEnding$script:lineEnding" - - if ($FromTag -eq "v0.0.0") { - $versionChangelog += "Initial release.$script:lineEnding$script:lineEnding" - } else { - $versionChangelog += "No significant changes detected since $FromTag.$script:lineEnding$script:lineEnding" - } - } - - return ($versionChangelog.Trim() + $script:lineEnding) -} - -function New-Changelog { - <# - .SYNOPSIS - Creates a complete changelog file. - .DESCRIPTION - Generates a comprehensive CHANGELOG.md with entries for all versions. - .PARAMETER Version - The current version number being released. - .PARAMETER CommitHash - The Git commit hash being released. - .PARAMETER OutputPath - Optional path to write the changelog file to. Defaults to workspace root. - .PARAMETER IncludeAllVersions - Whether to include all previous versions in the changelog. Defaults to $true. - .PARAMETER LatestChangelogFile - Optional path to write the latest version's changelog to. Defaults to "LATEST_CHANGELOG.md". - #> - [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] - [string]$Version, - [Parameter(Mandatory=$true)] - [string]$CommitHash, - [string]$OutputPath = "", - [bool]$IncludeAllVersions = $true, - [string]$LatestChangelogFile = "LATEST_CHANGELOG.md" - ) - - # Configure git versionsort to correctly handle prereleases - $suffixes = @('-alpha', '-beta', '-rc', '-pre') - foreach ($suffix in $suffixes) { - "git config versionsort.suffix `"$suffix`"" | Invoke-ExpressionWithLogging -Tags "Get-GitTags" | Write-InformationStream -Tags "Get-GitTags" - } - - # Get all tags sorted by version - $tags = Get-GitTags - $changelog = "" - - # Make sure tags is always an array - $tags = ConvertTo-ArraySafe -InputObject $tags - - # Check if we have any tags at all - $hasTags = $tags.Count -gt 0 - - # For first release, there's no previous tag to compare against - $previousTag = 'v0.0.0' - - # If we have tags, find the most recent one to compare against - if ($hasTags) { - $previousTag = $tags[0] # Most recent tag - } - - # Always add entry for current/new version (comparing current commit to previous tag or initial state) - $currentTag = "v$Version" - Write-Information "Generating changelog from $previousTag to $currentTag (commit: $CommitHash)" -Tags "New-Changelog" - $versionNotes = Get-VersionNotes -Tags $tags -FromTag $previousTag -ToTag $currentTag -ToSha $CommitHash - - # Store the latest version's notes for later use in GitHub releases - $latestVersionNotes = "" - - # If we have changes, add them to the changelog - if (-not [string]::IsNullOrWhiteSpace($versionNotes)) { - $changelog += $versionNotes - $latestVersionNotes = $versionNotes - } else { - # Handle no changes detected case - add a minimal entry - $minimalEntry = "## $currentTag$script:lineEnding$script:lineEnding" - $minimalEntry += "Initial release or no significant changes since $previousTag.$script:lineEnding$script:lineEnding" - - $changelog += $minimalEntry - $latestVersionNotes = $minimalEntry - } - - # Add entries for all previous versions if requested - if ($IncludeAllVersions -and $hasTags) { - $tagIndex = 0 - - foreach ($tag in $tags) { - if ($tag -like "v*") { - $previousTag = "v0.0.0" - if ($tagIndex -lt $tags.Count - 1) { - $previousTag = $tags[$tagIndex + 1] - } - - if (-not ($previousTag -like "v*")) { - $previousTag = "v0.0.0" - } - - $versionNotes = Get-VersionNotes -Tags $tags -FromTag $previousTag -ToTag $tag - $changelog += $versionNotes - } - $tagIndex++ - } - } - - # Write changelog to file - $filePath = if ($OutputPath) { Join-Path $OutputPath "CHANGELOG.md" } else { "CHANGELOG.md" } - - # Normalize line endings in changelog content - $changelog = $changelog.ReplaceLineEndings($script:lineEnding) - - [System.IO.File]::WriteAllText($filePath, $changelog, [System.Text.UTF8Encoding]::new($false)) | Write-InformationStream -Tags "New-Changelog" - - # Write latest version's changelog to separate file for GitHub releases - $latestPath = if ($OutputPath) { Join-Path $OutputPath $LatestChangelogFile } else { $LatestChangelogFile } - $latestVersionNotes = $latestVersionNotes.ReplaceLineEndings($script:lineEnding) - - # Truncate release notes if they exceed NuGet's 35,000 character limit - $maxLength = 35000 - if ($latestVersionNotes.Length -gt $maxLength) { - Write-Information "Release notes exceed $maxLength characters ($($latestVersionNotes.Length)). Truncating to fit NuGet limit." -Tags "New-Changelog" - $truncationMessage = "$script:lineEnding$script:lineEnding... (truncated due to NuGet length limits)" - $targetLength = $maxLength - $truncationMessage.Length - 10 # Extra buffer for safety - $truncatedNotes = $latestVersionNotes.Substring(0, $targetLength) - $truncatedNotes += $truncationMessage - $latestVersionNotes = $truncatedNotes - Write-Information "Truncated release notes to $($latestVersionNotes.Length) characters" -Tags "New-Changelog" - - # Final safety check - ensure we never exceed the limit - if ($latestVersionNotes.Length -gt $maxLength) { - Write-Warning "Truncated release notes still exceed limit ($($latestVersionNotes.Length) > $maxLength). Further truncating..." -Tags "New-Changelog" - $latestVersionNotes = $latestVersionNotes.Substring(0, $maxLength - 50) + "... (truncated)" - } - } - - [System.IO.File]::WriteAllText($latestPath, $latestVersionNotes, [System.Text.UTF8Encoding]::new($false)) | Write-InformationStream -Tags "New-Changelog" - Write-Information "Latest version changelog saved to: $latestPath" -Tags "New-Changelog" - - $versionCount = if ($hasTags) { $tags.Count + 1 } else { 1 } - Write-Information "Changelog generated with entries for $versionCount versions" -Tags "New-Changelog" -} - -#endregion - -#region Metadata Management - -function Update-ProjectMetadata { - <# - .SYNOPSIS - Updates project metadata files based on build configuration. - .DESCRIPTION - Generates and updates version information, license, changelog, and other metadata files for a project. - This function centralizes all metadata generation to ensure consistency across project documentation. - - Metadata files are always generated, but commits and pushes are only performed in official repositories - (not forks). This is controlled by the BuildConfiguration.IsOfficial flag. - .PARAMETER BuildConfiguration - The build configuration object containing paths, version info, and GitHub details. - Should be obtained from Get-BuildConfiguration. The IsOfficial property determines whether - metadata changes will be committed and pushed. - .PARAMETER Authors - Optional array of author names to include in the AUTHORS.md file. - .PARAMETER CommitMessage - Optional commit message to use when committing metadata changes. - Defaults to "[bot][skip ci] Update Metadata". - .EXAMPLE - $config = Get-BuildConfiguration -GitRef "refs/heads/main" -GitSha "abc123" -GitHubOwner "myorg" -GitHubRepo "myproject" - Update-ProjectMetadata -BuildConfiguration $config - .EXAMPLE - Update-ProjectMetadata -BuildConfiguration $config -Authors @("Developer 1", "Developer 2") -CommitMessage "Update project documentation" - .OUTPUTS - PSCustomObject with Success, Error, and Data properties. - Data contains Version, ReleaseHash, and HasChanges information. - #> - [CmdletBinding()] - [OutputType([PSCustomObject])] - param( - [Parameter(Mandatory = $true)] - [PSCustomObject]$BuildConfiguration, - [Parameter(Mandatory = $false)] - [string[]]$Authors = @(), - [Parameter(Mandatory = $false)] - [string]$CommitMessage = "[bot][skip ci] Update Metadata" - ) - - try { - Write-Information "Generating version information..." -Tags "Update-ProjectMetadata" - $version = New-Version -CommitHash $BuildConfiguration.ReleaseHash - Write-Information "Version: $version" -Tags "Update-ProjectMetadata" - - Write-Information "Generating license..." -Tags "Update-ProjectMetadata" - New-License -ServerUrl $BuildConfiguration.ServerUrl -Owner $BuildConfiguration.GitHubOwner -Repository $BuildConfiguration.GitHubRepo | Write-InformationStream -Tags "Update-ProjectMetadata" - - Write-Information "Generating changelog..." -Tags "Update-ProjectMetadata" - # Generate both full changelog and latest version changelog - try { - New-Changelog -Version $version -CommitHash $BuildConfiguration.ReleaseHash -LatestChangelogFile $BuildConfiguration.LatestChangelogFile | Write-InformationStream -Tags "Update-ProjectMetadata" - } - catch { - $errorMessage = $_.ToString() - Write-Information "Failed to generate complete changelog: $errorMessage" -Tags "Update-ProjectMetadata" - Write-Information "Creating minimal changelog instead..." -Tags "Update-ProjectMetadata" - - # Create a minimal changelog - $minimalChangelog = "## v$version$($script:lineEnding)$($script:lineEnding)" - $minimalChangelog += "Initial release or repository with no prior history.$($script:lineEnding)$($script:lineEnding)" - - [System.IO.File]::WriteAllText("CHANGELOG.md", $minimalChangelog, [System.Text.UTF8Encoding]::new($false)) | Write-InformationStream -Tags "Update-ProjectMetadata" - [System.IO.File]::WriteAllText($BuildConfiguration.LatestChangelogFile, $minimalChangelog, [System.Text.UTF8Encoding]::new($false)) | Write-InformationStream -Tags "Update-ProjectMetadata" - } - - # Create AUTHORS.md if authors are provided - if (@($Authors).Count -gt 0) { - Write-Information "Generating authors file..." -Tags "Update-ProjectMetadata" - $authorsContent = "# Project Authors$script:lineEnding$script:lineEnding" - foreach ($author in $Authors) { - $authorsContent += "* $author$script:lineEnding" - } - [System.IO.File]::WriteAllText("AUTHORS.md", $authorsContent, [System.Text.UTF8Encoding]::new($false)) | Write-InformationStream -Tags "Update-ProjectMetadata" - } - - # Create AUTHORS.url - $authorsUrl = "[InternetShortcut]$($script:lineEnding)URL=$($BuildConfiguration.ServerUrl)/$($BuildConfiguration.GitHubOwner)" - [System.IO.File]::WriteAllText("AUTHORS.url", $authorsUrl, [System.Text.UTF8Encoding]::new($false)) | Write-InformationStream -Tags "Update-ProjectMetadata" - - # Create PROJECT_URL.url - $projectUrl = "[InternetShortcut]$($script:lineEnding)URL=$($BuildConfiguration.ServerUrl)/$($BuildConfiguration.GitHubRepo)" - [System.IO.File]::WriteAllText("PROJECT_URL.url", $projectUrl, [System.Text.UTF8Encoding]::new($false)) | Write-InformationStream -Tags "Update-ProjectMetadata" - - Write-Information "Adding files to git..." -Tags "Update-ProjectMetadata" - $filesToAdd = @( - "VERSION.md", - "LICENSE.md", - "AUTHORS.md", - "CHANGELOG.md", - "COPYRIGHT.md", - "PROJECT_URL.url", - "AUTHORS.url" - ) - - # Add latest changelog if it exists - if (Test-Path $BuildConfiguration.LatestChangelogFile) { - $filesToAdd += $BuildConfiguration.LatestChangelogFile - } - Write-Information "Files to add: $($filesToAdd -join ", ")" -Tags "Update-ProjectMetadata" - "git add $filesToAdd" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Update-ProjectMetadata" - - Write-Information "Checking for changes to commit..." -Tags "Update-ProjectMetadata" - $postStatus = "git status --porcelain" | Invoke-ExpressionWithLogging -Tags "Update-ProjectMetadata" | Out-String - $hasChanges = -not [string]::IsNullOrWhiteSpace($postStatus) - $statusMessage = if ($hasChanges) { 'Changes detected' } else { 'No changes' } - Write-Information "Git status: $statusMessage" -Tags "Update-ProjectMetadata" - - # Get the current commit hash regardless of whether we make changes - $currentHash = "git rev-parse HEAD" | Invoke-ExpressionWithLogging - Write-Information "Current commit hash: $currentHash" -Tags "Update-ProjectMetadata" - - if (-not [string]::IsNullOrWhiteSpace($postStatus)) { - # Only commit and push metadata changes in official repositories - if ($BuildConfiguration.IsOfficial) { - # Configure git user before committing - Set-GitIdentity | Write-InformationStream -Tags "Update-ProjectMetadata" - - Write-Information "Committing changes..." -Tags "Update-ProjectMetadata" - "git commit -m `"$CommitMessage`"" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Update-ProjectMetadata" - - Write-Information "Pushing changes..." -Tags "Update-ProjectMetadata" - "git push" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Update-ProjectMetadata" - - Write-Information "Getting release hash..." -Tags "Update-ProjectMetadata" - $releaseHash = "git rev-parse HEAD" | Invoke-ExpressionWithLogging - Write-Information "Metadata committed as $releaseHash" -Tags "Update-ProjectMetadata" - } else { - Write-Information "Skipping metadata commit/push (not an official repository)" -Tags "Update-ProjectMetadata" - $releaseHash = $currentHash - } - - Write-Information "Metadata update completed successfully with changes" -Tags "Update-ProjectMetadata" - Write-Information "Version: $version" -Tags "Update-ProjectMetadata" - Write-Information "Release Hash: $releaseHash" -Tags "Update-ProjectMetadata" - - return [PSCustomObject]@{ - Success = $true - Error = "" - Data = [PSCustomObject]@{ - Version = $version - ReleaseHash = $releaseHash - HasChanges = $true - } - } - } - else { - Write-Information "No changes to commit" -Tags "Update-ProjectMetadata" - Write-Information "Version: $version" -Tags "Update-ProjectMetadata" - Write-Information "Using current commit hash: $currentHash" -Tags "Update-ProjectMetadata" - - return [PSCustomObject]@{ - Success = $true - Error = "" - Data = [PSCustomObject]@{ - Version = $version - ReleaseHash = $currentHash - HasChanges = $false - } - } - } - } - catch { - $errorMessage = $_.ToString() - Write-Information "Failed to update metadata: $errorMessage" -Tags "Update-ProjectMetadata" - return [PSCustomObject]@{ - Success = $false - Error = $errorMessage - Data = [PSCustomObject]@{ - Version = $null - ReleaseHash = $null - HasChanges = $false - StackTrace = $_.ScriptStackTrace - } - } - } -} - -#endregion - -#region Build Operations - -function Invoke-DotNetRestore { - <# - .SYNOPSIS - Restores NuGet packages. - .DESCRIPTION - Runs dotnet restore to get all dependencies. - #> - [CmdletBinding()] - param() - - Write-StepHeader "Restoring Dependencies" -Tags "Invoke-DotNetRestore" - - # Execute command and stream output directly to console - "dotnet restore --locked-mode -logger:`"Microsoft.Build.Logging.ConsoleLogger,Microsoft.Build;Summary;ForceNoAlign;ShowTimestamp;ShowCommandLine;Verbosity=quiet`"" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Invoke-DotNetRestore" - Assert-LastExitCode "Restore failed" -} - -function Invoke-DotNetBuild { - <# - .SYNOPSIS - Builds the .NET solution. - .DESCRIPTION - Runs dotnet build with specified configuration. - .PARAMETER Configuration - The build configuration (Debug/Release). - .PARAMETER BuildArgs - Additional build arguments. - #> - [CmdletBinding()] - param ( - [string]$Configuration = "Release", - [string]$BuildArgs = "" - ) - - Write-StepHeader "Building Solution" -Tags "Invoke-DotNetBuild" - - try { - # First attempt with quiet verbosity - stream output directly - "dotnet build --configuration $Configuration -logger:`"Microsoft.Build.Logging.ConsoleLogger,Microsoft.Build;Summary;ForceNoAlign;ShowTimestamp;ShowCommandLine;Verbosity=quiet`" --no-incremental $BuildArgs --no-restore" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Invoke-DotNetBuild" - - if ($LASTEXITCODE -ne 0) { - Write-Information "Build failed with exit code $LASTEXITCODE. Retrying with detailed verbosity..." -Tags "Invoke-DotNetBuild" - - # Retry with more detailed verbosity - stream output directly - "dotnet build --configuration $Configuration -logger:`"Microsoft.Build.Logging.ConsoleLogger,Microsoft.Build;Summary;ForceNoAlign;ShowTimestamp;ShowCommandLine;Verbosity=quiet`" --no-incremental $BuildArgs --no-restore" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Invoke-DotNetBuild" - - # Still failed, show diagnostic info and throw error - if ($LASTEXITCODE -ne 0) { - Write-Information "Checking for common build issues:" -Tags "Invoke-DotNetBuild" - - # Check for project files - $projectFiles = @(Get-ChildItem -Recurse -Filter *.csproj) - Write-Information "Found $($projectFiles.Count) project files" -Tags "Invoke-DotNetBuild" - - foreach ($proj in $projectFiles) { - Write-Information " - $($proj.FullName)" -Tags "Invoke-DotNetBuild" - } - - Assert-LastExitCode "Build failed" - } - } - } - catch { - Write-Information "Exception during build process: $_" -Tags "Invoke-DotNetBuild" - throw - } -} - -function Invoke-DotNetTest { - <# - .SYNOPSIS - Runs dotnet test with code coverage collection. - .DESCRIPTION - Runs dotnet test with code coverage collection. - .PARAMETER Configuration - The build configuration to use. - .PARAMETER CoverageOutputPath - The path to output code coverage results. - #> - [CmdletBinding()] - param ( - [string]$Configuration = "Release", - [string]$CoverageOutputPath = "coverage" - ) - - Write-StepHeader "Running Tests with Coverage" -Tags "Invoke-DotNetTest" - - # Check if there are any test projects in the solution - $testProjects = @(Get-ChildItem -Recurse -Filter "*.csproj" | Where-Object { - $_.Name -match "\.Test\.csproj$" -or - $_.Directory.Name -match "\.Test$" -or - $_.Directory.Name -eq "Test" -or - (Select-String -Path $_.FullName -Pattern "true" -Quiet) - }) - - if ($testProjects.Count -eq 0) { - Write-Information "No test projects found in solution. Skipping test execution." -Tags "Invoke-DotNetTest" - return - } - - Write-Information "Found $($testProjects.Count) test project(s)" -Tags "Invoke-DotNetTest" - - # Ensure the TestResults directory exists - $testResultsPath = Join-Path $CoverageOutputPath "TestResults" - New-Item -Path $testResultsPath -ItemType Directory -Force | Out-Null - - # Run tests with both coverage collection and TRX logging for SonarQube - "dotnet test --configuration $Configuration --coverage --coverage-output-format xml --coverage-output `"coverage.xml`" --results-directory `"$testResultsPath`" --report-trx --report-trx-filename TestResults.trx" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Invoke-DotNetTest" - Assert-LastExitCode "Tests failed" - - # Find and copy coverage file to expected location for SonarQube - $coverageFiles = @(Get-ChildItem -Path . -Recurse -Filter "coverage.xml" -ErrorAction SilentlyContinue) - if ($coverageFiles.Count -gt 0) { - $latestCoverageFile = $coverageFiles | Sort-Object LastWriteTime -Descending | Select-Object -First 1 - $targetCoverageFile = Join-Path $CoverageOutputPath "coverage.xml" - Copy-Item -Path $latestCoverageFile.FullName -Destination $targetCoverageFile -Force - Write-Information "Coverage file copied to: $targetCoverageFile" -Tags "Invoke-DotNetTest" - } else { - Write-Information "Warning: No coverage file found" -Tags "Invoke-DotNetTest" - } -} - -function Invoke-DotNetPack { - <# - .SYNOPSIS - Creates NuGet packages. - .DESCRIPTION - Runs dotnet pack to create NuGet packages. - .PARAMETER Configuration - The build configuration (Debug/Release). - .PARAMETER OutputPath - The path to output packages to. - .PARAMETER Project - Optional specific project to package. If not provided, all projects are packaged. - .PARAMETER LatestChangelogFile - Optional path to the latest changelog file to use for PackageReleaseNotesFile. Defaults to "LATEST_CHANGELOG.md". - #> - [CmdletBinding()] - param ( - [string]$Configuration = "Release", - [Parameter(Mandatory=$true)] - [string]$OutputPath, - [string]$Project = "", - [string]$LatestChangelogFile = "LATEST_CHANGELOG.md" - ) - - Write-StepHeader "Packaging Libraries" -Tags "Invoke-DotNetPack" - - # Ensure output directory exists - New-Item -Path $OutputPath -ItemType Directory -Force | Write-InformationStream -Tags "Invoke-DotNetPack" - - # Check if any projects exist (excluding test projects) - $projectFiles = @(Get-ChildItem -Recurse -Filter *.csproj -ErrorAction SilentlyContinue | Where-Object { - -not ($_.Name -match "\.Tests?\.csproj$" -or - $_.Directory.Name -match "\.Tests?$" -or - $_.Directory.Name -eq "Tests" -or - $_.Directory.Name -eq "Test" -or - (Select-String -Path $_.FullName -Pattern "true" -Quiet)) - }) - if ($projectFiles.Count -eq 0) { - Write-Information "No .NET library projects found to package" -Tags "Invoke-DotNetPack" - return - } - - try { - # Override PackageReleaseNotes to use LATEST_CHANGELOG.md instead of full CHANGELOG.md - # Use PackageReleaseNotesFile property to avoid command line length limits and escaping issues - $releaseNotesProperty = "" - - if (Test-Path $LatestChangelogFile) { - # Get absolute path to the changelog file for MSBuild - $absoluteChangelogPath = (Resolve-Path $LatestChangelogFile).Path - Write-Information "Using release notes from file: $absoluteChangelogPath" -Tags "Invoke-DotNetPack" - - # Use PackageReleaseNotesFile property instead of PackageReleaseNotes to avoid command line issues - $releaseNotesProperty = "-p:PackageReleaseNotesFile=`"$absoluteChangelogPath`"" - Write-Information "Overriding PackageReleaseNotesFile with latest changelog file path" -Tags "Invoke-DotNetPack" - } else { - Write-Information "No latest changelog found, SDK will use full CHANGELOG.md (automatically truncated if needed)" -Tags "Invoke-DotNetPack" - } - - # Build either a specific project or all non-test projects - if ([string]::IsNullOrWhiteSpace($Project)) { - Write-Information "Packaging $($projectFiles.Count) non-test projects..." -Tags "Invoke-DotNetPack" - foreach ($proj in $projectFiles) { - $projName = [System.IO.Path]::GetFileNameWithoutExtension($proj) - Write-Information "Packaging project: $projName" -Tags "Invoke-DotNetPack" - "dotnet pack `"$proj`" --configuration $Configuration -logger:`"Microsoft.Build.Logging.ConsoleLogger,Microsoft.Build;Summary;ForceNoAlign;ShowTimestamp;ShowCommandLine;Verbosity=quiet`" --no-build --output $OutputPath $releaseNotesProperty" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Invoke-DotNetPack" - if ($LASTEXITCODE -ne 0) { - throw "Library packaging failed for $projName with exit code $LASTEXITCODE" - } - } - } else { - Write-Information "Packaging project: $Project" -Tags "Invoke-DotNetPack" - "dotnet pack $Project --configuration $Configuration -logger:`"Microsoft.Build.Logging.ConsoleLogger,Microsoft.Build;Summary;ForceNoAlign;ShowTimestamp;ShowCommandLine;Verbosity=quiet`" --no-build --output $OutputPath $releaseNotesProperty" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Invoke-DotNetPack" - if ($LASTEXITCODE -ne 0) { - throw "Library packaging failed with exit code $LASTEXITCODE" - } - } - - # Report on created packages - $packages = @(Get-ChildItem -Path $OutputPath -Filter *.nupkg -ErrorAction SilentlyContinue) - if ($packages.Count -gt 0) { - Write-Information "Created $($packages.Count) packages in $OutputPath" -Tags "Invoke-DotNetPack" - foreach ($package in $packages) { - Write-Information " - $($package.Name)" -Tags "Invoke-DotNetPack" - } - } else { - Write-Information "No packages were created (projects may not be configured for packaging)" -Tags "Invoke-DotNetPack" - } - } - catch { - $originalException = $_.Exception - Write-Information "Package creation failed: $originalException" -Tags "Invoke-DotNetPack" - throw "Library packaging failed: $originalException" - } -} - -function Invoke-DotNetPublish { - <# - .SYNOPSIS - Publishes .NET applications and creates winget-compatible packages. - .DESCRIPTION - Runs dotnet publish and creates zip archives for applications. - Also creates winget-compatible packages for multiple architectures if console applications are found. - Uses the build configuration to determine output paths and version information. - .PARAMETER Configuration - The build configuration (Debug/Release). Defaults to "Release". - .PARAMETER BuildConfiguration - The build configuration object containing output paths, version, and other settings. - This object should be obtained from Get-BuildConfiguration. - .OUTPUTS - None. Creates published applications, zip archives, and winget packages in the specified output paths. - #> - [CmdletBinding()] - param ( - [string]$Configuration = "Release", - [Parameter(Mandatory=$true)] - [PSCustomObject]$BuildConfiguration - ) - - Write-StepHeader "Publishing Applications" -Tags "Invoke-DotNetPublish" - - # Find all projects (excluding test projects) - $projectFiles = @(Get-ChildItem -Recurse -Filter *.csproj -ErrorAction SilentlyContinue | Where-Object { - -not ($_.Name -match "\.Tests?\.csproj$" -or - $_.Directory.Name -match "\.Tests?$" -or - $_.Directory.Name -eq "Tests" -or - $_.Directory.Name -eq "Test" -or - (Select-String -Path $_.FullName -Pattern "true" -Quiet)) - }) - if ($projectFiles.Count -eq 0) { - Write-Information "No .NET application projects found to publish" -Tags "Invoke-DotNetPublish" - return - } - - # Clean output directory if it exists - if (Test-Path $BuildConfiguration.OutputPath) { - Remove-Item -Recurse -Force $BuildConfiguration.OutputPath | Write-InformationStream -Tags "Invoke-DotNetPublish" - } - - # Ensure staging directory exists - New-Item -Path $BuildConfiguration.StagingPath -ItemType Directory -Force | Write-InformationStream -Tags "Invoke-DotNetPublish" - - $publishedCount = 0 - $version = $BuildConfiguration.Version - - # Define target architectures for comprehensive publishing across all platforms - $architectures = @( - # Windows - "win-x64", "win-x86", "win-arm64", - # Linux - "linux-x64", "linux-arm64", - # macOS - "osx-x64", "osx-arm64" - ) - - foreach ($csproj in $projectFiles) { - $projName = [System.IO.Path]::GetFileNameWithoutExtension($csproj) - Write-Information "Publishing $projName..." -Tags "Invoke-DotNetPublish" - - foreach ($arch in $architectures) { - $outDir = Join-Path $BuildConfiguration.OutputPath "$projName-$arch" - - # Create output directory - New-Item -Path $outDir -ItemType Directory -Force | Write-InformationStream -Tags "Invoke-DotNetPublish" - - # Publish application with optimized settings for both general use and winget compatibility - # Note: PublishSingleFile is disabled because Silk.NET native libraries (GLFW, SDL) don't bundle correctly - "dotnet publish `"$csproj`" --configuration $Configuration --runtime $arch --self-contained true --output `"$outDir`" -p:PublishSingleFile=false -p:PublishTrimmed=false -p:DebugType=none -p:DebugSymbols=false -logger:`"Microsoft.Build.Logging.ConsoleLogger,Microsoft.Build;Summary;ForceNoAlign;ShowTimestamp;ShowCommandLine;Verbosity=quiet`"" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Invoke-DotNetPublish" - - if ($LASTEXITCODE -eq 0) { - # Create general application zip archive for all platforms - $stageFile = Join-Path $BuildConfiguration.StagingPath "$projName-$version-$arch.zip" - Compress-Archive -Path "$outDir/*" -DestinationPath $stageFile -Force | Write-InformationStream -Tags "Invoke-DotNetPublish" - - $publishedCount++ - Write-Information "Successfully published $projName for $arch" -Tags "Invoke-DotNetPublish" - } else { - Write-Information "Failed to publish $projName for $arch" -Tags "Invoke-DotNetPublish" - continue - } - } - } - - # Generate SHA256 hashes for all published packages - $allPackages = @(Get-ChildItem -Path $BuildConfiguration.StagingPath -Filter "*.zip" -ErrorAction SilentlyContinue) - - if ($allPackages.Count -gt 0) { - Write-Information "Generating SHA256 hashes for all published packages..." -Tags "Invoke-DotNetPublish" - - foreach ($package in $allPackages) { - # Calculate and store SHA256 hash - $hash = Get-FileHash -Path $package.FullName -Algorithm SHA256 - Write-Information "SHA256 for $($package.Name): $($hash.Hash)" -Tags "Invoke-DotNetPublish" - - # Store hash for integrity verification and distribution use - "$($package.Name)=$($hash.Hash)" | Out-File -FilePath (Join-Path $BuildConfiguration.StagingPath "hashes.txt") -Append -Encoding UTF8 - } - } - - if ($publishedCount -gt 0) { - Write-Information "Published $publishedCount application packages across all platforms and architectures" -Tags "Invoke-DotNetPublish" - - # Report hash generation results - if ($allPackages.Count -gt 0) { - Write-Information "Generated SHA256 hashes for $($allPackages.Count) published packages" -Tags "Invoke-DotNetPublish" - } - } else { - Write-Information "No applications were published (projects may not be configured as executables)" -Tags "Invoke-DotNetPublish" - } -} - -#endregion - -#region Publishing and Release - -function Invoke-NuGetPublish { - <# - .SYNOPSIS - Publishes NuGet packages. - .DESCRIPTION - Publishes packages to GitHub Packages and NuGet.org. - Uses the build configuration to determine package paths and authentication details. - .PARAMETER BuildConfiguration - The build configuration object containing package patterns, GitHub token, and NuGet API key. - This object should be obtained from Get-BuildConfiguration. - .OUTPUTS - None. Publishes packages to the configured package repositories. - #> - [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] - [PSCustomObject]$BuildConfiguration - ) - - # Check if there are any packages to publish - $packages = @(Get-Item -Path $BuildConfiguration.PackagePattern -ErrorAction SilentlyContinue) - if ($packages.Count -eq 0) { - Write-Information "No packages found to publish" -Tags "Invoke-NuGetPublish" - return - } - - Write-Information "Found $($packages.Count) package(s) to publish" -Tags "Invoke-NuGetPublish" - - Write-StepHeader "Publishing to GitHub Packages" -Tags "Invoke-NuGetPublish" - - # Execute the command and stream output - "dotnet nuget push `"$($BuildConfiguration.PackagePattern)`" --api-key `"$($BuildConfiguration.GithubToken)`" --source `"https://nuget.pkg.github.com/$($BuildConfiguration.GithubOwner)/index.json`" --skip-duplicate" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Invoke-NuGetPublish" - Assert-LastExitCode "GitHub package publish failed" - - # Only publish to NuGet.org if API key is provided - if (-not [string]::IsNullOrWhiteSpace($BuildConfiguration.NuGetApiKey)) { - Write-StepHeader "Publishing to NuGet.org" -Tags "Invoke-NuGetPublish" - - # Execute the command and stream output - "dotnet nuget push `"$($BuildConfiguration.PackagePattern)`" --api-key `"$($BuildConfiguration.NuGetApiKey)`" --source `"https://api.nuget.org/v3/index.json`" --skip-duplicate" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Invoke-NuGetPublish" - Assert-LastExitCode "NuGet.org package publish failed" - } else { - Write-Information "Skipping NuGet.org publishing - no API key provided" -Tags "Invoke-NuGetPublish" - } - - # Only publish to Ktsu.dev if API key is provided - if (-not [string]::IsNullOrWhiteSpace($BuildConfiguration.KtsuPackageKey)) { - Write-StepHeader "Publishing to packages.ktsu.dev" -Tags "Invoke-NuGetPublish" - - # Execute the command and stream output - "dotnet nuget push `"$($BuildConfiguration.PackagePattern)`" --api-key `"$($BuildConfiguration.KtsuPackageKey)`" --source `"https://packages.ktsu.dev/v3/index.json`" --skip-duplicate" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Invoke-NuGetPublish" - Assert-LastExitCode "packages.ktsu.dev package publish failed" - } else { - Write-Information "Skipping packages.ktsu.dev publishing - no API key provided" -Tags "Invoke-NuGetPublish" - } -} - -function New-GitHubRelease { - <# - .SYNOPSIS - Creates a new GitHub release. - .DESCRIPTION - Creates a new GitHub release with the specified version, creates and pushes a git tag, - and uploads release assets. Uses the GitHub CLI (gh) for release creation. - .PARAMETER BuildConfiguration - The build configuration object containing version, commit hash, GitHub token, and asset patterns. - This object should be obtained from Get-BuildConfiguration. - .OUTPUTS - None. Creates a GitHub release and uploads specified assets. - #> - [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] - [PSCustomObject]$BuildConfiguration - ) - - # Set GitHub token for CLI - $env:GH_TOKEN = $BuildConfiguration.GithubToken - - # Configure git user - Set-GitIdentity | Write-InformationStream -Tags "New-GitHubRelease" - - # Create and push the tag first - Write-Information "Creating and pushing tag v$($BuildConfiguration.Version)..." -Tags "New-GitHubRelease" - "git tag -a `"v$($BuildConfiguration.Version)`" `"$($BuildConfiguration.ReleaseHash)`" -m `"Release v$($BuildConfiguration.Version)`"" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "New-GitHubRelease" - Assert-LastExitCode "Failed to create git tag" - - "git push origin `"v$($BuildConfiguration.Version)`"" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "New-GitHubRelease" - Assert-LastExitCode "Failed to push git tag" - - # Collect all assets - $assets = @() - foreach ($pattern in $BuildConfiguration.AssetPatterns) { - $matched = Get-Item -Path $pattern -ErrorAction SilentlyContinue - if ($matched) { - $assets += $matched.FullName - } - } - - # Create release - Write-StepHeader "Creating GitHub Release v$($BuildConfiguration.Version)" -Tags "New-GitHubRelease" - - $releaseArgs = @( - "release", - "create", - "v$($BuildConfiguration.Version)" - ) - - # Add target commit - $releaseArgs += "--target" - $releaseArgs += $BuildConfiguration.ReleaseHash.ToString() - - # Add notes generation - $releaseArgs += "--generate-notes" - - # First check for latest changelog file (preferred for releases) - $latestChangelogPath = "LATEST_CHANGELOG.md" - if (Test-Path $latestChangelogPath) { - Write-Information "Using latest version changelog from $latestChangelogPath" -Tags "New-GitHubRelease" - $releaseArgs += "--notes-file" - $releaseArgs += $latestChangelogPath - } - # Fall back to full changelog if specified in config and latest not found - elseif (Test-Path $BuildConfiguration.ChangelogFile) { - Write-Information "Using full changelog from $($BuildConfiguration.ChangelogFile)" -Tags "New-GitHubRelease" - $releaseArgs += "--notes-file" - $releaseArgs += $BuildConfiguration.ChangelogFile - } - - # Add assets as positional arguments - $releaseArgs += $assets - - # Join the arguments into a single string - $releaseArgs = $releaseArgs -join ' ' - - "gh $releaseArgs" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "New-GitHubRelease" - Assert-LastExitCode "Failed to create GitHub release" -} - -#endregion - -#region Utility Functions - -function Assert-LastExitCode { - <# - .SYNOPSIS - Verifies that the last command executed successfully. - .DESCRIPTION - Throws an exception if the last command execution resulted in a non-zero exit code. - This function is used internally to ensure each step completes successfully. - .PARAMETER Message - The error message to display if the exit code check fails. - .PARAMETER Command - Optional. The command that was executed, for better error reporting. - .EXAMPLE - dotnet build - Assert-LastExitCode "The build process failed" -Command "dotnet build" - .NOTES - Author: ktsu.dev - #> - [CmdletBinding()] - param ( - [string]$Message = "Command failed", - [string]$Command = "" - ) - - if ($LASTEXITCODE -ne 0) { - $errorDetails = "Exit code: $LASTEXITCODE" - if (-not [string]::IsNullOrWhiteSpace($Command)) { - $errorDetails += " | Command: $Command" - } - - $fullMessage = "$Message$script:lineEnding$errorDetails" - Write-Information $fullMessage -Tags "Assert-LastExitCode" - throw $fullMessage - } -} - -function Write-StepHeader { - <# - .SYNOPSIS - Writes a formatted step header to the console. - .DESCRIPTION - Creates a visually distinct header for build steps in the console output. - Used to improve readability of the build process logs. - .PARAMETER Message - The header message to display. - .EXAMPLE - Write-StepHeader "Restoring Packages" - .NOTES - Author: ktsu.dev - #> - [CmdletBinding()] - param ( - [Parameter(Mandatory=$true)] - [string]$Message, - [Parameter()] - [AllowEmptyCollection()] - [string[]]$Tags = @("Write-StepHeader") - ) - Write-Information "$($script:lineEnding)=== $Message ===$($script:lineEnding)" -Tags $Tags -} - -function Test-AnyFiles { - <# - .SYNOPSIS - Tests if any files match the specified pattern. - .DESCRIPTION - Tests if any files exist that match the given glob pattern. This is useful for - determining if certain file types (like packages) exist before attempting operations - on them. - .PARAMETER Pattern - The glob pattern to check for matching files. - .EXAMPLE - if (Test-AnyFiles -Pattern "*.nupkg") { - Write-Host "NuGet packages found!" - } - .NOTES - Author: ktsu.dev - #> - [CmdletBinding()] - [OutputType([bool])] - param ( - [Parameter(Mandatory=$true)] - [string]$Pattern - ) - - # Use array subexpression to ensure consistent collection handling - $matchingFiles = @(Get-Item -Path $Pattern -ErrorAction SilentlyContinue) - return $matchingFiles.Count -gt 0 -} - -function Write-InformationStream { - <# - .SYNOPSIS - Streams output to the console. - .DESCRIPTION - Streams output to the console. - .PARAMETER Object - The object to write to the console. - .EXAMPLE - & git status | Write-InformationStream - .NOTES - Author: ktsu.dev - #> - [CmdletBinding()] - param ( - [Parameter(ValueFromPipeline=$true, ParameterSetName="Object")] - [object]$Object, - [Parameter()] - [AllowEmptyCollection()] - [string[]]$Tags = @("Write-InformationStream") - ) - - process { - # Use array subexpression to ensure consistent collection handling - $Object | ForEach-Object { - Write-Information $_ -Tags $Tags - } - } -} - -function Invoke-ExpressionWithLogging { - <# - .SYNOPSIS - Invokes an expression and logs the result to the console. - .DESCRIPTION - Invokes an expression and logs the result to the console. - .PARAMETER ScriptBlock - The script block to execute. - .PARAMETER Command - A string command to execute, which will be converted to a script block. - .PARAMETER Tags - Optional tags to include in the logging output for filtering and organization. - .OUTPUTS - The result of the expression. - .NOTES - Author: ktsu.dev - This function is useful for debugging expressions that are not returning the expected results. - #> - [CmdletBinding()] - param ( - [Parameter(ValueFromPipeline=$true, ParameterSetName="ScriptBlock")] - [scriptblock]$ScriptBlock, - - [Parameter(ValueFromPipeline=$true, ParameterSetName="Command")] - [string]$Command, - - [Parameter()] - [AllowEmptyCollection()] - [string[]]$Tags = @("Invoke-ExpressionWithLogging") - ) - - process { - # Convert command string to scriptblock if needed - if ($PSCmdlet.ParameterSetName -eq "Command" -and -not [string]::IsNullOrWhiteSpace($Command)) { - Write-Information "Executing command: $Command" -Tags $Tags - $ScriptBlock = [scriptblock]::Create($Command) - } - else { - Write-Information "Executing script block: $ScriptBlock" -Tags $Tags - } - - if ($ScriptBlock) { - # Execute the expression and return its result - & $ScriptBlock | ForEach-Object { - Write-Output $_ - } - } - } -} - -function Get-GitLineEnding { - <# - .SYNOPSIS - Gets the correct line ending based on git config. - .DESCRIPTION - Determines whether to use LF or CRLF based on the git core.autocrlf and core.eol settings. - Falls back to system default line ending if no git settings are found. - .OUTPUTS - String. Returns either "`n" for LF or "`r`n" for CRLF line endings. - .NOTES - The function checks git settings in the following order: - 1. core.eol setting (if set to 'lf' or 'crlf') - 2. core.autocrlf setting ('true', 'input', or 'false') - 3. System default line ending - #> - [CmdletBinding()] - [OutputType([string])] - param() - - $autocrlf = "git config --get core.autocrlf" | Invoke-ExpressionWithLogging - $eol = "git config --get core.eol" | Invoke-ExpressionWithLogging - - # If core.eol is set, use that - if ($LASTEXITCODE -eq 0 -and $eol -in @('lf', 'crlf')) { - return if ($eol -eq 'lf') { "`n" } else { "`r`n" } - } - - # Otherwise use autocrlf setting - if ($LASTEXITCODE -eq 0) { - switch ($autocrlf.ToLower()) { - 'true' { return "`n" } # Git will convert to CRLF on checkout - 'input' { return "`n" } # Always use LF - 'false' { - # Use OS default - return [System.Environment]::NewLine - } - default { - # Default to OS line ending if setting is not recognized - return [System.Environment]::NewLine - } - } - } - - # If git config fails or no setting found, use OS default - return [System.Environment]::NewLine -} - -function Set-GitIdentity { - <# - .SYNOPSIS - Configures git user identity for automated operations. - .DESCRIPTION - Sets up git user name and email globally for GitHub Actions or other automated processes. - #> - [CmdletBinding()] - param() - - Write-Information "Configuring git user for GitHub Actions..." -Tags "Set-GitIdentity" - "git config --global user.name `"Github Actions`"" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Set-GitIdentity" - Assert-LastExitCode "Failed to configure git user name" - "git config --global user.email `"actions@users.noreply.github.com`"" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Set-GitIdentity" - Assert-LastExitCode "Failed to configure git user email" -} - -function ConvertTo-ArraySafe { - <# - .SYNOPSIS - Safely converts an object to an array, even if it's already an array, a single item, or null. - .DESCRIPTION - Ensures that the returned object is always an array, handling PowerShell's behavior - where single item arrays are automatically unwrapped. Also handles error objects and other edge cases. - .PARAMETER InputObject - The object to convert to an array. - .OUTPUTS - Returns an array, even if the input is null or a single item. - #> - [CmdletBinding()] - [OutputType([object[]])] - param ( - [Parameter(ValueFromPipeline=$true)] - [AllowNull()] - [object]$InputObject - ) - - # Handle null or empty input - if ($null -eq $InputObject -or [string]::IsNullOrEmpty($InputObject)) { - return ,[object[]]@() - } - - # Handle error objects - return empty array for safety - if ($InputObject -is [System.Management.Automation.ErrorRecord]) { - Write-Information "ConvertTo-ArraySafe: Received error object, returning empty array" -Tags "ConvertTo-ArraySafe" - return ,[object[]]@() - } - - # Handle empty strings - if ($InputObject -is [string] -and [string]::IsNullOrWhiteSpace($InputObject)) { - return ,[object[]]@() - } - - try { - # Always force array context using the comma operator and explicit array subexpression - if ($InputObject -is [array]) { - # Ensure we return a proper array even if it's a single-item array - return ,[object[]]@($InputObject) - } - elseif ($InputObject -is [string] -and $InputObject.Contains("`n")) { - # Handle multi-line strings by splitting them - $lines = $InputObject -split "`n" | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } - return ,[object[]]@($lines) - } - else { - # Single item, make it an array using explicit array operators - return ,[object[]]@($InputObject) - } - } - catch { - Write-Information "ConvertTo-ArraySafe: Error converting object to array: $_" -Tags "ConvertTo-ArraySafe" - return ,[object[]]@() - } -} - -#endregion - -#region High-Level Workflows - -function Invoke-BuildWorkflow { - <# - .SYNOPSIS - Executes the main build workflow. - .DESCRIPTION - Runs the complete build, test, and package process. - .PARAMETER Configuration - The build configuration (Debug/Release). - .PARAMETER BuildArgs - Additional build arguments. - .PARAMETER BuildConfiguration - The build configuration object from Get-BuildConfiguration. - #> - [CmdletBinding()] - [OutputType([PSCustomObject])] - param ( - [string]$Configuration = "Release", - [string]$BuildArgs = "", - [Parameter(Mandatory=$true)] - [PSCustomObject]$BuildConfiguration - ) - - try { - # Setup - Initialize-BuildEnvironment | Write-InformationStream -Tags "Invoke-BuildWorkflow" - - # Install dotnet-script if needed - if ($BuildConfiguration.UseDotnetScript) { - Write-StepHeader "Installing dotnet-script" -Tags "Invoke-DotnetScript" - "dotnet tool install -g dotnet-script" | Invoke-ExpressionWithLogging | Write-InformationStream -Tags "Invoke-DotnetScript" - Assert-LastExitCode "Failed to install dotnet-script" - } - - # Build and Test - Invoke-DotNetRestore | Write-InformationStream -Tags "Invoke-BuildWorkflow" - Invoke-DotNetBuild -Configuration $Configuration -BuildArgs $BuildArgs | Write-InformationStream -Tags "Invoke-BuildWorkflow" - Invoke-DotNetTest -Configuration $Configuration -CoverageOutputPath "coverage" | Write-InformationStream -Tags "Invoke-BuildWorkflow" - - return [PSCustomObject]@{ - Success = $true - Error = "" - Data = [PSCustomObject]@{ - Configuration = $Configuration - BuildArgs = $BuildArgs - } - } - } - catch { - Write-Information "Build workflow failed: $_" -Tags "Invoke-BuildWorkflow" - return [PSCustomObject]@{ - Success = $false - Error = $_.ToString() - Data = [PSCustomObject]@{} - StackTrace = $_.ScriptStackTrace - } - } -} - -function Invoke-ReleaseWorkflow { - <# - .SYNOPSIS - Executes the release workflow. - .DESCRIPTION - Generates metadata, packages, and creates a release. - .PARAMETER Configuration - The build configuration (Debug/Release). Defaults to "Release". - .PARAMETER BuildConfiguration - The build configuration object from Get-BuildConfiguration. - .OUTPUTS - PSCustomObject with Success, Error, and Data properties. - #> - [CmdletBinding()] - [OutputType([PSCustomObject])] - param ( - [string]$Configuration = "Release", - [Parameter(Mandatory=$true)] - [PSCustomObject]$BuildConfiguration - ) - - try { - Write-StepHeader "Starting Release Process" -Tags "Invoke-ReleaseWorkflow" - - # Package and publish if not skipped - $packagePaths = @() - - # Create NuGet packages - try { - Write-StepHeader "Packaging Libraries" -Tags "Invoke-DotNetPack" - Invoke-DotNetPack -Configuration $Configuration -OutputPath $BuildConfiguration.StagingPath -LatestChangelogFile $BuildConfiguration.LatestChangelogFile | Write-InformationStream -Tags "Invoke-DotNetPack" - - # Add package paths if they exist - if (Test-Path $BuildConfiguration.PackagePattern) { - $packagePaths += $BuildConfiguration.PackagePattern - } - if (Test-Path $BuildConfiguration.SymbolsPattern) { - $packagePaths += $BuildConfiguration.SymbolsPattern - } - } - catch { - Write-Information "Library packaging failed: $_" -Tags "Invoke-DotNetPack" - Write-Information "Continuing with release process without NuGet packages." -Tags "Invoke-DotNetPack" - } - - # Create application packages - try { - Invoke-DotNetPublish -Configuration $Configuration -BuildConfiguration $BuildConfiguration | Write-InformationStream -Tags "Invoke-DotNetPublish" - - # Add application paths if they exist - if (Test-Path $BuildConfiguration.ApplicationPattern) { - $packagePaths += $BuildConfiguration.ApplicationPattern - } - - # Note: hashes.txt is now stored in staging directory alongside packages - } - catch { - Write-Information "Application publishing failed: $_" -Tags "Invoke-DotNetPublish" - Write-Information "Continuing with release process without application packages." -Tags "Invoke-DotNetPublish" - } - - # Publish packages if we have any and NuGet key is provided AND this is a release build - $packages = @(Get-Item -Path $BuildConfiguration.PackagePattern -ErrorAction SilentlyContinue) - if ($packages.Count -gt 0 -and -not [string]::IsNullOrWhiteSpace($BuildConfiguration.NuGetApiKey) -and $BuildConfiguration.ShouldRelease) { - Write-StepHeader "Publishing NuGet Packages" -Tags "Invoke-NuGetPublish" - try { - Invoke-NuGetPublish -BuildConfiguration $BuildConfiguration | Write-InformationStream -Tags "Invoke-NuGetPublish" - } - catch { - Write-Information "NuGet package publishing failed: $_" -Tags "Invoke-NuGetPublish" - Write-Information "Continuing with release process." -Tags "Invoke-NuGetPublish" - } - } elseif ($packages.Count -gt 0 -and -not $BuildConfiguration.ShouldRelease) { - Write-Information "Packages found but skipping publication (not a release build: ShouldRelease=$($BuildConfiguration.ShouldRelease))" -Tags "Invoke-ReleaseWorkflow" - } - - # Create GitHub release only if this is a release build - if ($BuildConfiguration.ShouldRelease) { - Write-StepHeader "Creating GitHub Release" -Tags "New-GitHubRelease" - Write-Information "Creating release for version $($BuildConfiguration.Version)..." -Tags "New-GitHubRelease" - New-GitHubRelease -BuildConfiguration $BuildConfiguration | Write-InformationStream -Tags "New-GitHubRelease" - } else { - Write-Information "Skipping GitHub release creation (not a release build: ShouldRelease=$($BuildConfiguration.ShouldRelease))" -Tags "Invoke-ReleaseWorkflow" - } - - Write-StepHeader "Release Process Completed" -Tags "Invoke-ReleaseWorkflow" - Write-Information "Release process completed successfully!" -Tags "Invoke-ReleaseWorkflow" - return [PSCustomObject]@{ - Success = $true - Error = "" - Data = [PSCustomObject]@{ - Version = $BuildConfiguration.Version - ReleaseHash = $BuildConfiguration.ReleaseHash - PackagePaths = $packagePaths - } - } - } - catch { - Write-Information "Release workflow failed: $_" -Tags "Invoke-ReleaseWorkflow" - return [PSCustomObject]@{ - Success = $false - Error = $_.ToString() - Data = [PSCustomObject]@{ - ErrorDetails = $_.Exception.Message - PackagePaths = @() - } - StackTrace = $_.ScriptStackTrace - } - } -} - -function Invoke-CIPipeline { - <# - .SYNOPSIS - Executes the CI/CD pipeline. - .DESCRIPTION - Executes the CI/CD pipeline, including metadata updates and build workflow. - .PARAMETER BuildConfiguration - The build configuration to use. - #> - [CmdletBinding()] - [OutputType([PSCustomObject])] - param ( - [Parameter(Mandatory=$true)] - [PSCustomObject]$BuildConfiguration - ) - - Write-Information "BuildConfiguration: $($BuildConfiguration | ConvertTo-Json -Depth 10)" -Tags "Invoke-CIPipeline" - - try { - Write-Information "Updating metadata..." -Tags "Invoke-CIPipeline" - $metadata = Update-ProjectMetadata ` - -BuildConfiguration $BuildConfiguration - - if ($null -eq $metadata) { - Write-Information "Metadata update returned null" -Tags "Invoke-CIPipeline" - return [PSCustomObject]@{ - Success = $false - Error = "Metadata update returned null" - StackTrace = $_.ScriptStackTrace - } - } - - Write-Information "Metadata: $($metadata | ConvertTo-Json -Depth 10)" -Tags "Invoke-CIPipeline" - - $BuildConfiguration.Version = $metadata.Data.Version - $BuildConfiguration.ReleaseHash = $metadata.Data.ReleaseHash - - if (-not $metadata.Success) { - Write-Information "Failed to update metadata: $($metadata.Error)" -Tags "Invoke-CIPipeline" - return [PSCustomObject]@{ - Success = $false - Error = "Failed to update metadata: $($metadata.Error)" - StackTrace = $_.ScriptStackTrace - } - } - - # Get the version increment info to check if we should skip the release - Write-Information "Checking for significant changes..." -Tags "Invoke-CIPipeline" - $versionInfo = Get-VersionInfoFromGit -CommitHash $BuildConfiguration.ReleaseHash - - if ($versionInfo.Data.VersionIncrement -eq "skip") { - Write-Information "Skipping release: $($versionInfo.Data.IncrementReason)" -Tags "Invoke-CIPipeline" - return [PSCustomObject]@{ - Success = $true - Error = "" - Data = [PSCustomObject]@{ - Version = $metadata.Data.Version - ReleaseHash = $metadata.Data.ReleaseHash - SkippedRelease = $true - SkipReason = $versionInfo.Data.IncrementReason - } - } - } - - Write-Information "Running build workflow..." -Tags "Invoke-CIPipeline" - $result = Invoke-BuildWorkflow -BuildConfiguration $BuildConfiguration - if (-not $result.Success) { - Write-Information "Build workflow failed: $($result.Error)" -Tags "Invoke-CIPipeline" - return [PSCustomObject]@{ - Success = $false - Error = "Build workflow failed: $($result.Error)" - StackTrace = $_.ScriptStackTrace - } - } - - Write-Information "Running release workflow..." -Tags "Invoke-CIPipeline" - $result = Invoke-ReleaseWorkflow -BuildConfiguration $BuildConfiguration - if (-not $result.Success) { - Write-Information "Release workflow failed: $($result.Error)" -Tags "Invoke-CIPipeline" - return [PSCustomObject]@{ - Success = $false - Error = "Release workflow failed: $($result.Error)" - StackTrace = $_.ScriptStackTrace - } - } - - Write-Information "CI/CD pipeline completed successfully" -Tags "Invoke-CIPipeline" - return [PSCustomObject]@{ - Success = $true - Version = $metadata.Data.Version - ReleaseHash = $metadata.Data.ReleaseHash - } - } - catch { - Write-Information "CI/CD pipeline failed: $_" -Tags "Invoke-CIPipeline" - return [PSCustomObject]@{ - Success = $false - Error = "CI/CD pipeline failed: $_" - StackTrace = $_.ScriptStackTrace - } - } -} - -#endregion - -# Export public functions -# Core build and environment functions -Export-ModuleMember -Function Initialize-BuildEnvironment, - Get-BuildConfiguration - -# Version management functions -Export-ModuleMember -Function Get-GitTags, - Get-VersionType, - Get-VersionInfoFromGit, - New-Version - -# Version comparison and conversion functions -Export-ModuleMember -Function ConvertTo-FourComponentVersion, - Get-VersionNotes - -# Metadata and documentation functions -Export-ModuleMember -Function New-Changelog, - Update-ProjectMetadata, - New-License - -# .NET SDK operations -Export-ModuleMember -Function Invoke-DotNetRestore, - Invoke-DotNetBuild, - Invoke-DotNetTest, - Invoke-DotNetPack, - Invoke-DotNetPublish - -# Release and publishing functions -Export-ModuleMember -Function Invoke-NuGetPublish, - New-GitHubRelease - -# Utility functions -Export-ModuleMember -Function Assert-LastExitCode, - Write-StepHeader, - Test-AnyFiles, - Get-GitLineEnding, - Set-GitIdentity, - Write-InformationStream, - Invoke-ExpressionWithLogging, - ConvertTo-ArraySafe - -# High-level workflow functions -Export-ModuleMember -Function Invoke-BuildWorkflow, - Invoke-ReleaseWorkflow, - Invoke-CIPipeline - -#region Module Variables -$script:DOTNET_VERSION = '9.0' -$script:LICENSE_TEMPLATE = Join-Path $PSScriptRoot "LICENSE.template" - -# Set PowerShell preferences -$ErrorActionPreference = 'Stop' -$WarningPreference = 'Stop' -$InformationPreference = 'Continue' -$DebugPreference = 'Ignore' -$VerbosePreference = 'Ignore' -$ProgressPreference = 'Ignore' - -# Get the line ending for the current system -$script:lineEnding = Get-GitLineEnding -#endregion diff --git a/scripts/README.md b/scripts/README.md deleted file mode 100644 index 7f9fff0..0000000 --- a/scripts/README.md +++ /dev/null @@ -1,202 +0,0 @@ -# PSBuild Module - -A comprehensive PowerShell module for automating the build, test, package, and release process for .NET applications using Git-based versioning. - -## Features - -- Semantic versioning based on git history and commit messages -- Automatic version calculation from commit analysis -- Metadata file generation and management -- Comprehensive build, test, and package pipeline -- NuGet package creation and publishing -- GitHub release creation with assets -- Proper line ending handling based on git config - -## Installation - -1. Copy the `PSBuild.psm1` file to your project's `scripts` directory -2. Import the module in your PowerShell session: - ```powershell - Import-Module ./scripts/PSBuild.psm1 - ``` - -## Usage - -The main entry point is `Invoke-CIPipeline`, which handles the complete build, test, package, and release process: - -```powershell -# First, get the build configuration -$buildConfig = Get-BuildConfiguration ` - -ServerUrl "https://github.com" ` - -GitRef "refs/heads/main" ` - -GitSha "abc123" ` - -GitHubOwner "myorg" ` - -GitHubRepo "myrepo" ` - -GithubToken $env:GITHUB_TOKEN ` - -NuGetApiKey $env:NUGET_API_KEY ` # Optional - can be empty to skip NuGet.org publishing - -WorkspacePath "." ` - -ExpectedOwner "myorg" ` - -ChangelogFile "CHANGELOG.md" ` - -AssetPatterns @("staging/*.nupkg", "staging/*.zip") - -# Then run the pipeline -$result = Invoke-CIPipeline -BuildConfiguration $buildConfig - -if ($result.Success) { - Write-Host "Pipeline completed successfully!" - Write-Host "Version: $($result.Version)" - Write-Host "Release Hash: $($result.ReleaseHash)" -} -``` - -## Object Model - -The module consistently uses PSCustomObjects for return values and data storage, providing several benefits: - -- Easy property access with dot notation -- Better IntelliSense support in modern editors -- Consistent patterns throughout the codebase -- More efficient memory usage -- Clearer code structure - -Each function returns a standardized object with at least: -- `Success`: Boolean indicating operation success -- `Error`: Error message if operation failed -- `Data`: Object containing function-specific results - -## Managed Files - -The module manages several metadata files in your repository: - -| File | Description | -|------|-------------| -| VERSION.md | Contains the current semantic version | -| LICENSE.md | MIT license with project URL and copyright | -| COPYRIGHT.md | Copyright notice with year range and owner | -| AUTHORS.md | List of contributors from git history | -| CHANGELOG.md | Auto-generated changelog from git history | -| PROJECT_URL.url | Link to project repository | -| AUTHORS.url | Link to organization/owner | - -## Version Control - -### Version Tags - -Commits can include the following tags to control version increments: - -| Tag | Description | Example | -|-----|-------------|---------| -| [major] | Triggers a major version increment | 2.0.0 | -| [minor] | Triggers a minor version increment | 1.2.0 | -| [patch] | Triggers a patch version increment | 1.1.2 | -| [pre] | Creates/increments pre-release version | 1.1.2-pre.1 | - -### Automatic Version Calculation - -The module analyzes commit history to determine appropriate version increments, following semantic versioning principles: - -1. Checks for explicit version tags in commit messages ([major], [minor], [patch], [pre]) -2. Detects public API changes by analyzing code diffs - - Adding, modifying, or removing public classes, interfaces, enums, structs, or records - - Changes to public methods, properties, or constants - - Any public API surface change triggers a minor version bump -3. Non-API changing code commits trigger patch version increments -4. Minimal changes default to prerelease increments - -This approach ensures that: -- Breaking changes are always major version increments (manually tagged) -- Public API additions or modifications are minor version increments (automatically detected) -- Bug fixes and internal changes are patch version increments -- Trivial changes result in prerelease increments - -### Public API Detection - -The module automatically analyzes code changes to detect modifications to the public API surface: - -- Added, modified, or removed public/protected classes, interfaces, enums, structs, or records -- Added, modified, or removed public/protected methods -- Added, modified, or removed public/protected properties -- Added or removed public constants - -When any of these changes are detected, the module automatically triggers a minor version increment, following semantic versioning best practices where non-breaking API changes warrant a minor version bump. - -## Build Configuration - -The `Get-BuildConfiguration` function returns a configuration object with the following key properties: - -| Property | Description | -|----------|-------------| -| IsOfficial | Whether this is an official repository build | -| IsMain | Whether building from main branch | -| IsTagged | Whether the current commit is tagged | -| ShouldRelease | Whether a release should be created | -| UseDotnetScript | Whether .NET script files are present | -| OutputPath | Path for build outputs | -| StagingPath | Path for staging artifacts | -| PackagePattern | Pattern for NuGet packages | -| SymbolsPattern | Pattern for symbol packages | -| ApplicationPattern | Pattern for application archives | -| Version | Current version number | -| ReleaseHash | Hash of the release commit | - -## Advanced Usage - -The module provides several functions for advanced scenarios: - -### Build and Release Functions -- `Initialize-BuildEnvironment`: Sets up the build environment -- `Get-BuildConfiguration`: Creates the build configuration object -- `Invoke-BuildWorkflow`: Runs the build and test process -- `Invoke-ReleaseWorkflow`: Handles package creation and publishing - -### Version Management Functions -- `Get-GitTags`: Gets sorted list of version tags -- `Get-VersionType`: Determines version increment type -- `Get-VersionInfoFromGit`: Gets comprehensive version information -- `New-Version`: Creates a new version file - -### Package and Release Functions -- `Invoke-DotNetRestore`: Restores NuGet packages -- `Invoke-DotNetBuild`: Builds the solution -- `Invoke-DotNetTest`: Runs unit tests with coverage -- `Invoke-DotNetPack`: Creates NuGet packages -- `Invoke-DotNetPublish`: Publishes applications -- `Invoke-NuGetPublish`: Publishes packages to repositories -- `New-GitHubRelease`: Creates GitHub release with assets - -### Utility Functions -- `Assert-LastExitCode`: Verifies command execution success -- `Write-StepHeader`: Creates formatted step headers in logs -- `Test-AnyFiles`: Tests for existence of files matching a pattern -- `Get-GitLineEnding`: Determines correct line endings based on git config -- `Set-GitIdentity`: Configures git user identity for automated operations -- `Write-InformationStream`: Streams output to the information stream -- `Invoke-ExpressionWithLogging`: Executes commands with proper logging - -## Line Ending Handling - -The module respects git's line ending settings when generating files: - -1. Uses git's `core.eol` setting if defined -2. Falls back to `core.autocrlf` setting -3. Defaults to OS-specific line endings if no git settings are found - -## Git Status Handling - -The module carefully handles git status to prevent empty commits: - -1. Only attempts commits when there are actual changes -2. Properly captures and interprets the git status output -3. Reports clear status messages during metadata updates -4. Preserves the correct commit hash for both success and no-change scenarios - -## Contributing - -1. Fork the repository -2. Create a feature branch -3. Commit your changes with appropriate version tags -4. Create a pull request - -## License - -MIT License - See LICENSE.md for details diff --git a/scripts/update-winget-manifests.ps1 b/scripts/update-winget-manifests.ps1 deleted file mode 100644 index 8755661..0000000 --- a/scripts/update-winget-manifests.ps1 +++ /dev/null @@ -1,1037 +0,0 @@ -#Requires -Version 7.0 -<# -.SYNOPSIS - Updates winget manifest files with new version and SHA256 hashes from GitHub releases. - -.DESCRIPTION - This script automates the process of updating winget manifest files when a new version - is released. It fetches the SHA256 hashes from the GitHub releases and updates the - manifest files accordingly. Settings are automatically inferred from the repository. - -.PARAMETER Version - The version to update the manifests for (e.g., "1.0.3") - -.PARAMETER GitHubRepo - The GitHub repository in the format "owner/repo" (optional - will be detected from git remote) - -.PARAMETER PackageId - The package identifier (e.g., "company.Product") - optional - -.PARAMETER ArtifactNamePattern - Pattern for artifact filenames, with {version} and {arch} placeholders - optional - -.PARAMETER ExecutableName - Name of the executable in the zip file - optional - -.PARAMETER CommandAlias - Command alias for the executable - optional - -.PARAMETER ConfigFile - Path to a JSON configuration file with project-specific settings (optional) - -.EXAMPLE - .\update-winget-manifests.ps1 -Version "1.0.3" - -.EXAMPLE - .\update-winget-manifests.ps1 -Version "1.0.3" -GitHubRepo "myorg/myrepo" -PackageId "myorg.MyApp" -#> - -param( - [Parameter(Mandatory = $true)] - [string]$Version, - - [Parameter(Mandatory = $false)] - [string]$GitHubRepo, - - [Parameter(Mandatory = $false)] - [string]$PackageId, - - [Parameter(Mandatory = $false)] - [string]$ArtifactNamePattern, - - [Parameter(Mandatory = $false)] - [string]$ExecutableName, - - [Parameter(Mandatory = $false)] - [string]$CommandAlias, - - [Parameter(Mandatory = $false)] - [string]$ConfigFile -) - -$ErrorActionPreference = "Stop" - -# ----- Helper Functions ----- - -function Test-IsLibraryOnlyProject { - param ( - [string]$RootDir, - [hashtable]$ProjectInfo - ) - - $hasApplications = $false - $hasLibraries = $false - $isMainProjectLibrary = $false - - # Get the repository name to identify the main project - $repoName = (Get-Item -Path $RootDir).Name - - # Check for generated NuGet packages in bin directories (indicator, not definitive) - $nupkgFiles = Get-ChildItem -Path $RootDir -Filter "*.nupkg" -Recurse -File -ErrorAction SilentlyContinue | - Where-Object { $_.Directory.Name -eq "Release" -or $_.Directory.Name -eq "Debug" } - if ($nupkgFiles.Count -gt 0) { - Write-Host "Detected NuGet package files" -ForegroundColor Yellow - $hasLibraries = $true - } - - # Check for C# projects - if ($ProjectInfo.type -eq "csharp") { - $csprojFiles = Get-ChildItem -Path $RootDir -Filter "*.csproj" -Recurse -File -Depth 3 - - foreach ($csprojFile in $csprojFiles) { - $csprojContent = Get-Content -Path $csprojFile.FullName -Raw - $projectName = $csprojFile.BaseName - - # Check if this is the main project (matches repository name or starts with it) - # For multi-project solutions like "Semantics.Strings" in repo "Semantics" - # Also handle naming variations like "ImGui.App" in repo "ImGuiApp" - $normalizedRepoName = $repoName -replace '[\.\-_]', '' - $normalizedProjectName = $projectName -replace '[\.\-_]', '' - $isMainProject = ($projectName -eq $repoName -or - $projectName.StartsWith("$repoName.") -or - $normalizedProjectName -eq $normalizedRepoName -or - $normalizedProjectName.StartsWith($normalizedRepoName)) - - # Skip test projects - $isTestProject = ($csprojContent -match 'Sdk="[^"]*\.Test["/]' -or - $csprojContent -match 'Sdk="[^"]*Sdk\.Test["/]' -or - $csprojContent -match 'Sdk="[^"]*Test[^"]*"' -or - $projectName -match "Test" -or - $projectName -match "\.Tests$") - - # Skip demo/example projects - $isDemoProject = ($projectName -match "Demo|Example|Sample" -or - $projectName.Contains("Demo") -or - $projectName.Contains("Example") -or - $projectName.Contains("Sample")) - - if ($isTestProject -or $isDemoProject) { - continue - } - - # Explicitly check if it's an executable - $isExecutable = ($csprojContent -match "\s*Exe\s*" -or - $csprojContent -match "\s*WinExe\s*" -or - $csprojContent -match 'Sdk="[^"]*\.App["/]' -or - $csprojContent -match 'Sdk="[^"]*Sdk\.App["/]' -or - $csprojContent -match '' -or - $csprojContent -match '') - - # Check if it's a library (explicit markers or implicit) - $isLibrary = ($csprojContent -match "\s*Library\s*" -or - $csprojContent -match "" -or - $csprojContent -match "\s*true\s*" -or - $csprojContent -match "\s*true\s*" -or - $csprojContent -match 'Sdk="[^"]*\.Lib["/]' -or - $csprojContent -match 'Sdk="[^"]*Sdk\.Lib["/]' -or - $csprojContent -match 'Sdk="[^"]*Library[^"]*"' -or - $csprojContent -match '' -or - $csprojContent -match '' -or - $csprojContent -match '' -or - $csprojContent -match "" -or # Multiple target frameworks often = library - (-not $isExecutable)) # No explicit exe = library by default - - if ($isLibrary) { - $hasLibraries = $true - if ($isMainProject) { - $isMainProjectLibrary = $true - } - } - - if ($isExecutable) { - $hasApplications = $true - } - } - } - - # Check for Node.js library patterns - if ($ProjectInfo.type -eq "node") { - $packageJsonPath = Join-Path -Path $RootDir -ChildPath "package.json" - if (Test-Path $packageJsonPath) { - $packageJson = Get-Content -Path $packageJsonPath -Raw | ConvertFrom-Json - # Check if it's a library (no bin field, or private: true) - if (-not $packageJson.bin -or $packageJson.private -eq $true) { - $hasLibraries = $true - } else { - $hasApplications = $true - } - } - } - - # Check for standalone NuGet package indicators (separate from project files) - $nuspecFiles = Get-ChildItem -Path $RootDir -Filter "*.nuspec" -Recurse -File -Depth 2 - if ($nuspecFiles.Count -gt 0) { - $hasLibraries = $true - } - - # Return true if the main project is a library and we have no main applications (demos don't count) - return $isMainProjectLibrary -and -not $hasApplications -} - -function Exit-GracefullyForLibrary { - param ( - [string]$Message = "Detected library project - no executable artifacts expected." - ) - - Write-Host $Message -ForegroundColor Yellow - Write-Host "Skipping winget manifest generation as this appears to be a library/NuGet package." -ForegroundColor Yellow - Write-Host "Winget manifests are intended for executable applications, not libraries." -ForegroundColor Cyan - exit 0 -} - -function Get-MSBuildProperty { - param ( - [string]$ProjectPath, - [string]$PropertyName - ) - - try { - $dotnetPath = Get-Command dotnet -ErrorAction SilentlyContinue - if (-not $dotnetPath) { - return $null - } - - # Use dotnet msbuild with /getProperty to get the evaluated property value - $result = & dotnet msbuild "$ProjectPath" /nologo /t:Build /p:DesignTimeBuild=true /getProperty:$PropertyName 2>$null - if ($LASTEXITCODE -eq 0 -and $result) { - $value = $result.Trim() - if ($value -and $value -ne "") { - return $value - } - } - } - catch { - # Silently fail, caller will handle null - } - - return $null -} - -function Get-MSBuildProperties { - param ( - [string]$ProjectPath - ) - - try { - $dotnetPath = Get-Command dotnet -ErrorAction SilentlyContinue - if (-not $dotnetPath) { - Write-Host "dotnet CLI not found. Falling back to XML parsing." -ForegroundColor Yellow - return $null - } - - # Restore packages first so SDK-provided properties are available - Write-Host "Restoring packages to resolve SDK properties..." -ForegroundColor Yellow - & dotnet restore "$ProjectPath" --verbosity quiet 2>$null - - $properties = @{} - $propertyNames = @("AssemblyName", "RootNamespace", "PackageId", "Product", "Authors", "Version", "Description", "RepositoryUrl", "Copyright", "PackageTags") - - foreach ($propName in $propertyNames) { - $value = Get-MSBuildProperty -ProjectPath $ProjectPath -PropertyName $propName - if ($value) { - $properties[$propName] = $value - } - } - - if ($properties.Count -gt 0) { - return $properties - } - - Write-Host "MSBuild property evaluation returned no results. Falling back to XML parsing." -ForegroundColor Yellow - return $null - } - catch { - Write-Host "Error evaluating MSBuild properties: $_" -ForegroundColor Yellow - Write-Host "Falling back to XML parsing." -ForegroundColor Yellow - return $null - } -} - -function Get-GitRemoteInfo { - param ( - [string]$RootDir - ) - - try { - # Get the GitHub URL from git remote - $remoteUrl = git remote get-url origin 2>$null - if ($remoteUrl) { - # Extract owner and repo from different Git URL formats (HTTPS or SSH) - if ($remoteUrl -match "github\.com[:/]([^/]+)/([^/.]+)(\.git)?$") { - $owner = $Matches[1] - $repo = $Matches[2] - return "$owner/$repo" - } - } - } - catch { - # Ignore errors if git is not available - Write-Host "Git command failed, cannot auto-detect repository info: $_" -ForegroundColor Yellow - } - - # Try to extract from PROJECT_URL.url file if available - if ($RootDir) { - $projectUrlFile = Join-Path -Path $RootDir -ChildPath "PROJECT_URL.url" - if (Test-Path $projectUrlFile) { - $content = Get-Content -Path $projectUrlFile -Raw - if ($content -match "URL=https://github.com/([^/]+)/([^/\r\n]+)") { - return "$($Matches[1])/$($Matches[2])" - } - } - } - - return $null -} - -function Get-FileContent { - param ( - [string]$FilePath - ) - - if (Test-Path $FilePath) { - return Get-Content -Path $FilePath -Raw - } - - return $null -} - -function Get-FirstLine { - param ( - [string]$Text - ) - - if ($Text) { - $lines = $Text -split "`n" - return $lines[0].Trim() - } - - return $null -} - -function Get-ShortDescription { - param ( - [string]$Text - ) - - if (-not $Text) { return $null } - - # Try to find quoted text that might be a short description - if ($Text -match ">\s*(.+?)(?=\r?\n|$)") { - return $Matches[1].Trim() - } - - # Or try the first non-empty line after title - $lines = $Text -split "`n" - foreach ($line in $lines | Select-Object -Skip 1) { - $trimmed = $line.Trim() - if ($trimmed -and -not $trimmed.StartsWith('#')) { - return $trimmed - } - } - - return $null -} - -function ConvertFrom-TagsList { - param ( - [string]$TagsText - ) - - if (-not $TagsText) { return @() } - - $tags = @() - $tagsList = $TagsText -split ";" | ForEach-Object { $_.Trim() } - - foreach ($tag in $tagsList) { - if ($tag) { - # Replace spaces and hyphens with underscores, and take only the first word - $cleanTag = $tag -replace "[\s\-]+", "-" - # Limit to first 3 words - $cleanTag = ($cleanTag -split "-" | Select-Object -First 3) -join "-" - $tags += $cleanTag - } - } - - # Return top tags (most relevant for winget) - return $tags | Select-Object -First 10 -} - -function Find-ProjectInfo { - param ( - [string]$RootDir - ) - - $projectInfo = @{ - name = "" - type = "unknown" - executableName = "" - fileExtensions = @() - tags = @() - version = "" - shortDescription = "" - description = "" - publisher = "" - rootNamespace = "" - } - - # Try to get version from VERSION.md - $versionFile = Join-Path -Path $RootDir -ChildPath "VERSION.md" - if (Test-Path $versionFile) { - $versionContent = Get-Content -Path $versionFile -Raw - if ($versionContent) { - $projectInfo.version = $versionContent.Trim() - } - } - - # Try to get publisher info from AUTHORS.md - $authorsFile = Join-Path -Path $RootDir -ChildPath "AUTHORS.md" - if (Test-Path $authorsFile) { - $authorsContent = Get-Content -Path $authorsFile -Raw - if ($authorsContent -and $authorsContent.Trim()) { - $projectInfo.publisher = $authorsContent.Trim().Split("`n")[0].Trim() - } - } - - # Try to get short description from README.md - $readmeFile = Join-Path -Path $RootDir -ChildPath "README.md" - if (Test-Path $readmeFile) { - $readmeContent = Get-Content -Path $readmeFile -Raw - - # Extract name from README title - if ($readmeContent -match "^#\s+(.+?)(?=\r?\n|$)") { - $projectInfo.name = $Matches[1].Trim() - } - - # Extract short description - $shortDesc = Get-ShortDescription -Text $readmeContent - if ($shortDesc) { - $projectInfo.shortDescription = $shortDesc - } - } - - # Try to get detailed description from DESCRIPTION.md - $descriptionFile = Join-Path -Path $RootDir -ChildPath "DESCRIPTION.md" - if (Test-Path $descriptionFile) { - $descContent = Get-Content -Path $descriptionFile -Raw - if ($descContent -and $descContent.Trim()) { - $projectInfo.description = $descContent.Trim() - } - elseif ($projectInfo.shortDescription) { - # Use short description as fallback - $projectInfo.description = $projectInfo.shortDescription - } - } - elseif ($projectInfo.shortDescription) { - # Use short description as fallback - $projectInfo.description = $projectInfo.shortDescription - } - - # Try to get tags from TAGS.md - $tagsFile = Join-Path -Path $RootDir -ChildPath "TAGS.md" - if (Test-Path $tagsFile) { - $tagsContent = Get-Content -Path $tagsFile -Raw - if ($tagsContent -and $tagsContent.Trim()) { - $projectInfo.tags = ConvertFrom-TagsList -TagsText $tagsContent.Trim() - } - } - - # Check for .csproj files (C# projects) - $csprojFiles = Get-ChildItem -Path $RootDir -Filter "*.csproj" -Recurse -File -Depth 3 - if ($csprojFiles.Count -gt 0) { - $projectInfo.type = "csharp" - $csproj = $csprojFiles[0] - - # Try to use MSBuild to extract project properties - $msBuildProps = Get-MSBuildProperties -ProjectPath $csproj.FullName - - if ($msBuildProps -and $msBuildProps.Count -gt 0) { - Write-Host "Successfully extracted MSBuild properties from project" -ForegroundColor Green - - # Extract project properties - if (-not $projectInfo.name -and $msBuildProps.Product) { - $projectInfo.name = $msBuildProps.Product - } elseif (-not $projectInfo.name -and $msBuildProps.AssemblyName) { - $projectInfo.name = $msBuildProps.AssemblyName - } - - if (-not $projectInfo.version -and $msBuildProps.Version) { - $projectInfo.version = $msBuildProps.Version - } - - if (-not $projectInfo.description -and $msBuildProps.Description) { - $projectInfo.description = $msBuildProps.Description - if (-not $projectInfo.shortDescription) { - $projectInfo.shortDescription = $msBuildProps.Description.Split('.')[0] + '.' - } - } - - if (-not $projectInfo.publisher -and $msBuildProps.Authors) { - $projectInfo.publisher = $msBuildProps.Authors.Split(',')[0].Trim() - } - - if ($msBuildProps.PackageTags) { - $packageTags = $msBuildProps.PackageTags.Split(';').Trim() | Where-Object { $_ } - if ($packageTags -and $packageTags.Count -gt 0) { - foreach ($tag in $packageTags) { - if ($tag -and -not $projectInfo.tags.Contains($tag)) { - $projectInfo.tags += $tag - } - } - } - } - - if ($msBuildProps.RootNamespace) { - $projectInfo.rootNamespace = $msBuildProps.RootNamespace - } - } else { - # Fallback to parsing the csproj XML - Write-Host "Falling back to parsing csproj XML" -ForegroundColor Yellow - - # Extract project name from csproj if not already set - if (-not $projectInfo.name) { - $csprojContent = Get-Content -Path $csproj.FullName -Raw - if ($csprojContent -match "(.*?)") { - $projectInfo.name = $Matches[1] - } - elseif ($csproj.BaseName) { - $projectInfo.name = $csproj.BaseName - } - - # Try to extract other properties - if ($csprojContent -match "(.*?)") { - $projectInfo.version = $Matches[1] - } - - if ($csprojContent -match "(.*?)") { - $projectInfo.description = $Matches[1] - if (-not $projectInfo.shortDescription) { - $projectInfo.shortDescription = $Matches[1].Split('.')[0] + '.' - } - } - - if ($csprojContent -match "(.*?)") { - $projectInfo.publisher = $Matches[1].Split(',')[0].Trim() - } - - if ($csprojContent -match "(.*?)") { - $projectInfo.rootNamespace = $Matches[1] - } - } - } - - # Attempt to find supported file extensions from project - $projectFileExtensions = @() - - # Try to find file extensions from .csproj - # Look for ItemGroups that might contain extensions the app handles - $csprojContent = Get-Content -Path $csproj.FullName -Raw - if ($csprojContent -match "(.*?)") { - $extensions = $Matches[1] -split "[,;]" | ForEach-Object { $_.Trim() } - foreach ($ext in $extensions) { - # Remove any dots and ensure lowercase - $ext = $ext.TrimStart(".").ToLower() - if ($ext -and -not [string]::IsNullOrWhiteSpace($ext)) { - $projectFileExtensions += $ext - } - } - } - - # Look for patterns - $extensionMatches = [regex]::Matches($csprojContent, '(.*?)") { - $projectInfo.executableName = $Matches[1] - } else { - $projectInfo.executableName = "$($projectInfo.name).exe" - } - - # Look for command alias - if ($csprojContent -match "(.*?)") { - $projectInfo.commandAlias = $Matches[1] - } - } - - # Check for package.json (Node.js projects) - $packageJsonPath = Join-Path -Path $RootDir -ChildPath "package.json" - if (Test-Path $packageJsonPath) { - $projectInfo.type = "node" - $packageJson = Get-Content -Path $packageJsonPath -Raw | ConvertFrom-Json - - # Extract name from package.json if not already set - if (-not $projectInfo.name -and $packageJson.name) { - $projectInfo.name = $packageJson.name - } - - # Extract description if not already set - if (-not $projectInfo.shortDescription -and $packageJson.description) { - $projectInfo.shortDescription = $packageJson.description - } - - # Add common file extensions for Node.js projects if not set - if ($projectInfo.fileExtensions.Count -eq 0) { - $projectInfo.fileExtensions = @("js", "json", "ts", "html", "css") - } - - # Add Node.js tags if not set - if ($projectInfo.tags.Count -eq 0) { - $projectInfo.tags = @("nodejs", "javascript") - } - else { - # Add Node.js tags to existing tags - $projectInfo.tags += @("nodejs", "javascript") - $projectInfo.tags = $projectInfo.tags | Select-Object -Unique - } - - $projectInfo.executableName = "$($projectInfo.name).js" - } - - # Check for Cargo.toml (Rust projects) - $cargoTomlPath = Join-Path -Path $RootDir -ChildPath "Cargo.toml" - if (Test-Path $cargoTomlPath) { - $projectInfo.type = "rust" - $cargoContent = Get-Content -Path $cargoTomlPath -Raw - - # Extract name from Cargo.toml if not already set - if (-not $projectInfo.name -and $cargoContent -match "\[package\][\s\S]*?name\s*=\s*""([^""]+)""") { - $projectInfo.name = $Matches[1] - } - - # Add common file extensions for Rust projects if not set - if ($projectInfo.fileExtensions.Count -eq 0) { - $projectInfo.fileExtensions = @("rs", "toml") - } - - # Add Rust tags if not set - if ($projectInfo.tags.Count -eq 0) { - $projectInfo.tags = @("rust") - } - else { - # Add Rust tags to existing tags - $projectInfo.tags += @("rust") - $projectInfo.tags = $projectInfo.tags | Select-Object -Unique - } - - $projectInfo.executableName = $projectInfo.name - } - - # Find executables in common build directories - if (-not $projectInfo.executableName -or -not (Test-Path -Path "$RootDir/bin/$($projectInfo.executableName)")) { - # Look for executables in common build directories - $buildDirs = @("bin", "publish", "target/release", "target/debug", "dist", "build", "out") - - foreach ($dir in $buildDirs) { - $exeFiles = Get-ChildItem -Path "$RootDir/$dir" -Filter "*.exe" -File -Recurse -ErrorAction SilentlyContinue - if ($exeFiles.Count -gt 0) { - $projectInfo.executableName = $exeFiles[0].Name - break - } - } - } - - # If we still don't have a project name, use the directory name - if (-not $projectInfo.name) { - $projectInfo.name = (Get-Item -Path $RootDir).Name - } - - return $projectInfo -} - -function Get-FileDescription { - param ( - [string]$ProjectType, - [string]$FallbackDescription - ) - - # Return fallback if provided - if ($FallbackDescription) { - return $FallbackDescription - } - - # Otherwise return a generic description based on project type - switch ($ProjectType) { - "csharp" { return ".NET application" } - "node" { return "Node.js application" } - "rust" { return "Rust application" } - default { return "Software application" } - } -} - -# ----- Main Script ----- - -# Set root directory to the parent of the scripts folder -$rootDir = Join-Path $PSScriptRoot ".." -$manifestDir = Join-Path $rootDir "winget" - -# Create the directory if it doesn't exist -if (-not (Test-Path $manifestDir)) { - New-Item -Path $manifestDir -ItemType Directory | Out-Null -} - -# Load configuration from file if it exists and specified -$config = @{} -if ($ConfigFile -and (Test-Path $ConfigFile)) { - Write-Host "Loading configuration from $ConfigFile..." -ForegroundColor Yellow - $config = Get-Content -Path $ConfigFile -Raw | ConvertFrom-Json -AsHashtable -} - -# Detect repository info if not provided -if (-not $GitHubRepo) { - $detectedRepo = Get-GitRemoteInfo -RootDir $rootDir - if ($detectedRepo) { - $GitHubRepo = $detectedRepo - Write-Host "Detected GitHub repository: $GitHubRepo" -ForegroundColor Green - } - elseif ($config.githubRepo) { - $GitHubRepo = $config.githubRepo - } - else { - Write-Error "Could not detect GitHub repository. Please specify it using -GitHubRepo parameter." - exit 1 - } -} - -# Extract owner and repo name from GitHub repo string -$ownerRepo = $GitHubRepo.Split('/') -$owner = $ownerRepo[0] -$repo = $ownerRepo[1] - -# Detect project info from repository -$projectInfo = Find-ProjectInfo -RootDir $rootDir -Write-Host "Detected project: $($projectInfo.name) (Type: $($projectInfo.type))" -ForegroundColor Green - -# Early check for library-only projects to avoid unnecessary processing -if (Test-IsLibraryOnlyProject -RootDir $rootDir -ProjectInfo $projectInfo) { - Exit-GracefullyForLibrary -Message "Detected library-only solution with no applications." -} - -# Check for explicit version provided -if ($projectInfo.version -and -not $Version) { - $Version = $projectInfo.version - Write-Host "Using detected version: $Version" -ForegroundColor Green -} - -# Build configuration object with detected and provided values -$config = @{ - packageId = if ($PackageId) { $PackageId } elseif ($config.packageId) { $config.packageId } elseif ($projectInfo.rootNamespace) { $projectInfo.rootNamespace } elseif ($projectInfo.name) { $projectInfo.name } else { "$owner.$repo" } - githubRepo = $GitHubRepo - artifactNamePattern = if ($ArtifactNamePattern) { $ArtifactNamePattern } elseif ($config.artifactNamePattern) { $config.artifactNamePattern } else { "$repo-{version}-{arch}.zip" } - executableName = if ($ExecutableName) { $ExecutableName } elseif ($config.executableName) { $config.executableName } elseif ($projectInfo.executableName) { $projectInfo.executableName } else { "$repo.exe" } - commandAlias = if ($CommandAlias) { $CommandAlias } elseif ($config.commandAlias) { $config.commandAlias } else { $repo.ToLower() } - packageName = if ($config.packageName) { $config.packageName } else { $projectInfo.name -replace "^$owner\.", "" } - publisher = if ($config.publisher) { $config.publisher } elseif ($projectInfo.publisher) { $projectInfo.publisher } else { $owner } - shortDescription = if ($config.shortDescription) { $config.shortDescription } elseif ($projectInfo.shortDescription) { $projectInfo.shortDescription } else { "A $($projectInfo.type) application" } - description = if ($config.description) { $config.description } elseif ($projectInfo.description) { $projectInfo.description } else { Get-FileDescription -ProjectType $projectInfo.type } - fileExtensions = if ($config.fileExtensions -and $config.fileExtensions.Count -gt 0) { $config.fileExtensions } elseif ($projectInfo.fileExtensions.Count -gt 0) { $projectInfo.fileExtensions } else { @("txt", "md", "json") } - tags = if ($config.tags -and $config.tags.Count -gt 0) { $config.tags } elseif ($projectInfo.tags.Count -gt 0) { $projectInfo.tags } else { @("utility", "application") } -} - -Write-Host "Configuration:" -ForegroundColor Yellow -Write-Host " Package ID: $($config.packageId)" -ForegroundColor Cyan -Write-Host " GitHub Repo: $($config.githubRepo)" -ForegroundColor Cyan -Write-Host " Package Name: $($config.packageName)" -ForegroundColor Cyan -Write-Host " Publisher: $($config.publisher)" -ForegroundColor Cyan -Write-Host " Artifact Pattern: $($config.artifactNamePattern)" -ForegroundColor Cyan -Write-Host " Executable: $($config.executableName)" -ForegroundColor Cyan -Write-Host " Command Alias: $($config.commandAlias)" -ForegroundColor Cyan -Write-Host " Description: $($config.shortDescription)" -ForegroundColor Cyan -Write-Host " Tags: $($config.tags -join ', ')" -ForegroundColor Cyan - -# GitHub API configuration -$releaseUrl = "https://api.github.com/repos/$($config.githubRepo)/releases/tags/v$Version" -$downloadBaseUrl = "https://github.com/$($config.githubRepo)/releases/download/v$Version" - -# Build headers for GitHub API requests (with optional authentication) -$githubHeaders = @{ - "User-Agent" = "Winget-Manifest-Updater" - "Accept" = "application/vnd.github.v3+json" -} - -# Check for GITHUB_TOKEN environment variable for authenticated requests (higher rate limit) -$githubToken = $env:GITHUB_TOKEN -if (-not $githubToken) { - $githubToken = $env:GH_TOKEN -} -if ($githubToken) { - $githubHeaders["Authorization"] = "Bearer $githubToken" - Write-Host "Using authenticated GitHub API requests" -ForegroundColor Green -} else { - Write-Host "Warning: No GITHUB_TOKEN found. API requests may be rate-limited." -ForegroundColor Yellow - Write-Host "Set GITHUB_TOKEN environment variable for authenticated requests." -ForegroundColor Yellow -} - -Write-Host "Updating winget manifests for $($config.packageName) version $Version..." -ForegroundColor Green - -# Fetch release information from GitHub -try { - Write-Host "Fetching release information from GitHub..." -ForegroundColor Yellow - $release = Invoke-RestMethod -Uri $releaseUrl -Headers $githubHeaders - - Write-Host "Found release: $($release.name)" -ForegroundColor Green - $releaseDate = [DateTime]::Parse($release.published_at).ToString("yyyy-MM-dd") -} catch { - # Check if this might be a library-only project before failing - if (Test-IsLibraryOnlyProject -RootDir $rootDir -ProjectInfo $projectInfo) { - Exit-GracefullyForLibrary -Message "Failed to fetch release information, but detected library-only solution." - } - - Write-Error "Failed to fetch release information: $_" - exit 1 -} - -# Download and calculate SHA256 hashes for each architecture -$architectures = @("win-x64", "win-x86", "win-arm64") -$sha256Hashes = @{} - -# First, try to read hashes from local file if available (from recent build) -$localHashesFile = Join-Path $rootDir "staging" "hashes.txt" -$localHashes = @{} - -if (Test-Path $localHashesFile) { - Write-Host "Reading hashes from local build output..." -ForegroundColor Yellow - Get-Content $localHashesFile | ForEach-Object { - if ($_ -match '^(.+)=(.+)$') { - $localHashes[$Matches[1]] = $Matches[2] - } - } -} - -foreach ($arch in $architectures) { - # Replace placeholders in artifact name pattern - $fileName = $config.artifactNamePattern -replace '{name}', $repo -replace '{version}', $Version -replace '{arch}', $arch - - # Try to use local hash first - if ($localHashes.ContainsKey($fileName)) { - $sha256Hashes[$arch] = $localHashes[$fileName].ToUpper() - Write-Host " $arch`: $($sha256Hashes[$arch]) (from local build)" -ForegroundColor Cyan - continue - } - - # Fall back to downloading and calculating hash - $downloadUrl = "$downloadBaseUrl/$fileName" - $tempFile = Join-Path $env:TEMP $fileName - - try { - Write-Host "Downloading $fileName to calculate SHA256..." -ForegroundColor Yellow - Invoke-WebRequest -Uri $downloadUrl -OutFile $tempFile -UseBasicParsing - - $hash = Get-FileHash -Path $tempFile -Algorithm SHA256 - $sha256Hashes[$arch] = $hash.Hash.ToUpper() - - Write-Host " $arch`: $($hash.Hash)" -ForegroundColor Cyan - - # Clean up temp file - Remove-Item $tempFile -Force - } catch { - Write-Host "Warning: Failed to download or hash $fileName`: $_" -ForegroundColor Yellow - Write-Host "Skipping this architecture. If required, please provide the correct artifact name pattern." -ForegroundColor Yellow - } -} - -# Check if we have at least one hash -if ($sha256Hashes.Count -eq 0) { - # Check if this appears to be a library-only project (no executable artifacts) - if (Test-IsLibraryOnlyProject -RootDir $rootDir -ProjectInfo $projectInfo) { - Exit-GracefullyForLibrary - } else { - Write-Error "Could not obtain any SHA256 hashes. Please check that the artifact name pattern matches your release files." - exit 1 - } -} - -# Update version manifest -$versionManifestPath = Join-Path $manifestDir "$($config.packageId).yaml" -Write-Host "Updating version manifest: $versionManifestPath" -ForegroundColor Yellow - -$versionContent = @" -# yaml-language-server: `$schema=https://aka.ms/winget-manifest.version.1.10.0.schema.json -PackageIdentifier: $($config.packageId) -PackageVersion: $Version -DefaultLocale: en-US -ManifestType: version -ManifestVersion: 1.10.0 -"@ - -Set-Content -Path $versionManifestPath -Value $versionContent -Encoding UTF8 - -# Update locale manifest -$localeManifestPath = Join-Path $manifestDir "$($config.packageId).locale.en-US.yaml" -Write-Host "Updating locale manifest: $localeManifestPath" -ForegroundColor Yellow - -# Generate tags string for YAML -$tagsYaml = "" -if ($config.tags -and $config.tags.Count -gt 0) { - $tagsYaml = "Tags:`n" - foreach ($tag in $config.tags) { - $tagsYaml += "- $tag`n" - } -} - -$localeContent = @" -# yaml-language-server: `$schema=https://aka.ms/winget-manifest.defaultLocale.1.10.0.schema.json -PackageIdentifier: $($config.packageId) -PackageVersion: $Version -PackageLocale: en-US -Publisher: $($config.publisher) -PublisherUrl: https://github.com/$owner -PublisherSupportUrl: https://github.com/$($config.githubRepo)/issues -# PrivacyUrl: -Author: $($config.publisher) -PackageName: $($config.packageName) -PackageUrl: https://github.com/$($config.githubRepo) -License: MIT -LicenseUrl: https://github.com/$($config.githubRepo)/blob/main/LICENSE.md -Copyright: Copyright (c) $($config.publisher) -# CopyrightUrl: -ShortDescription: $($config.shortDescription) -Description: $($config.description) -Moniker: $($config.commandAlias) -$tagsYaml -ReleaseNotes: |- - See full changelog at: https://github.com/$($config.githubRepo)/blob/main/CHANGELOG.md -ReleaseNotesUrl: https://github.com/$($config.githubRepo)/releases/tag/v$Version -# PurchaseUrl: -# InstallationNotes: -Documentations: -- DocumentLabel: README - DocumentUrl: https://github.com/$($config.githubRepo)/blob/main/README.md -ManifestType: defaultLocale -ManifestVersion: 1.10.0 -"@ - -Set-Content -Path $localeManifestPath -Value $localeContent -Encoding UTF8 - -# Update installer manifest -$installerManifestPath = Join-Path $manifestDir "$($config.packageId).installer.yaml" -Write-Host "Updating installer manifest: $installerManifestPath" -ForegroundColor Yellow - -# Generate file extensions string for YAML -$fileExtensionsYaml = "" -if ($config.fileExtensions -and $config.fileExtensions.Count -gt 0) { - $fileExtensionsYaml = "FileExtensions:`n" - foreach ($ext in $config.fileExtensions) { - $fileExtensionsYaml += "- $ext`n" - } -} - -# Generate commands string for YAML -$commandsYaml = "Commands:`n- $($config.commandAlias)" -if ($config.executableName -ne $config.commandAlias) { - $commandsYaml += "`n- $($config.executableName.Replace('.exe', ''))" -} - -$installerContent = @" -# yaml-language-server: `$schema=https://aka.ms/winget-manifest.installer.1.10.0.schema.json -PackageIdentifier: $($config.packageId) -PackageVersion: $Version -Platform: -- Windows.Desktop -MinimumOSVersion: 10.0.17763.0 -InstallerType: zip -InstallModes: -- interactive -- silent -UpgradeBehavior: install -$($commandsYaml.TrimEnd()) -$($fileExtensionsYaml.TrimEnd()) -ReleaseDate: $releaseDate -Dependencies: - PackageDependencies: - -"@ - -# Add .NET dependency based on project type -if ($projectInfo.type -eq "csharp") { - $installerContent += " - PackageIdentifier: Microsoft.DotNet.DesktopRuntime.10`n" -} - -$installerContent += "Installers:`n" - -foreach ($arch in $sha256Hashes.Keys) { - # Replace placeholders in artifact name pattern - $fileName = $config.artifactNamePattern -replace '{name}', $repo -replace '{version}', $Version -replace '{arch}', $arch - - # Add installer entry for this architecture - $installerContent += @" -- Architecture: $($arch.Replace('win-', '')) - InstallerUrl: $downloadBaseUrl/$fileName - InstallerSha256: $($sha256Hashes[$arch]) - NestedInstallerType: portable - NestedInstallerFiles: - - RelativeFilePath: $($config.executableName) - PortableCommandAlias: $($config.commandAlias) - -"@ -} - -$installerContent += @" -ManifestType: installer -ManifestVersion: 1.10.0 -"@ - -Set-Content -Path $installerManifestPath -Value $installerContent -Encoding UTF8 - -# Try to upload manifest files to GitHub release if gh CLI is available -try { - $ghCommand = Get-Command gh -ErrorAction SilentlyContinue - if ($ghCommand) { - Write-Host "Uploading manifest files to GitHub release..." -ForegroundColor Yellow - gh release upload v$Version $versionManifestPath $localeManifestPath $installerManifestPath --repo $($config.githubRepo) - Write-Host "Manifest files uploaded to release." -ForegroundColor Green - } -} catch { - # Check if upload failure might be due to missing release artifacts for library-only projects - if ($_.Exception.Message -match "not found|404" -and (Test-IsLibraryOnlyProject -RootDir $rootDir -ProjectInfo $projectInfo)) { - Exit-GracefullyForLibrary -Message "Release upload failed, likely due to library-only solution having no executable artifacts." - } - - Write-Host "GitHub CLI not available or error uploading files: $_" -ForegroundColor Yellow - Write-Host "Manifest files were created but not uploaded to the release." -ForegroundColor Yellow -} - -Write-Host "`n✅ Winget manifests updated successfully!" -ForegroundColor Green -Write-Host "Files updated:" -ForegroundColor Yellow -Write-Host " - $versionManifestPath" -ForegroundColor Cyan -Write-Host " - $localeManifestPath" -ForegroundColor Cyan -Write-Host " - $installerManifestPath" -ForegroundColor Cyan - -Write-Host "`nNext steps:" -ForegroundColor Yellow -Write-Host "1. Review the updated manifest files" -ForegroundColor White -Write-Host "2. Test the manifests locally with: winget install --manifest $manifestDir" -ForegroundColor White -Write-Host "3. Submit to winget-pkgs repository: https://github.com/microsoft/winget-pkgs" -ForegroundColor White -Write-Host "4. Create a PR following the winget contribution guidelines" -ForegroundColor White