I work on the supply side of electrical steel, the thin silicon steel that motor and transformer cores are stamped from. A lot of my week goes to one dull problem: the same material shows up under three different names.
A buyer in Vietnam asks for 50A310. The motor drawing from Germany says M310-50A. The mill certificate from China says 50W310. All three are the same steel: 0.50 mm thick, guaranteed core loss of no more than 3.10 W/kg at 1.5 T and 50 Hz.
If you build software for procurement, ERP, BOMs, or quality systems in manufacturing, you will run into this. Here is a small Python parser that handles it, plus a short calculation that tells you how much strip is actually in a coil.
The three naming schemes
For non-oriented electrical steel (the kind used in motors), the three common standards encode the same two numbers in different orders:
| Standard | Example | Pattern |
|---|---|---|
| JIS C 2552 (Japan) | 50A310 |
thickness×100, A, loss×100 |
| GB/T 2521.1 (China) | 50W310 |
thickness×100, W, loss×100 |
| EN 10106 / IEC (Europe) | M310-50A |
M, loss×100, -, thickness×100, A
|
Two things worth noticing:
-
The loss number is a ceiling, not a measurement.
310means the core loss is guaranteed to be at most 3.10 W/kg. The actual coil may test lower, and the real value is printed on the mill certificate. - The test point is part of the meaning. For non-oriented grades, the loss is measured at 1.5 T / 50 Hz. Grain-oriented (transformer) grades are measured at 1.7 T and use different letters, so a parser for motor steel should reject them instead of guessing.
The parser
import re
from dataclasses import dataclass
@dataclass(frozen=True)
class Grade:
thickness_mm: float # nominal thickness
max_loss: float # max core loss, W/kg at 1.5 T / 50 Hz
def as_jis(self):
return f"{self.t100}A{self.loss100}"
def as_en(self):
return f"M{self.loss100}-{self.t100}A"
def as_gb(self):
return f"{self.t100}W{self.loss100}"
@property
def t100(self):
return round(self.thickness_mm * 100)
@property
def loss100(self):
return round(self.max_loss * 100)
PATTERNS = {
"JIS C 2552": re.compile(r"^(?P<t>\d{2})A(?P<loss>\d{3,4})$"),
"GB/T 2521.1": re.compile(r"^(?P<t>\d{2})W(?P<loss>\d{3,4})$"),
"EN 10106": re.compile(r"^M(?P<loss>\d{3,4})-(?P<t>\d{2})A$"),
}
def parse(code):
code = code.strip().upper().replace(" ", "")
for standard, pattern in PATTERNS.items():
m = pattern.match(code)
if m:
grade = Grade(int(m["t"]) / 100, int(m["loss"]) / 100)
return standard, grade
raise ValueError(f"Not a non-oriented grade code I know: {code!r}")
The named groups t and loss keep the EN pattern readable even though it puts the numbers in the opposite order. Loss gets \d{3,4} because the high-loss grades go past 10 W/kg, for example M1000-65A.
Running it:
for code in ["50A310", "M310-50A", "50w310", "35A250", "M800-65A"]:
standard, g = parse(code)
print(f"{code:>9} {standard:<12} {g.thickness_mm:.2f} mm "
f"<= {g.max_loss:.2f} W/kg -> "
f"{g.as_jis()} / {g.as_en()} / {g.as_gb()}")
50A310 JIS C 2552 0.50 mm <= 3.10 W/kg -> 50A310 / M310-50A / 50W310
M310-50A EN 10106 0.50 mm <= 3.10 W/kg -> 50A310 / M310-50A / 50W310
50w310 GB/T 2521.1 0.50 mm <= 3.10 W/kg -> 50A310 / M310-50A / 50W310
35A250 JIS C 2552 0.35 mm <= 2.50 W/kg -> 35A250 / M250-35A / 35W250
M800-65A EN 10106 0.65 mm <= 8.00 W/kg -> 65A800 / M800-65A / 65W800
Because Grade is a frozen dataclass, equality works out of the box. That is the useful part for deduplicating a parts master:
>>> parse("50A310")[1] == parse("M310-50A")[1] == parse("50W310")[1]
True
And a grain-oriented code fails loudly instead of being misread:
>>> parse("30Q120")
ValueError: Not a non-oriented grade code I know: '30Q120'
What this parser does not do
- It does not prove the grade exists. The converter will happily turn a JIS code into an EN-style string, but not every thickness/loss pair is listed in every standard. Check the target standard's table (or a grade equivalents chart) before you print the result on a purchase order.
-
It ignores mill brand codes. Steelmakers sell the same material under their own catalogue codes, like
B50A310or35JN210. Those are not standard designations. Map them in a lookup table, not with a regex. -
It does not handle grain-oriented grades. They are measured at 1.7 T and use different letters in each standard. That deserves its own pattern set and its own
Gradetype, so the two can never be compared by accident.
Bonus: how much strip is in that coil?
Steel is bought by weight, but a stamping press consumes it by length. Converting between the two is plain geometry:
def strip_length_m(coil_kg, thickness_mm, width_mm, density=7.65):
"""Metres of strip in a coil. density in g/cm3."""
kg_per_m = thickness_mm * width_mm * density / 1000
return coil_kg / kg_per_m
The units cancel neatly: mm × mm × g/cm³ ÷ 1000 gives kg per metre. For a real order we shipped recently (a 4.8-ton coil of 0.50 mm strip slit to 113 mm):
>>> length = strip_length_m(4800, 0.50, 113)
>>> f"{length:,.0f} m"
'11,105 m'
That is about 11 km of strip on one coil, or roughly 2.3 km per ton.
Slit coils from that order, loaded in the container.
That number changes how you think about quality data. A burr height or camber check is done on a sample a few metres long, but the press runs 11 km of strip. If your QA software stores one measurement per coil, it is storing one point from an 11 km line.
The default density of 7.65 g/cm³ is a reasonable figure for silicon steel. Lower-loss grades contain more silicon and are slightly lighter, so if you need precision, take the density from the supplier's datasheet for the exact grade.
Wrapping up
- Normalize grade codes into a value object (thickness, max loss) as early as possible, and compare those, not strings.
- Keep non-oriented and grain-oriented grades in separate types. They are tested at different conditions, and comparing them is meaningless.
- Leave brand codes to a lookup table.
- When a system tracks steel by weight, store the width and thickness too, so you can always derive length.
If you have dealt with other messy industrial naming schemes (bearing codes, fastener specs, wire gauges), I would like to hear how you modelled them in the comments.
Disclosure: I work at Zhongxin Steel, a Chinese supplier of silicon steel, and the coil example comes from one of our orders. The code above is free to use.

Top comments (0)