Dimensionally-inconsistent Calculations (Emperical formulae)#

Sometimes calculations and formulae are based on curve-fitting the results of various tests. The resulting formula from the curve-fitting may assume input units with a particular scaling and produce outputs assumed to be in a different scaling.

[24]:
import forallpeople as si
si.environment('default')

Dimensionally-inconsistent radicals#

Observed in many concrete design codes is a value expressed as \(\sqrt{f'_c}\) where \(f'_c\) is in scaled units of stress (e.g. MPa) and \(\sqrt{f'_c}\) is also in units of stress (MPa in this example).

The intention behind this particular expression is to compute the square-root of 35 and for that value to be in units of MPa.

Strategy 1 - Manually add the units back on#

[25]:
import math
MPa = 1e6 * si.Pa
f_c = 35e6 * si.Pa

math.sqrt(f_c) * MPa
[25]:
5.916 MPa

Strategy 2 - Define a custom sqrt function#

[26]:
import math
MPa = 1e6 * si.Pa

def sqrt(x):
    MPa = 1e6 * si.Pa
    if (
        isinstance(x, si.Physical)
        and x.dimensions == (1, -1, -2, 0, 0, 0, 0)
    ):
        return math.sqrt(x) * MPa # Special case
    else:
        return x**(1/2) # General case

f_c = 35 * MPa # Example of special case
area = 0.0350 * si.m**2 # Example of general case

display(
    f_c,
    sqrt(f_c)
)

display(
    area,
    sqrt(area)
)
35.000 MPa
5.916 MPa
35000.000 mm2
187.083 mm

Strategy 3#

Continuing with the example above, a calculation may intend to compute the square root of 35e6 (instead of 35) and apply the same units to that resulting value.

This strategy can be combined with Strategy 2 to define a custom sqrt function for special cases.

[27]:
import math
MPa = 1e6 * si.Pa
f_c = 35 * MPa

value, unit = f_c.split()
math.sqrt(value) * unit
[27]:
5.916 kPa

Hidden units#

Another kind of seemingly dimensionally-inconsistent formula may just be a case where units are assumed within a numerical value but not communicated.

An example commonly used in Canadian flexural concrete design:

\[A_s = 0.0015 (f'_c) (b) (d) - \sqrt{d^2 - \frac{3.85 M_f}{(f'_c) (b)}}\]

This formula for calculating the required area of flexural tension steel should result in values of area (say, in mm2). In its current form, however, it is dimensionally-inconsistent because the value of 0.0015 contains hidden units of 1 / MPa.

[28]:
si.environment('default')
MPa = 1e6 * si.Pa

f_c = 35 * MPa
b = 0.30 * si.m
d = 0.60 * si.m
M_f = 200e3 * si.N * si.m

A_s = 0.0015 * f_c * b * (d - sqrt(d**2 - (3.85 * M_f)/(f_c * b)))
A_s # Resulting units are in N, which are not expected
[28]:
1.017 kN

Here, we will manually add the units into the calculation as (0.0015 / MPa).

[30]:
A_s = (0.0015 / MPa) * f_c * b * (d - sqrt(d**2 - (3.85 * M_f)/(f_c * b)))
A_s # These are the correct dimensions
[30]:
1017.251 mm2

Completely inconsistent formulae#

Last, there are times where the formula essentially serves as a numeric transformation and the units simply need to be manually applied.

An example from the Canadian wood design code:

\[QS_i = 14t \sqrt{\frac{d_e}{1 - \frac{d_e}{d}}}\]

This formula for calculating tension resistance in the parallel-to-grain direction of a bolted timber connection is based on empirically-measured fracture test results.

All symbols (t, d_e, and d) are numbers, scaled to units of mm, and the resulting value of QS_i is a number in units of force, N.

Because this formula is a strict numeric computation with no dimensional consistency, it is best to treat it as such by manually removing units from the inputs and adding the units to the output.

[31]:
si.environment('default')
import math

# Use .prefix to force the numerical value to scale in mm
t = (0.354 * si.m).prefix('m')
d = (0.620 * si.m).prefix('m')
d_e = (0.480 * si.m).prefix('m')

t = float(t)
d = float(d)
d_e = float(d_e)

QS_i = 14 * t * math.sqrt(d_e / (1 - d_e/d)) * si.N
QS_i
[31]:
228.499 kN