Skip to content

Commit 2b71128

Browse files
Implement chemical quantities including ActivationEnergy, AmountOfSubstance, Concentration, DynamicViscosity, EnzymeActivity, MolarMass, PH, RateConstant, ReactionRate, and SurfaceTension. Update PhysicalDimensions and Units to include new chemical dimensions and units. Mark the Chemical domain as fully implemented in TODO_DOMAINS.md, enhancing the completeness of the quantities implementation.
1 parent 931d64e commit 2b71128

13 files changed

Lines changed: 1914 additions & 59 deletions

.cursor/rules/derived-cursor-rules.mdc

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,12 @@ When adding XML documentation comments:
243243
2. Remove the `BaseUnit` property from `PhysicalQuantity` and its interface.
244244
3. Update `PhysicalQuantity.In()` to use `Dimension.BaseUnit`.
245245
* When implementing PhysicalQuantity classes, ensure the containing type is one of the parameters in the generic type definition.
246+
* When creating new quantities, implement the following:
247+
* Add any necessary dimensions to `PhysicalDimensions.cs`.
248+
* Add any new units to `Units.cs`.
249+
* Follow the existing code style and patterns.
250+
* Include XML documentation comments.
251+
* Implement mathematical relationships between related quantities.
246252

247253
## DEBUGGING
248254

@@ -319,6 +325,12 @@ When adding XML documentation comments:
319325
* When encountering CS0108 errors, 'SoundPressureLevel<T>.Create(T)' hides inherited member, add the `new` keyword if hiding was intended.
320326
* When encountering CS0200 errors, Property or indexer 'PhysicalQuantity<SoundIntensityLevel<T>, T>.Value' cannot be assigned to -- it is read only, make sure you are not trying to assign to a read-only property.
321327
* When encountering CA1062 errors, In externally visible method 'SoundPressureLevel<T> SoundPressureLevel<T>.FromSoundPressure(SoundPressure<T> soundPressure)', validate parameter 'soundPressure' is non-null before using it. If appropriate, throw an 'ArgumentNullException' when the argument is 'null' add a null check for the reference parameter.
328+
* When fixing CS0704 errors (Cannot do non-virtual member lookup in 'T' because it is a type parameter), add the appropriate generic constraints (e.g., `where T : struct, INumber<T>, IFloatingPoint<T>`).
329+
* When fixing CA1062 errors (In externally visible method X, validate parameter Y is non-null before using it), add `ArgumentNullException.ThrowIfNull(parameterName)` at the beginning of the method.
330+
* When a file is missing the standard header, add the following to the top of the file:
331+
```csharp
332+
// Copyright (c) KTSU. All rights reserved.
333+
```
322334

323335
## WORKFLOW & RELEASE RULES
324336

@@ -339,9 +351,4 @@ When adding XML documentation comments:
339351
* For ease of use, use a single common namespace for all types in the library.
340352
* SemanticStringValidationExamples can just be built in semantic types where appropriate
341353
* The built in semantic string types should not need the string suffix.
342-
* Instead of creating generic built-in types like `Base64String`, use validation attributes like `[IsBase64]`, `[IsGuid]`, `[IsEmailAddress]` etc. that can be applied to domain-specific semantic string types.
343-
* Validation attributes should be separated into their own files.
344-
* Instead of creating generic built-in types like `Base6String`, use validation attributes like `[IsBase64]`, `[IsGuid]`, `[IsEmailAddress]` etc. that can be applied to domain-specific semantic string types.
345-
* The built in semantic string types are too niche or too simple, create a collection of useful builtin semantic string types with appropriate validations, where they aren't already implemented in dotnet, like Uri is.
346-
* Validation attributes should be separated into their own files.
347-
* Instead of creating generic built-in types like `Base64String`, use validation attributes like `[IsBase64]`, `[IsGuid]`, `[IsEmailAddress]` etc
354+
*

.specstory/history/2025-06-12_03-24-tasks-for-completing-quantities-implementation.md

Lines changed: 1812 additions & 0 deletions
Large diffs are not rendered by default.

Semantics/Quantities/Chemical/ActivationEnergy.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// Copyright (c) KTSU. All rights reserved.
1+
// Copyright (c) ktsu.dev
2+
// All rights reserved.
3+
// Licensed under the MIT license.
24

35
namespace ktsu.Semantics;
46

@@ -10,7 +12,7 @@ namespace ktsu.Semantics;
1012
/// </summary>
1113
/// <typeparam name="T">The numeric type for the activation energy value.</typeparam>
1214
public sealed record ActivationEnergy<T> : PhysicalQuantity<ActivationEnergy<T>, T>
13-
where T : struct, INumber<T>
15+
where T : struct, INumber<T>, IFloatingPoint<T>
1416
{
1517
/// <summary>Gets the physical dimension of activation energy [M L² T⁻² N⁻¹].</summary>
1618
public override PhysicalDimension Dimension => PhysicalDimensions.ActivationEnergy;
@@ -38,7 +40,7 @@ public static ActivationEnergy<T> FromArrheniusPlot(RateConstant<T> rateConstant
3840
T t2 = temperature2.In(Units.Kelvin);
3941
T gasConstant = T.CreateChecked(8.314); // J/(mol·K)
4042

41-
T lnRatio = T.Log(k2 / k1);
43+
T lnRatio = T.CreateChecked(Math.Log(double.CreateChecked(k2 / k1)));
4244
T tempDifference = (T.One / t1) - (T.One / t2);
4345
T ea = -gasConstant * lnRatio / tempDifference;
4446
return Create(ea);
@@ -80,7 +82,7 @@ public T CalculateRateRatio(Temperature<T> temperature1, Temperature<T> temperat
8082
T t2 = temperature2.In(Units.Kelvin);
8183
T gasConstant = T.CreateChecked(8.314); // J/(mol·K)
8284

83-
T exponent = (ea / gasConstant) * ((T.One / t1) - (T.One / t2));
84-
return T.Exp(exponent);
85+
T exponent = ea / gasConstant * ((T.One / t1) - (T.One / t2));
86+
return T.CreateChecked(Math.Exp(double.CreateChecked(exponent)));
8587
}
8688
}

Semantics/Quantities/Chemical/AmountOfSubstance.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// Copyright (c) KTSU. All rights reserved.
1+
// Copyright (c) ktsu.dev
2+
// All rights reserved.
3+
// Licensed under the MIT license.
24

35
namespace ktsu.Semantics;
46

@@ -43,6 +45,7 @@ public T GetNumberOfEntities()
4345
/// <returns>The mass of the substance.</returns>
4446
public Mass<T> CalculateMass(MolarMass<T> molarMass)
4547
{
48+
ArgumentNullException.ThrowIfNull(molarMass);
4649
T molesValue = In(Units.Mole);
4750
T molarMassValue = molarMass.In(Units.GramPerMole);
4851
T massInGrams = molesValue * molarMassValue;

Semantics/Quantities/Chemical/Concentration.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Copyright (c) ktsu.dev
2+
// All rights reserved.
3+
// Licensed under the MIT license.
4+
15
namespace ktsu.Semantics;
26

37
using System.Numerics;
@@ -22,6 +26,8 @@ public Concentration() : base() { }
2226
/// <returns>The molarity (mol/L).</returns>
2327
public static Concentration<T> FromMolarity(AmountOfSubstance<T> amountOfSubstance, Volume<T> volume)
2428
{
29+
ArgumentNullException.ThrowIfNull(amountOfSubstance);
30+
ArgumentNullException.ThrowIfNull(volume);
2531
T moles = amountOfSubstance.In(Units.Mole);
2632
T liters = volume.In(Units.Liter);
2733
T molarity = moles / liters;
@@ -34,6 +40,8 @@ public static Concentration<T> FromMolarity(AmountOfSubstance<T> amountOfSubstan
3440
/// <returns>The concentration in ppm.</returns>
3541
public static Concentration<T> FromPartsPerMillion(Mass<T> soluteMass, Mass<T> solutionMass)
3642
{
43+
ArgumentNullException.ThrowIfNull(soluteMass);
44+
ArgumentNullException.ThrowIfNull(solutionMass);
3745
T soluteGrams = soluteMass.In(Units.Gram);
3846
T solutionGrams = solutionMass.In(Units.Gram);
3947
T ratio = soluteGrams / solutionGrams;
@@ -47,6 +55,8 @@ public static Concentration<T> FromPartsPerMillion(Mass<T> soluteMass, Mass<T> s
4755
/// <returns>The concentration in % w/v.</returns>
4856
public static Concentration<T> FromWeightVolumePercent(Mass<T> soluteMass, Volume<T> solutionVolume)
4957
{
58+
ArgumentNullException.ThrowIfNull(soluteMass);
59+
ArgumentNullException.ThrowIfNull(solutionVolume);
5060
T massGrams = soluteMass.In(Units.Gram);
5161
T volumeML = solutionVolume.In(Units.Milliliter);
5262
T ratio = massGrams / volumeML;
@@ -60,10 +70,12 @@ public static Concentration<T> FromWeightVolumePercent(Mass<T> soluteMass, Volum
6070
/// <returns>The final concentration after dilution.</returns>
6171
public Concentration<T> Dilute(Volume<T> initialVolume, Volume<T> finalVolume)
6272
{
73+
ArgumentNullException.ThrowIfNull(initialVolume);
74+
ArgumentNullException.ThrowIfNull(finalVolume);
6375
T c1 = In(Units.Molar);
6476
T v1 = initialVolume.In(Units.Liter);
6577
T v2 = finalVolume.In(Units.Liter);
66-
T c2 = (c1 * v1) / v2;
78+
T c2 = c1 * v1 / v2;
6779
return Create(c2);
6880
}
6981
}

Semantics/Quantities/Chemical/DynamicViscosity.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// Copyright (c) KTSU. All rights reserved.
1+
// Copyright (c) ktsu.dev
2+
// All rights reserved.
3+
// Licensed under the MIT license.
24

35
namespace ktsu.Semantics;
46

Semantics/Quantities/Chemical/EnzymeActivity.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// Copyright (c) KTSU. All rights reserved.
1+
// Copyright (c) ktsu.dev
2+
// All rights reserved.
3+
// Licensed under the MIT license.
24

35
namespace ktsu.Semantics;
46

Semantics/Quantities/Chemical/MolarMass.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// Copyright (c) KTSU. All rights reserved.
1+
// Copyright (c) ktsu.dev
2+
// All rights reserved.
3+
// Licensed under the MIT license.
24

35
namespace ktsu.Semantics;
46

Semantics/Quantities/Chemical/RateConstant.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// Copyright (c) KTSU. All rights reserved.
1+
// Copyright (c) ktsu.dev
2+
// All rights reserved.
3+
// Licensed under the MIT license.
24

35
namespace ktsu.Semantics;
46

@@ -10,7 +12,7 @@ namespace ktsu.Semantics;
1012
/// </summary>
1113
/// <typeparam name="T">The numeric type for the rate constant value.</typeparam>
1214
public sealed record RateConstant<T> : PhysicalQuantity<RateConstant<T>, T>
13-
where T : struct, INumber<T>
15+
where T : struct, INumber<T>, IFloatingPoint<T>
1416
{
1517
/// <summary>Gets the physical dimension of rate constant [T⁻¹] for first-order reactions.</summary>
1618
public override PhysicalDimension Dimension => PhysicalDimensions.RateConstant;
@@ -34,7 +36,7 @@ public static RateConstant<T> FromArrheniusEquation(T preExponentialFactor,
3436
T gasConstant = T.CreateChecked(8.314); // J/(mol·K)
3537

3638
T exponent = -ea / (gasConstant * temp);
37-
T k = preExponentialFactor * T.Exp(exponent);
39+
T k = preExponentialFactor * T.CreateChecked(Math.Exp(double.CreateChecked(exponent)));
3840
return Create(k);
3941
}
4042

@@ -56,8 +58,8 @@ public RateConstant<T> AtTemperature(Temperature<T> temperature1, Temperature<T>
5658
T ea = activationEnergy.In(Units.JoulesPerMole);
5759
T gasConstant = T.CreateChecked(8.314); // J/(mol·K)
5860

59-
T exponent = (ea / gasConstant) * ((T.One / t1) - (T.One / t2));
60-
T ratio = T.Exp(exponent);
61+
T exponent = ea / gasConstant * ((T.One / t1) - (T.One / t2));
62+
T ratio = T.CreateChecked(Math.Exp(double.CreateChecked(exponent)));
6163
T k2 = k1 * ratio;
6264
return Create(k2);
6365
}

Semantics/Quantities/Chemical/ReactionRate.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// Copyright (c) KTSU. All rights reserved.
1+
// Copyright (c) ktsu.dev
2+
// All rights reserved.
3+
// Licensed under the MIT license.
24

35
namespace ktsu.Semantics;
46

@@ -10,7 +12,7 @@ namespace ktsu.Semantics;
1012
/// </summary>
1113
/// <typeparam name="T">The numeric type for the reaction rate value.</typeparam>
1214
public sealed record ReactionRate<T> : PhysicalQuantity<ReactionRate<T>, T>
13-
where T : struct, INumber<T>
15+
where T : struct, INumber<T>, IFloatingPoint<T>
1416
{
1517
/// <summary>Gets the physical dimension of reaction rate [N L⁻³ T⁻¹].</summary>
1618
public override PhysicalDimension Dimension => PhysicalDimensions.ReactionRate;
@@ -52,7 +54,7 @@ public static ReactionRate<T> FromRateLaw(RateConstant<T> rateConstant,
5254
T cA = concentrationA.In(Units.Molar);
5355
T cB = concentrationB.In(Units.Molar);
5456

55-
T rateValue = k * T.Pow(cA, orderA) * T.Pow(cB, orderB);
57+
T rateValue = k * T.CreateChecked(Math.Pow(double.CreateChecked(cA), double.CreateChecked(orderA))) * T.CreateChecked(Math.Pow(double.CreateChecked(cB), double.CreateChecked(orderB)));
5658
return Create(rateValue);
5759
}
5860

@@ -72,7 +74,7 @@ public RateConstant<T> CalculateRateConstant(Concentration<T> concentrationA, Co
7274
T cA = concentrationA.In(Units.Molar);
7375
T cB = concentrationB.In(Units.Molar);
7476

75-
T denominator = T.Pow(cA, orderA) * T.Pow(cB, orderB);
77+
T denominator = T.CreateChecked(Math.Pow(double.CreateChecked(cA), double.CreateChecked(orderA))) * T.CreateChecked(Math.Pow(double.CreateChecked(cB), double.CreateChecked(orderB)));
7678
T k = rate / denominator;
7779
return RateConstant<T>.Create(k);
7880
}

0 commit comments

Comments
 (0)