Skip to main content

Materials-Science

Nuclear isotopes, radioactive decay chains, radiation dose, and hazardous chemical substances with toxicological thresholds and unit conversions.

Namespace: CSharpNumerics.Physics.Materials

using CSharpNumerics.Physics.Materials.Nuclear.Isotopes;
using CSharpNumerics.Physics.Materials.Nuclear.Decay;
using CSharpNumerics.Physics.Materials.Nuclear.DecayChains;
using CSharpNumerics.Physics.Materials.Nuclear.RadiationDose;
using CSharpNumerics.Physics.Materials.Chemical;

πŸ§ͺ Isotopes & Library​

// Built-in isotopes
Isotope cs = Isotope.Cs137; // Cs-137: tΒ½ = 30.17 years
Isotope iodine = Isotope.I131; // I-131: tΒ½ = 8.02 days

// Lookup by name
Isotope sr = IsotopeLibrary.Get("Sr90");
bool found = IsotopeLibrary.TryGet("Co60", out Isotope co);

// Properties
double halfLife = cs.HalfLifeSeconds; // ~9.51e8 s
double lambda = cs.Lambda; // decay constant (ln2/tΒ½)
double activity = cs.SpecificActivityBqKg; // Bq per kg
bool stable = Isotope.Ba137.IsStable; // true

// Filter by element
var caesiumIsotopes = IsotopeLibrary.ByElement(55); // Z = 55

// Register custom isotope at runtime
IsotopeLibrary.Register(new Isotope("Pu239", 94, 239, 7.6e11, 2.3e9,
DecayMode.Alpha, 0.0, 4.4e-5, 0));

βš›οΈ Radioactive Decay​

// Simple decay
double a0 = Decay.Activity(massKg: 0.001, Isotope.Cs137); // initial Bq
double aT = Decay.ActivityAtTime(a0, timeSeconds: 3600, Isotope.Cs137);
double remaining = Decay.RemainingMass(0.001, 3600, Isotope.Cs137);

Decay chains β€” Bateman equations for sequential decay:

// Decay chain (Bateman equations)
var chain = DecayChain.Cs137Chain(); // Cs137 β†’ Ba137m β†’ Ba137
double[] masses = chain.Evolve(
initialMasses: new[] { 1.0, 0.0, 0.0 },
timeSeconds: 3600);

double[] activities = chain.EvolveActivity(
new[] { 1.0, 0.0, 0.0 }, 3600); // Bq for each isotope

// I-131 chain
var iChain = DecayChain.I131Chain(); // I131 β†’ Xe131

☒️ Radiation Dose​

// Point-source dose rate (inverse-square law)
double svPerHour = RadiationDose.DoseRate(
activityBq: 1e9, distanceM: 1.0, Isotope.Cs137);

// Ground-shine dose (deposition on surface)
double sv = RadiationDose.GroundShineDose(
depositionBqM2: 1e6, Isotope.Cs137, timeSeconds: 3600);

// Inhalation dose
double inhDose = RadiationDose.InhalationDose(
concentrationBqM3: 100,
breathingRateM3s: RadiationDose.DefaultBreathingRate,
exposureTimeSeconds: 3600,
Isotope.Cs137);

🏭 Chemical Materials​

Namespace: CSharpNumerics.Physics.Materials.Chemical

Model hazardous chemical substances with toxicological thresholds (IDLH, ERPG, LC50, TLV) and unit conversions (kg/mΒ³ ↔ ppm). Integrates with the GIS dispersion pipeline via .WithMaterial(Materials.Chemical("Cl2")).

Built-in substances

FormulaNameMW (g/mol)IDLH (ppm)ERPG-2 (ppm)ERPG-3 (ppm)Phase
Clβ‚‚Chlorine70.910320Gas
NH₃Ammonia17.03002001000Gas
Hβ‚‚SHydrogen Sulfide34.15030100Gas
CHβ‚„Methane16.0N/A*500050000Gas
C₃Hβ‚ˆPropane44.12100500017000Liquefied gas

*Simple asphyxiant β€” no toxicity-based IDLH.

Substance lookup & custom registration

using CSharpNumerics.Physics.Materials.Chemical;

// Static instances
ChemicalSubstance cl = ChemicalSubstance.Chlorine;
ChemicalSubstance nh3 = ChemicalSubstance.Ammonia;

// Library lookup (case-insensitive)
ChemicalSubstance h2s = ChemicalLibrary.Get("H2S");
bool found = ChemicalLibrary.TryGet("CH4", out ChemicalSubstance methane);

// Properties
double mw = cl.MolarMass; // 70.906 g/mol
double idlh = cl.IDLH; // 10 ppm
double erpg2 = cl.ERPG2; // 3 ppm
double vapDens = cl.VapourDensity; // 2.49 (heavier than air)

// Register custom substance
ChemicalLibrary.Register(new ChemicalSubstance(
"COCl2", "Phosgene", "75-44-5",
98.92, 3.4, 8.3, PhaseAtSTP.Gas,
idlh: 2, erpg2: 0.5, erpg3: 1.5, lc50: 5,
tlvTwa: 0.1, tlvStel: 0.3));

Unit conversion (kg/mΒ³ ↔ ppm)

// Convert mass concentration to ppm at 20Β°C, 1 atm
double ppm = cl.KgM3ToPpm(2.95e-6); // β‰ˆ 1 ppm
double kgm3 = cl.PpmToKgM3(10); // IDLH in kg/mΒ³

GIS pipeline integration

using CSharpNumerics.Physics.Materials;

// Attach chemical material to a dispersion scenario
var result = RiskScenario
.ForGaussianPlume(5.0)
.FromSource(new Vector(0, 0, 10))
.WithWind(5, new Vector(1, 0, 0))
.WithStability(StabilityClass.D)
.WithMaterial(Materials.Chemical("Cl2")) // ← chemical
.OverGrid(new GeoGrid(-500, 500, -500, 500, 0, 100, 50))
.OverTime(0, 3600, 60)
.RunSingle();

// Snapshot layers: "ppm" and "toxicDose" (ppmΒ·s)
double[] ppmLayer = result.Snapshots[0].GetLayer("ppm");
double[] toxDose = result.Snapshots[0].GetLayer("toxicDose");

// IDLH exceedance polygon (Cl2 IDLH = 10 ppm)
var idlhZone = result.GeneratePeakExposurePolygon(10, "ppm");

// ERPG-2 exceedance polygon (3 ppm)
var erpg2Zone = result.GeneratePeakExposurePolygon(3, "ppm");

// Integrated toxic dose polygon
var integratedZone = result.GenerateIntegratedExposurePolygon(
threshold: 1000, layerName: "toxicDose"); // 1000 ppmΒ·s

🧱 Engineering Materials​

The EngineeringMaterial immutable struct bundles thermo-mechanical-electrical properties for multiphysics simulations. The EngineeringLibrary provides common pre-defined materials.

Namespace: CSharpNumerics.Physics.Materials.Engineering

Material Properties

PropertyUnitDescription
ThermalConductivityW/(mΒ·K)Heat conduction coefficient kk
SpecificHeatJ/(kgΒ·K)Specific heat capacity cpc_p
Densitykg/m³Mass density ρ\rho
DynamicViscosityPaΒ·sDynamic viscosity ΞΌ\mu
ElectricPermittivityF/mDielectric permittivity Ξ΅\varepsilon
YoungsModulusPaYoung's modulus EE
PoissonsRatioβ€”Poisson's ratio Ξ½\nu

Computed properties:

ComputedFormulaDescription
ThermalDiffusivityα=k/(ρcp)\alpha = k/(\rho c_p)Heat diffusion rate
KinematicViscosityν=μ/ρ\nu = \mu/\rhoFlow diffusion rate

Pre-defined Materials

using CSharpNumerics.Physics.Materials.Engineering;

var steel = EngineeringLibrary.Steel; // E=200 GPa, k=50 W/(mΒ·K)
var al = EngineeringLibrary.Aluminum; // E=69 GPa, k=237 W/(mΒ·K)
var cu = EngineeringLibrary.Copper; // E=117 GPa, k=401 W/(mΒ·K)
var water = EngineeringLibrary.Water; // μ=1e-3 Pa·s, ρ=1000 kg/m³
var air = EngineeringLibrary.Air; // ΞΌ=1.81e-5 PaΒ·s
var conc = EngineeringLibrary.Concrete; // E=30 GPa
var glass = EngineeringLibrary.Glass; // E=70 GPa

double alpha = steel.ThermalDiffusivity; // k/(ρ·cp)
double nu = water.KinematicViscosity; // μ/ρ

Custom Materials

var titanium = new EngineeringMaterial(
name: "Titanium",
thermalConductivity: 21.9, // W/(mΒ·K)
specificHeat: 523, // J/(kgΒ·K)
density: 4507, // kg/mΒ³
dynamicViscosity: 0, // not a fluid
electricPermittivity: 0,
youngsModulus: 116e9, // Pa
poissonsRatio: 0.34);

🌲 Fuel Models (Anderson 13)​

The FuelModel readonly struct holds Rothermel fuel parameters. All 13 standard Anderson models are pre-loaded in FuelLibrary.

// All models are in SI units
FuelModel chaparral = FuelLibrary.Get(FuelModelType.Chaparral);
// chaparral.SurfaceAreaToVolumeRatio = 4921 (1/m)
// chaparral.FuelBedDepth = 1.829 (m)
// chaparral.OvendryFuelLoad = 3.663 (kg/mΒ²)
// chaparral.MoistureOfExtinction = 0.20

// Iterate all models
foreach (var fuel in FuelLibrary.All)
Console.WriteLine($"{fuel.Type}: Ξ΄={fuel.FuelBedDepth:F3} m");

// Register a custom fuel model at runtime
FuelLibrary.Register(new FuelModel(
(FuelModelType)99, "Custom Sage", sigma: 5000,
delta: 0.5, w0: 1.2, Mx: 0.25, h: 18000));
ModelTypeσ (1/m)δ (m)w₀ (kg/m²)Mₓ
1Short Grass11,4830.3050.1660.12
2Timber/Grass Understory3,2810.3050.8970.15
3Tall Grass4,9210.7620.6750.25
4Chaparral6,5621.8293.6630.20
5Brush6,5620.6100.7840.20
6Dormant Brush5,7410.7620.6720.25
7Southern Rough5,7410.7620.5290.40
8Closed Timber Litter6,5620.0611.1210.30
9Hardwood Litter8,2020.0610.3270.25
10Timber/Litter Understory6,5620.3051.1210.25
11Light Logging Slash4,9210.3051.3450.15
12Medium Logging Slash4,9210.7013.3630.20
13Heavy Logging Slash4,9210.9145.6040.25

πŸ”¦ Optical Materials​

Pre-defined media are available via OpticalMaterialLibrary or the Materials.Optical() factory:

using CSharpNumerics.Physics.Materials;
using CSharpNumerics.Physics.Optics;

var diamond = OpticalMaterialLibrary.Diamond; // n=2.417
var bk7 = Materials.Optical("CrownGlass"); // factory lookup
var flint = Materials.Optical("SF11"); // n=1.7847, low Abbe -> high dispersion

🌊 Aquatic Contaminants​

Pre-loaded contaminant library with decay, adsorption, and toxicity data:

Namespace: CSharpNumerics.Physics.Materials.Water

using CSharpNumerics.Physics.Materials.Water;

// Built-in contaminants: Cs137, Sr90, I131, Benzene, Toluene, Cyanide,
// Mercury, Lead, Arsenic, EColi, Enterococcus, GenericHeat
var cs137 = ContaminantLibrary.Get("Cs-137");
// cs137.HalfLifeSeconds β‰ˆ 9.5Γ—10⁸, cs137.PartitionCoefficient = 1000, etc.

var benzene = AquaticContaminant.Benzene;
bool isConservative = benzene.IsConservative; // false (has half-life)
double lambda = benzene.DecayConstant; // ln(2) / tΒ½

// Create a custom contaminant
var custom = new AquaticContaminant("PFOS",
ContaminantType.Chemical,
halfLifeSeconds: 0, // persistent (conservative tracer)
partitionCoefficient: 10,
toxicityThresholdMgL: 0.00007,
lethalThresholdMgL: 50);

ContaminantLibrary.Register(custom);

Pre-defined Contaminants

ContaminantTypeHalf-lifeKd (L/kg)Toxicity (mg/L)
Cs-137Radioactive30.17 yr10000.002
Sr-90Radioactive28.8 yr2000.008
I-131Radioactive8.02 d100.003
BenzeneChemical180 d1.80.005
CyanideChemical∞00.07
MercuryChemical∞50000.001
E. coliBiological2 d00.0001

🦠 Biological Materials β€” Bioaerosol Agents​

Namespace: CSharpNumerics.Physics.Materials.Biological

Model biological aerosol agents (viruses, bacteria, spores) with viability decay, unit-mass conversion (kg/mΒ³ ↔ units/mΒ³), and screening-level infectious dose layers. Integrates with the GIS dispersion pipeline via Materials.Biological("Virus").

Agent properties​

PropertyDescription
CodeShort identifier for lookup (e.g. "Virus")
NameHuman-readable name
ClassificationBiologicalAgentClass enum: Virus, Bacteria, Spore
TypicalDiameterMicronsRepresentative aerodynamic diameter (Β΅m)
UnitMassKgMass of one biological unit (kg) β€” converts kg/mΒ³ β†’ units/mΒ³
ViabilityHalfLifeSecondsScreening half-life for viability in air (seconds)
IsPersistenttrue when viability is treated as non-decaying

Built-in agents​

CodeNameClassDiameter (Β΅m)Unit mass (kg)Viability tΒ½
VirusGeneric Viral AerosolVirus0.121 Γ— 10⁻¹⁸6 h
BacteriaGeneric Bacterial AerosolBacteria1.51 Γ— 10⁻¹⁡12 h
SporeGeneric Biological SporeSpore2.53 Γ— 10⁻¹⁡7 d

Agent lookup & custom registration​

using CSharpNumerics.Physics.Materials.Biological;

// Static instances
BiologicalAgent virus = BiologicalAgent.GenericVirus;
BiologicalAgent bacteria = BiologicalAgent.GenericBacteria;
BiologicalAgent spore = BiologicalAgent.GenericSpore;

// Library lookup (case-insensitive, supports aliases)
BiologicalAgent v = BiologicalLibrary.Get("virus");
BiologicalAgent s = BiologicalLibrary.Get("Spores"); // alias
bool found = BiologicalLibrary.TryGet("bacteria", out BiologicalAgent b);

// All registered agents (de-duplicated)
IReadOnlyCollection<BiologicalAgent> all = BiologicalLibrary.All();

// Register custom agent with aliases
BiologicalLibrary.Register(
new BiologicalAgent(
code: "Anthrax",
name: "Bacillus anthracis spore",
classification: BiologicalAgentClass.Spore,
typicalDiameterMicrons: 1.5,
unitMassKg: 1e-15,
viabilityHalfLifeSeconds: 30 * 24 * 3600), // 30 days
"anthrax", "b.anthracis");

Unit conversion (kg/mΒ³ ↔ units/mΒ³)​

BiologicalAgent bacteria = BiologicalAgent.GenericBacteria;

// Convert mass concentration to biological units per mΒ³
double units = bacteria.KgM3ToUnitsPerM3(2.5e-12); // 2500 units/mΒ³
double kgm3 = bacteria.UnitsPerM3ToKgM3(units); // round-trip

// Viability fraction at a given time
double fraction = bacteria.ViabilityFractionAt(6 * 3600); // after 6 h

GIS pipeline integration​

using CSharpNumerics.Physics.Materials;

// Attach biological material to a dispersion scenario
var sim = new PlumeSimulator(
2.0, 8, new Vector(1, 0, 0), 50,
new Vector(0, 0, 50), StabilityClass.D);
sim.Material = Materials.Biological("Bacteria");

var snaps = sim.Run(grid, tf);

// Snapshot layers: "bioUnits", "viableBioUnits", and "infectiousDose"
double[] bioUnits = snaps[0].GetLayer("bioUnits");
double[] viable = snaps[0].GetLayer("viableBioUnits");
double[] infectious = snaps[0].GetLayer("infectiousDose");

The viableBioUnits layer applies exponential viability decay based on the agent's half-life, so viable counts decrease over successive time steps while raw bioUnits reflect total transported mass only.