Chemical calculator: Difference between revisions
(Added Go) |
No edit summary |
||
Line 1: | Line 1: | ||
{{draft task}} |
{{draft task}} |
||
This application calculates the molar mass given |
This application calculates the molar mass given the molecule's chemical formula. |
||
;Introduction |
|||
⚫ | |||
* A molecule consists of atoms. E.g. methane, CH4 has one carbon atom and four hydrogen atoms. |
|||
* Grouping with () |
|||
* The mass of water, H2O is 1.008 * 2 + 15.999 = 18.015 |
|||
* Repetition |
|||
* An atom name consists of one or two letters, e.g. H (Hydrogen), He (Helium). |
|||
* The number of atoms is stated behind the atom or atom group. |
|||
* An atom group is specified using parenthesis. Example: Butyric acid has two CH3 groups (CH3)2CHCOOH |
|||
* A group may contain other groups, e.g. COOH(C(CH3)2)3CH3 |
|||
⚫ | |||
;Background |
|||
* The mass is dimensionless. It is relative to 1/12 of Carbon-12. |
|||
* Carbon-12 has exactly 12 protons and 12 electrons. |
|||
* One mole of H2O has the mass 18.015 grams. |
|||
* One mole is 6.02214076E23 |
|||
;Examples: |
;Examples: |
||
Line 15: | Line 27: | ||
* assert 84.162 == molar_mass('C6H12') |
* assert 84.162 == molar_mass('C6H12') |
||
* assert 186.29499999999996 == molar_mass('COOH(C(CH3)2)3CH3') |
* assert 186.29499999999996 == molar_mass('COOH(C(CH3)2)3CH3') |
||
;Link |
|||
https://en.wikipedia.org/wiki/Molecular_mass |
|||
=={{header|Go}}== |
=={{header|Go}}== |
Revision as of 20:16, 18 March 2019
This application calculates the molar mass given the molecule's chemical formula.
- Introduction
- A molecule consists of atoms. E.g. methane, CH4 has one carbon atom and four hydrogen atoms.
- The mass of water, H2O is 1.008 * 2 + 15.999 = 18.015
- An atom name consists of one or two letters, e.g. H (Hydrogen), He (Helium).
- The number of atoms is stated behind the atom or atom group.
- An atom group is specified using parenthesis. Example: Butyric acid has two CH3 groups (CH3)2CHCOOH
- A group may contain other groups, e.g. COOH(C(CH3)2)3CH3
- The atom masses are available in the Python section below.
- Background
- The mass is dimensionless. It is relative to 1/12 of Carbon-12.
- Carbon-12 has exactly 12 protons and 12 electrons.
- One mole of H2O has the mass 18.015 grams.
- One mole is 6.02214076E23
- Examples
- assert 1.008 == molar_mass('H')
- assert 2.016 == molar_mass('H2')
- assert 18.015 == molar_mass('H2O')
- assert 142.03553856000002 == molar_mass('Na2SO4')
- assert 84.162 == molar_mass('C6H12')
- assert 186.29499999999996 == molar_mass('COOH(C(CH3)2)3CH3')
- Link
https://en.wikipedia.org/wiki/Molecular_mass
Go
<lang go>package main
import (
"fmt" "strconv" "strings"
)
type any = interface{}
type ht = map[string]int
type Stack []any
func (s *Stack) push(a any) {
*s = append(*s, a)
}
func (s *Stack) pop() any {
le := len(*s) if le == 0 { panic("Attempt to pop from an empty stack") } le-- a := (*s)[le] *s = (*s)[:le] return a
}
var atomicMass = map[string]float64{
"H": 1.008, "He": 4.002602, "Li": 6.94, "Be": 9.0121831, "B": 10.81, "C": 12.011, "N": 14.007, "O": 15.999, "F": 18.998403163, "Ne": 20.1797, "Na": 22.98976928, "Mg": 24.305, "Al": 26.9815385, "Si": 28.085, "P": 30.973761998, "S": 32.06, "Cl": 35.45, "Ar": 39.948, "K": 39.0983, "Ca": 40.078, "Sc": 44.955908, "Ti": 47.867, "V": 50.9415, "Cr": 51.9961, "Mn": 54.938044, "Fe": 55.845, "Co": 58.933194, "Ni": 58.6934, "Cu": 63.546, "Zn": 65.38, "Ga": 69.723, "Ge": 72.630, "As": 74.921595, "Se": 78.971, "Br": 79.904, "Kr": 83.798, "Rb": 85.4678, "Sr": 87.62, "Y": 88.90584, "Zr": 91.224, "Nb": 92.90637, "Mo": 95.95, "Ru": 101.07, "Rh": 102.90550, "Pd": 106.42, "Ag": 107.8682, "Cd": 112.414, "In": 114.818, "Sn": 118.710, "Sb": 121.760, "Te": 127.60, "I": 126.90447, "Xe": 131.293, "Cs": 132.90545196, "Ba": 137.327, "La": 138.90547, "Ce": 140.116, "Pr": 140.90766, "Nd": 144.242, "Pm": 145, "Sm": 150.36, "Eu": 151.964, "Gd": 157.25, "Tb": 158.92535, "Dy": 162.500, "Ho": 164.93033, "Er": 167.259, "Tm": 168.93422, "Yb": 173.054, "Lu": 174.9668, "Hf": 178.49, "Ta": 180.94788, "W": 183.84, "Re": 186.207, "Os": 190.23, "Ir": 192.217, "Pt": 195.084, "Au": 196.966569, "Hg": 200.592, "Tl": 204.38, "Pb": 207.2, "Bi": 208.98040, "Po": 209, "At": 210, "Rn": 222, "Fr": 223, "Ra": 226, "Ac": 227, "Th": 232.0377, "Pa": 231.03588, "U": 238.02891, "Np": 237, "Pu": 244, "Am": 243, "Cm": 247, "Bk": 247, "Cf": 251, "Es": 252, "Fm": 257,
}
// "H2O" => H 2 O func parse(s string) (result []string, pattern string) {
for i := 0; i < len(s); { switch { case s[i] >= 'A' && s[i] <= 'Z': if i+1 < len(s) && s[i+1] >= 'a' && s[i+1] <= 'z' { result = append(result, fmt.Sprintf("%c%c", s[i], s[i+1])) pattern += "A" i += 2 } else { result = append(result, string(s[i])) pattern += "A" i++ } case s[i] >= '0' && s[i] <= '9': antal := int(s[i]) - 48 i++ for i < len(s) && s[i] >= '0' && s[i] <= '9' { antal = antal*10 + int(s[i]) - 48 i++ } result = append(result, strconv.Itoa(antal)) pattern += "1" default: result = append(result, string(s[i])) pattern += string(s[i]) i++ } } return
}
// H 2 O => H * 2 + O func pass1(m1 []string, m2 string) (result []string) {
const symbols = "A1()" matrix := [4]string{ "+*+=", // A = Atom "+x+=", // 1 = Count "=x=x", // ( "+*+=", // ) } add := func(a string) []string { return []string{a, "+"} } mul := func(a string) []string { return []string{a, "*"} } err := func(a string) []string { return []string{} } nop := func(a string) []string { return []string{a} } operation := map[byte]func(string) []string{ '+': add, '*': mul, 'x': err, '=': nop, } for i := 0; i < len(m1)-1; i++ { ch0 := m2[i] ch1 := m2[i+1] i0 := strings.IndexByte(symbols, ch0) i1 := strings.IndexByte(symbols, ch1) op := matrix[i0][i1] result = append(result, operation[op](m1[i])...) } result = append(result, m1[len(m1)-1]) return
}
// H * 2 + O => H 2 * O + func pass2(tokens []string) (result []string) {
ops := "+*" stack := new(Stack) for _, token := range tokens { switch token { case "(": stack.push(token) case ")": for len(*stack) > 0 { op := stack.pop().(string) if op == "(" { break } result = append(result, op) } default: if strings.Index(ops, token) >= 0 { for len(*stack) > 0 { op := (*stack)[len(*stack)-1].(string) if strings.Index(ops, op) == -1 { break } if strings.Index(ops, token) >= strings.Index(ops, op) { break } stack.pop() result = append(result, op) } stack.push(token) } else { result = append(result, token) } } } for len(*stack) > 0 { result = append(result, stack.pop().(string)) } return
}
// H 2 * O + => { H:2, O:1 } func pass3(rpn []string) ht {
stack := new(Stack) for _, item := range rpn { switch { case item == "+": h1 := stack.pop().(ht) h2 := (*stack)[len(*stack)-1].(ht) for key := range h1 { if h2[key] > 0 { h2[key] += h1[key] } else { h2[key] = h1[key] } } case item == "*": antal, _ := strconv.Atoi(stack.pop().(string)) hash := (*stack)[len(*stack)-1].(ht) for key := range hash { hash[key] *= antal } case atomicMass[item] > 0.0: res := make(ht) res[item] = 1 stack.push(res) default: stack.push(item) } } return stack.pop().(ht)
}
// { H:2, O:1 } => 18.015 func pass4(atoms ht) float64 {
sum := 0.0 for key := range atoms { sum += atomicMass[key] * float64(atoms[key]) } return sum
}
func molarMass(molecule string) float64 {
atomList, pattern := parse(molecule) infix := pass1(atomList, pattern) rpn := pass2(infix) atoms := pass3(rpn) return pass4(atoms)
}
func main() {
molecules := []string{"H", "H2", "H2O", "Na2SO4", "C6H12", "COOH(C(CH3)2)3CH3"} for _, molecule := range molecules { mass := molarMass(molecule) fmt.Printf("%17s -> %g\n", molecule, mass) }
}</lang>
- Output:
H -> 1.008 H2 -> 2.016 H2O -> 18.015 Na2SO4 -> 142.03553856000002 C6H12 -> 84.162 COOH(C(CH3)2)3CH3 -> 186.29499999999996
Python
Atom masses
<lang python>ATOMIC_MASS = { 'H':1.008, 'He':4.002602, 'Li':6.94, 'Be':9.0121831, 'B':10.81, 'C':12.011, 'N':14.007, 'O':15.999, 'F':18.998403163, 'Ne':20.1797, 'Na':22.98976928, 'Mg':24.305, 'Al':26.9815385, 'Si':28.085, 'P':30.973761998, 'S':32.06, 'Cl':35.45, 'Ar':39.948, 'K':39.0983, 'Ca':40.078, 'Sc':44.955908, 'Ti':47.867, 'V':50.9415, 'Cr':51.9961, 'Mn':54.938044, 'Fe':55.845, 'Co':58.933194, 'Ni':58.6934, 'Cu':63.546, 'Zn':65.38, 'Ga':69.723, 'Ge':72.630, 'As':74.921595, 'Se':78.971, 'Br':79.904, 'Kr':83.798, 'Rb':85.4678, 'Sr':87.62, 'Y':88.90584, 'Zr':91.224, 'Nb':92.90637, 'Mo':95.95, 'Ru':101.07, 'Rh':102.90550, 'Pd':106.42, 'Ag':107.8682, 'Cd':112.414, 'In':114.818, 'Sn':118.710, 'Sb':121.760, 'Te':127.60, 'I':126.90447, 'Xe':131.293, 'Cs':132.90545196, 'Ba':137.327, 'La':138.90547, 'Ce':140.116, 'Pr':140.90766, 'Nd':144.242, 'Pm':145, 'Sm':150.36, 'Eu':151.964, 'Gd':157.25, 'Tb':158.92535, 'Dy':162.500, 'Ho':164.93033, 'Er':167.259, 'Tm':168.93422, 'Yb':173.054, 'Lu':174.9668, 'Hf':178.49, 'Ta':180.94788, 'W':183.84, 'Re':186.207, 'Os':190.23, 'Ir':192.217, 'Pt':195.084, 'Au':196.966569, 'Hg':200.592, 'Tl':204.38, 'Pb':207.2, 'Bi':208.98040, 'Po':209, 'At':210, 'Rn':222, 'Fr':223, 'Ra':226, 'Ac':227, 'Th':232.0377, 'Pa':231.03588, 'U':238.02891, 'Np':237, 'Pu':244, 'Am':243, 'Cm':247, 'Bk':247, 'Cf':251, 'Es':252, 'Fm':257 }</lang>
Solution
<lang python>from atomic_mass import ATOMIC_MASS
def parse(s): # 'H2O' => H 2 O result = [] pattern = i = 0 while i<len(s): if s[i].isupper(): if i+1<len(s) and s[i+1].islower(): result.append(s[i]+s[i+1]) pattern += 'A' i+=2 else: result.append(s[i]) pattern += 'A' i+=1 elif s[i].isdigit(): antal = int(s[i]) i+=1 while i<len(s) and s[i].isdigit(): antal = antal*10 + int(s[i]) i+=1 result.append(str(antal)) pattern += '1' else: result.append(s[i]) pattern += s[i] i+=1 return result,pattern
def pass1(m1,m2): # H 2 O => H * 2 + O SYMBOLS = 'A1()' matrix = [ '+*+=', # A = Atom '+x+=', # 1 = Count '=x=x', # ( '+*+='] # )
result = []
add = lambda a, b: [a, '+'] mul = lambda a, b: [a, '*'] error = lambda a, b: [] nop = lambda a, b: [a] operation = {'+': add, '*': mul, 'x': error, '=': nop}
for i in range(len(m1)-1): ch0 = m2[i] ch1 = m2[i+1] i0 = SYMBOLS.index(ch0) i1 = SYMBOLS.index(ch1) op = matrix[i0][i1] result += operation[op](m1[i],m1[i+1]) result.append(m1[-1]) return result
def pass2(tokens): # H * 2 + O => H 2 * O + ops = "+*" stack = [] result = []
for token in tokens: if token == '(': stack.append(token) elif token == ')': while len(stack) > 0: op = stack.pop() if op == '(': break result.append(op) else: if token in ops: while len(stack) > 0: op = stack[-1] if not (op in ops): break if ops.find(token) >= ops.find(op): break stack.pop() result.append(op) stack.append(token) else: result.append(token)
while len(stack) > 0: result.append(stack.pop()) return result
def pass3(rpn): # H 2 * O + => { H:2, O:1 } stack = [] for item in rpn: if item == '+': h1 = stack.pop() h2 = stack[-1] for key in h1: if key in h2: h2[key] += h1[key] else: h2[key] = h1[key] elif item == '*': antal = int(stack.pop()) hash = stack[-1] for key in hash: hash[key] *= antal elif item in ATOMIC_MASS: res = {} res[item] = 1 stack.append(res) else: stack.append(item) return stack.pop()
def pass4(atoms): # { H:2, O:1 } => 18.015 return sum([ATOMIC_MASS[key] * atoms[key] for key in atoms])
def molar_mass(molecule): atom_list,pattern = parse(molecule) infix = pass1(atom_list,pattern) rpn = pass2(infix) atoms = pass3(rpn) return pass4(atoms)
#assert 'Na 10 C O O H ( C ( C H 3 ) 2 ) 3 C H 3' == ' '.join(atom_list) #assert 'A1AAAA(A(AA1)1)1AA1' == pattern #assert 'Na * 10 + C + O + O + H + ( C + ( C + H * 3 ) * 2 ) * 3 + C + H * 3' == (' '.join(infix)) #assert 'Na 10 * C O O H C C H 3 * + 2 * + 3 * C H 3 * + + + + + + +' == ' '.join(rpn) #assert {'Na':10, 'C': 11, 'O': 2, 'H': 22} == atoms
assert 1.008 == molar_mass('H') assert 2.016 == molar_mass('H2') assert 18.015 == molar_mass('H2O') assert 142.03553856000002 == molar_mass('Na2SO4') assert 84.162 == molar_mass('C6H12') assert 186.29499999999996 == molar_mass('COOH(C(CH3)2)3CH3')</lang>