Skip to content

Instantly share code, notes, and snippets.

@jwosty
Last active August 29, 2015 14:11
Show Gist options
  • Save jwosty/0e70ac6ec409e182901a to your computer and use it in GitHub Desktop.
Save jwosty/0e70ac6ec409e182901a to your computer and use it in GitHub Desktop.
Useful chemistry things
open Microsoft.FSharp.Data.UnitSystems.SI.UnitNames
open Microsoft.FSharp.Data.UnitSystems.SI.UnitSymbols
open System
open System.Text.RegularExpressions
[<Measure>] type g
let (|Integer|_|) str =
let mutable result = 0
if Int32.TryParse (str, &result) then Some(result) else None
let (|Regex|_|) regex input =
let m = Regex.Match (input, regex)
if m.Success then
let groups = [ for g in m.Groups -> g.Value ]
Some((if m.Groups.Count = 1 then groups else List.tail groups), input.Substring m.Length)
else None
let gPerKg = 1000.<g/mol>
/// Attempts to parse an integer from the beginning of the string. On success, yields the integer and the rest
/// of the string; if no integer is found, yields defaultValue and the rest
let parseInt defaultValue input =
let m = Regex.Match (input, "^\d+")
if m.Success then int m.Value, input.Substring m.Length else defaultValue, input
type Element =
{ name: string; abbreviation: string; atomicNumber: int; mass: float<g/mol> }
static member PeriodicTable =
[|"Hydrogen", "H", 1, 1.00794<g/mol>; "Helium", "He", 2, 4.002602<g/mol>; "Lithium", "Li", 3, 6.941<g/mol>; "Beryllium", "Be", 4, 9.012182<g/mol>; "Boron", "B", 5, 10.811<g/mol>; "Carbon", "C", 6, 12.0107<g/mol>;
"Nitrogen", "N", 7, 14.0067<g/mol>; "Oxygen", "O", 8, 15.9994<g/mol>; "Fluorine", "F", 9, 18.9984032<g/mol>; "Neon", "N", 10, 20.1797<g/mol>;
"Sodium", "Na", 11, 22.98976928<g/mol>; "Magnesium", "Mg", 12, 24.3050<g/mol>; "Aluminum", "Al", 13, 26.9815386<g/mol>; "Silicon", "Si", 14, 28.0855<g/mol>;
"Phosphorus", "P", 15, 30.973762<g/mol>; "Sulfer", "S", 16, 32.065<g/mol>; "Chlorine", "Cl", 17, 35.065<g/mol>; "Argon", "Ar", 18, 39.948<g/mol>;
"Potassium", "K", 19, 39.0983<g/mol>; "Calcium", "Ca", 20, 40.078<g/mol>; "Gallium", "Ga", 31, 69.723<g/mol>; "Garmanium", "Ge", 32, 72.64<g/mol>;
"Arsenic", "As", 33, 74.9216<g/mol>; "Selenium", "Se", 34, 78.96<g/mol>; "Bromine", "Br", 35, 79.904<g/mol>; "Krypton", "Kr", 36, 83.798<g/mol>|]
|> Array.map (fun (name, abr, num, mass) -> { name = name; abbreviation = abr; atomicNumber = num; mass = mass })
/// Looks up an element by name or abbreviation (e.g. Gold/Au). Returns None if the name is not recognized.
static member TryFind name = Array.tryFind (fun element -> name = element.name || name = element.abbreviation) Element.PeriodicTable
/// Looks up an element by name or abbreviation (e.g. Gold/Au). Throws an error if the name is not recognized.
static member Find name = match Element.TryFind name with | Some(e) -> e | None -> failwith (name + " is not a recognized element name or abbreviation.")
type ChemicalFormula =
{ elements: (int*Element) list }
member this.molecularMass = this.elements |> List.fold (fun formulaMass (quantity, element) -> formulaMass + (element.mass * float quantity)) 0.<g/mol>
static member Parse formulaString =
let (|ElementRegex|_|) = (|Regex|_|) "^([A-Z][a-z]*)(\d*)"
match formulaString with
| "" -> []
| ElementRegex ([name; ""], rest) -> (1, Element.Find name) :: ChemicalFormula.Parse rest
| ElementRegex ([name; Integer quantity], rest) -> (quantity, Element.Find name) :: ChemicalFormula.Parse rest
let findElement = Element.Find
let parseFormula = ChemicalFormula.Parse
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment