Cement chemist notation
Cement chemistry writes its phases in a shorthand that is opaque on first sight and indispensable once learned: alite is C3S, portlandite is CH, ettringite is C6AS̄3H32. This page is the key to it, and to how ChemistryLab reads it.
The idea is simple. A cement phase is an assembly of oxides, so each oxide gets a one-letter symbol and a phase is written as the oxides it contains, with their molar multiplicities. C3S is three parts lime to one of silica —
The alphabet
| Symbol | Oxide | Name | Molar mass |
|---|---|---|---|
C | lime | 56.08 g/mol | |
S | silica | 60.08 g/mol | |
A | alumina | 101.96 g/mol | |
F | ferric oxide | 159.69 g/mol | |
M | magnesia | 40.30 g/mol | |
K | potassium oxide | 94.20 g/mol | |
N | sodium oxide | 61.98 g/mol | |
T | titania | 79.86 g/mol | |
P | phosphorus pentoxide | 141.94 g/mol | |
H | water | 18.01 g/mol | |
C̄ | carbon dioxide | 44.01 g/mol | |
S̄ | sulfur trioxide | 80.06 g/mol | |
N̄ | nitrate | 62.00 g/mol |
The first four are the oxides a clinker is made of, and the four a datasheet always reports; the next five are the minor oxides; the last three are the acidic ones that take a bar.
Two conventions decide everything, and they are where a newcomer stumbles:
A letter stands for an oxide, not an element.
Cis lime, not carbon; Sis silica, not sulfur; Nis, not nitrogen. The bar marks the acidic oxides whose letter is already taken. Carbon dioxide is
C̄becauseCis lime, and sulfur trioxide isS̄becauseSis silica. The bar is a combining macron (U+0304): type the letter, then that character.
The table above is the mapping the parser itself uses, CEMENT_TO_MENDELEEV. The block below prints it from the code and checks the two agree, so the table cannot quietly drift from what ChemistryLab accepts:
using ChemistryLab
using DynamicQuantities
# (formula, molar mass) exactly as the table above states them
documented = Dict(
:C => ("CaO", 56.08), :S => ("SiO2", 60.08), :A => ("Al2O3", 101.96),
:F => ("Fe2O3", 159.69), :M => ("MgO", 40.30), :K => ("K2O", 94.20),
:N => ("Na2O", 61.98), :T => ("TiO2", 79.86), :P => ("P2O5", 141.94),
:H => ("H2O", 18.01), :C̄ => ("CO2", 44.01), :S̄ => ("SO3", 80.06),
:N̄ => ("NO3", 62.00),
)
for (letter, oxide) in CEMENT_TO_MENDELEEV
sp = Species(oxide)
formula, mass = documented[letter]
@assert atoms(sp) == atoms(Species(formula)) "table disagrees on $letter"
computed = round(ustrip(us"g/mol", sp.M), digits = 2)
@assert abs(computed - mass) < 0.005 "table gives $mass for $letter, the data give $computed"
println(rpad(string(letter), 3), " = ", rpad(unicode(sp), 8),
lpad(computed, 8), " g/mol")
endC = CaO 56.08 g/mol
M = MgO 40.3 g/mol
S = SiO₂ 60.08 g/mol
A = Al₂O₃ 101.96 g/mol
F = Fe₂O₃ 159.69 g/mol
K = K₂O 94.2 g/mol
N = Na₂O 61.98 g/mol
P = O₅P₂ 141.94 g/mol
T = TiO₂ 79.86 g/mol
C̄ = CO₂ 44.01 g/mol
S̄ = SO₃ 80.06 g/mol
N̄ = NO₃ 62.0 g/mol
H = H₂O 18.01 g/molH is water, which is why hydrates carry a large H count: C4AH13 is
Reading and building a phase
CemSpecies parses the shorthand. Ordinary ASCII digits and Unicode subscripts are both accepted, and non-integer multiplicities are allowed — which matters for C-S-H, whose lime-to-silica ratio is a composition, not a constant:
for name in ["C3S", "C2S", "C3A", "C4AF", "CH", "C₄AH₁₃", "C1.7SH4"]
sp = CemSpecies(name)
println(rpad(name, 9), " -> ", rpad(unicode(sp), 12),
" M = ", round(ustrip(us"g/mol", sp.M), digits = 2), " g/mol")
endC3S -> C₃S M = 228.31 g/mol
C2S -> C₂S M = 172.24 g/mol
C3A -> C₃A M = 270.19 g/mol
C4AF -> C₄AF M = 485.96 g/mol
CH -> CH M = 74.09 g/mol
C₄AH₁₃ -> C₄AH₁₃ M = 560.46 g/mol
C1.7SH4 -> C₁.₇SH₄ M = 227.47 g/molThe composition in ordinary elements is always available, so the shorthand is a way of writing a species and never a different kind of object:
atoms(CemSpecies("C3S"))OrderedCollections.OrderedDict{Symbol, Int64} with 3 entries:
:Ca => 3
:O => 5
:Si => 1The trap: Species and CemSpecies read the same string differently
This is the single most expensive mistake to make, and it is silent:
cem = CemSpecies("C3S")
ord = Species("C3S")
println("CemSpecies(\"C3S\") = ", unicode(cem), " M = ",
round(ustrip(us"g/mol", cem.M), digits = 2), " g/mol (3 CaO + SiO2)")
println("Species(\"C3S\") = ", unicode(ord), " M = ",
round(ustrip(us"g/mol", ord.M), digits = 2), " g/mol (3 carbons + 1 sulfur)")CemSpecies("C3S") = C₃S M = 228.31 g/mol (3 CaO + SiO2)
Species("C3S") = C₃S M = 68.09 g/mol (3 carbons + 1 sulfur)Both display as C₃S, and they differ by more than a factor of three in molar mass. Species applies the ordinary rules of chemical formulas, in which C is carbon and S is sulfur; CemSpecies applies the cement convention. Neither is wrong — they answer different questions — but a recipe that reaches for the wrong one is wrong everywhere downstream and raises no error.
Read a database phase by name, never by re-parsing its symbol
A phase read from a thermodynamic database already carries its composition and its molar mass. Look it up by name — byname["C3S"] — rather than rebuilding it from its symbol with Species, which would read C3S as three carbons and a sulfur. This is the reason the worked examples in this documentation never write a molar mass by hand.
The common phases, and what they weigh
Nothing below is typed from a table: each mass is computed from the formula and the element data.
phases = [
("C3S", "alite"),
("C2S", "belite"),
("C3A", "aluminate"),
("C4AF", "ferrite"),
("CS̄H2", "gypsum"),
("CH", "portlandite"),
("CC̄", "calcite"),
("C6AS̄3H32", "ettringite (AFt)"),
("C4AS̄H12", "monosulfoaluminate (AFm)"),
("C4AH13", "hydroxy-AFm"),
("C2ASH8", "strätlingite"),
("C3AH6", "hydrogarnet"),
("M5AH13", "hydrotalcite"),
]
for (name, english) in phases
sp = CemSpecies(name)
println(rpad(unicode(sp), 13), rpad(english, 28),
lpad(round(ustrip(us"g/mol", sp.M), digits = 2), 8), " g/mol")
endC₃S alite 228.31 g/mol
C₂S belite 172.24 g/mol
C₃A aluminate 270.19 g/mol
C₄AF ferrite 485.96 g/mol
CS̄H₂ gypsum 172.16 g/mol
CH portlandite 74.09 g/mol
CC̄ calcite 100.09 g/mol
C₆AS̄₃H₃₂ ettringite (AFt) 1255.07 g/mol
C₄AS̄H₁₂ monosulfoaluminate (AFm) 622.51 g/mol
C₄AH₁₃ hydroxy-AFm 560.46 g/mol
C₂ASH₈ strätlingite 418.32 g/mol
C₃AH₆ hydrogarnet 378.28 g/mol
M₅AH₁₃ hydrotalcite 537.68 g/molEttringite carries alumina, and the shorthand must say so
Ettringite is A gives C6S̄3H32, which parses without complaint, weighs 1153 g/mol and is not a cement phase. The shorthand is compact enough that an omission looks like a typo and behaves like a different substance.
Where the notation is used
CemSpeciesfor the species themselves;the oxide components of a stoichiometric decomposition — see Stoichiometric matrices, where a clinker phase is expressed over
C,S,A,F;the Bogue Calculation, which is that decomposition inverted and converted to mass.