**The site ahead contains harmful programs**

I have now managed to get Google to agree that there are in fact no harmful programs, but in the process Chrome will now only download from links starting with https: but almost all the links inside blog posts start with http: and will not download using Chrome. Options that still work are:

- Use Edge, Internet Explorer, or possibly other browsers other than Chrome
- Click on the Downloads link from the menu at the top of the page. That links to a spreadsheet listing all the download files which all start with https: and work from Chrome.
- Leave a comment listing the file(s) you want to download, and I will edit the link

I have also deleted all the Excel files saved as .xls, .xlsb or .xlsm and will be re-uploading as zip files, and updating the downloads spreadsheet.

]]>I last looked at using the just-in-time compiler Numba with Python some time ago (Two Timers), but I was prompted to take another look by an article in Medium that claimed Python Can Be Faster Than C++.

The article compared times for a short function finding all the prime numbers between 1 and 10,000,000, using Python, C++, and Python with Numba, and finding that the Numba function was almost 60 times faster than pure Python, and about 3 times faster than C++. Using similar code, modified to be called from Excel, I compared the pure Python code below with two variants using Numba.

```
@xl_func()
@xl_arg('num', 'int')
def is_prime(num):
if num == 2: return True
if num <= 1 or not num % 2: return False
for div in range(3,int(math.sqrt(num)+1),2):
if not num % div: return False
return True
```

This was modified to use Numba:

```
@xl_func()
@xl_arg('num', 'int')
@njit(fastmath=True, cache=True)
def is_prime2(num):
if num == 2: return True
if num <= 1 or not num % 2: return False
for div in range(3,int(math.sqrt(num)+1),2):
if not num % div: return False
return True
```

And two alternatives were used to call the is_prime2 function:

```
def Call_is_prime2(N):
for i in range(N):
is_prime2(i)
return
@njit(fastmath=True, cache=True, parallel=True)
def Call_is_prime2N(N):
for i in prange(N):
is_prime2(i)
return
```

Calling Numba at the higher level (with the line @njit(fastmath=True, cache=True, parallel=True)) allows the parallel=True option to be set.

Results from these functions were:

- Pure Python: 58.6 sec
- Numba applied to low level function: 4.2 sec
- Numba with parallel=True: 0.58 sec

so the Numba code with parallel=True was 100 x faster than the pure Python code.

The next example compares the efficiency of Numba with using Numpy “ufuncs” that automatically operate on an entire array, rather than requiring a loop to operate on each individual element. The code below generates an array of random integers, then finds the inverse of each value:

```
@xl_func
@xl_return('numpy_array')
def timeinv(its = 1000000):
its = np.int(its)
res = np.zeros((2,3))
res2 = np.zeros((its,4))
np.random.seed(0)
vals = np.random.randint(1, 100, size = its)
stime = time.perf_counter()
inv = np.zeros(its)
for i in range(its):
inv[i] = 1.0/vals[i]
res[0,0] = time.perf_counter()-stime
```

Because “vals” is a Numpy array, the loop calculating the inverse values can be replaced with a single statement:

```
stime = time.perf_counter()
npinv = 1.0/vals
res[0,1] = time.perf_counter()-stime
```

This is compared with calling a function with the Numba @njit decorater:

```
stime = time.perf_counter()
jitinv = inv2(vals, its)
res[0,2] = time.perf_counter()-stime
return res
@xl_func
@xl_arg('its', 'int')
@njit(fastmath=True, cache=True)
def inv2(vals, its):
inv = np.zeros(its)
for i in range(its):
inv[i] = 1.0/vals[i]
return inv
```

In this case using the Numpy ufunc and the Numba function gave similar results:

- On an array of 100,000 integers the Numpy function reduced the computation time from 0.16 seconds to 0.12 milliseconds, about 1300 times faster. Using the Numba function was about 1100 times faster than plain Python.
- The difference was slightly reduced with larger arrays, with an array of 1 million integers being about 800 times faster with Numpy and 750 times faster with Numba.

As a further check, I reviewed the results of my previous benchmarks with Numpy and Numba:

The table shows times for VBA and Python functions to sum 7 arrays of sequential integers, ranging from 10 to 10 million values. As a check that the functions were working correctly the Python functions were modified to return the sum of the largest array in the first row, revealing that the Numpy code was returning the wrong results! The code with the problem was:

```
@xl_func
def numpysumn(k):
a = np.arange(k)
return a.sum()
```

It seems that the Numpy arange function uses 32 bit integers, even if the range extends outside the 32 bit range! Note that the Numba code calls the same Numpy function, but apparently changes the data type to at least 64 bit. To fix the Numpy function, the data type must be specified:

```
@xl_func
def numpysumn(k):
a = np.arange(k, dtype=np.int64)
return a.sum()
```

Results with the corrected Numpy function are shown below:

The bottom two rows of the table show the performance of each function (in iterations/sec) divided by the VBA function performance in the first row, and divided by the pure Python performance in the second row. The results show considerable differences compared with the Inverse function:

- The function using the Numba loop shows more than 3 times better performance than the Numpy function, or the Numpy+Numba code with parallel set to False.
- The Numba code using the Numpy arange function with parallel=True (last column) shows more than 35,000 times better performance than the pure Python code, and the value in the first row indicates it is returning the correct value for the sum!

It can be seen that the calculation time for the last column is almost the same for all iterations, suggesting that the Numba code recognises that the Numpy arange function returns a sequence of integers with uniform spacing, and applies a simple formula to find the sum, rather than looping through the entire range. If the array returned by arange is modified in any way, such as:

```
b = np.arange(0,k)
b[0] = 0
return np.sum(b)
```

The Numba code recognises that the array may not still have uniform spacing (even though in this case the first value is unchanged), and returns to looping through the array to find the sum:

The next post in this series will look at using Numba in more realistic tasks, such as forming stiffness matrices for frame analysis programs.

]]>The changes in the new version are:

- The code input now has provision for an additional reduction factor, applied to the concrete stress, for use where the AS 3600 rectangular stress block is being used with a section where the width of the compression zone reduces towards the compression face. The code requires a 5% reduction (factor = 0.95) for circular sections, or a 10% reduction (factor = 0.9) for any other section with reducing width, such as the rotated square section shown below.
- The maximum axial load calculation for Eurocode 2 with non-rectangular stress blocks has been modified to correctly account for steel in compression.
- The routine for finding the depth of the Neutral Axis has been modified to improve performance in sections with multiple layers of reinforcement.

A note has been added to the 3DFrame sheet with the definition of Section Properties I1 and I2 and SA1 and SA2:

Section Properties

I1 and I2 are the second moments of area about Axis 1 and Axis 2 respectively.

SA1 and SA2 are shear areas for forces in the directions of Axis 1 and Axis 2 respectively.

It follows that SA1 and I2 control deflections due to forces in the plane of Axis 1, and SA2 and I1 for forces in the plane of Axis 2. This is handled correctly in the main frame analysis, but not in the calculation of intermediate deflections. Note that in the workbook comparing results with a Strand7 analysis all the results are unchanged because the beam sections are rectangular, and have the same shear area in each direction.

]]>I have now modified the Beam Design Functions spreadsheet to carry out a biaxial moment analysis with linear elastic material properties. The spreadsheet, including full open-source code, can be downloaded from:

Beam Design Functions Biax.zip

Input for a square section is shown below (the cross section is plotted with an xy graph, so x and y axes are not plotted to equal scale). The Biax function adjusts the position of the neutral axis so that the stress at one end is close to zero. The other stresses are then calculated based on the input neutral axis angle. Clicking the “Adjust NA Angle” button adjusts the angle so that the stress at both ends are near equal.

Maximum concrete compression and steel tension are plotted below for a resultant input moment angle to the X axis between 0 and 90 degrees.

Input and results below are for a rectangular section, 2000 mm wide by 1000 mm deep.

Non-rectangular sections may also be analysed. In some cases the “Adjust NA Angle” routine may fail to find a solution. Adjusting the initial input angle, so that the NA stresses are both close to zero should allow the routine to work:

Detailed output results are given on the “Elastic Out” sheet, including:

- Section properties for the concrete in compression, reinforcement, and the combined section. Note that the listed properties are calculated with the section rotated so that the neutral axis is parallel to the X axis.
- Concrete stresses at each node of the section in compression.
- Reinforcement stresses at each end of each layer.

The spreadsheet also allows for input of steel prestress forces:

The image below shows the same section input with coordinates rotated through 90 degrees, and with the same moment applied about the Y axis. The neutral axis angle is also rotated through 90 degrees, giving identical results for concrete and reinforcement stresses:

The results have also been checked against the “Elastic” single axis bending function, showing exact agreement:

A prestressed example with biaxial bending is shown below. Note that for complex shapes the Excel goal-seek function (used by the Adjust NA Angle routine) is more likely to fail to find a solution, and some initial manual adjustment of the NA angle may be required:

]]>prompted me to look at how these numbers are handled in Excel, in VBA called from Excel, and in Python called from Excel via pyxll. The results are shown in the screenshot below:

Column A shows the operation performed on each line, with results from on-sheet calculations, VBA user defined functions (UDFs), Python UDFs, and Python UDFs using Numpy float64 data type, in the following columns.

For the operations using (0.1+0.2)+0.3 and 0.1+(0.2+0.3):

- Both Excel and VBA show the two expressions as being equal to 0.6, with the difference exactly 0 and the two expressions as equal.
- Both Python and Numpy, when called from Excel, show the two expressions as exactly equal to 0.6, but a small difference between them, and the expressions as unequal.
- When the expressions are entered directly in Python, (0.1+0.2)+0.3 returns 0.600000000000001, with the other results as when called from Excel (see screen-shot below).

For the operations involving a division by zero:

- The Excel on-sheet calculation returns #DIV/0! for all three cases.
- The VBA code returns #VALUE! for all three. Note that it is possible to check for zero divisions, and return #DIV/0! with additional code.
- Python using Python float values returns a zero division error message for all three cases.
- Python using Numpy float values returns 1.7977E+308 for the first two cases and #NUM! for the third when called from Excel.
- Entering the divisions by zero directly in Python returns the same error message as when called from Excel, but when using Numpy Float64 values, it returns inf, -inf, and nan, respectively, together with a warning (see screen-shot below).

Finally, comparing or subtracting two large numbers, with more than 15 significant figures, creates an inconsistency in Excel:

- The numbers actually entered (formatted as text) are shown in column A, but when these numbers are entered as values the final 9 of the smaller one is truncated to 0, and the displayed number is 9999999999999990. Nonetheless, subtracting the two numbers returns zero, but checking for equality returns FALSE.
- VBA, Python and Numpy all return the same results with these numbers. When the numbers are passed from Excel the truncated value of the smaller number is preserved, and subtraction returns 10.0 (rather than the exact result of 1.0), and checking for equality returns FALSE. When the numbers are entered directly in VBA or Python code the smaller number is rounded up to 1.0E16, so subtraction returns 0 and the equality check returns TRUE.
- When entered in the VBA editor, the editor updates the display automatically changes the rounded up value to display as 1E16 (see screen-shots below).

In summary:

- Excel does not fully comply with the IEEE 754 format, since it displays results close to zero as zero, and it has no equivalent of inf, -inf, or nan.
- The Excel rounding or truncating of numbers is preserved when the values are passed to VBA or external code.
- Checking for equality in Excel, the results are not always consistent with the displayed values.
- When checking for equality VBA returned the same results as Excel (at least for the examples used here), even though it did not always return the same subtraction result.
- The VBA equality and subtraction results were self-consistent (for the values checked).
- When checking if two numbers are equal, either in on-sheet calculations or VBA or external code, always either used rounded values, or check that the relative absolute difference is below a specified tolerance.

For more examples, details, and discussion see: When does 35 not equalÂ 35?

]]>It is also what happened at Uluru this week:

]]>The file has now been updated, with the offending module deleted, and I’m told that Malwarebyte no longer raises a warning.

The message is, always check downloaded files, even from trusted sources, and please let me know if you get any malware or other warnings on files downloaded from here.

Updated download file: CSpline2

]]>ExcelJet has a detailed post on how to use the new function, including code for several examples. Extracts from two of these examples are shown below, together with VBA code for UDFs with the same functionality.

The screen-shots below show code for a Lambda function to evaluate the volume of a sphere. First by entering the function in a worksheet cell:

Then by entering the function in the Name Manager, and giving it a convenient name:

A slightly more complex example counts the number of words in any text string:

Results for the same two examples are shown below, using VBA UDFs:

Here is the VBA code for these two functions:

```
Const Pi As Double = 3.14159265358979
Function SphereVol(r As Double)
SphereVol = 4 / 3 * Pi * (r) ^ 3
End Function
Function CountWords(Text As String)
If Len(Trim(Text)) = 0 Then
CountWords = 0
Else
CountWords = Len(Trim(Text)) - Len(WorksheetFunction.Substitute(Text, " ", "")) + 1
End If
End Function
```

From my brief comparison, I would list the advantages of the alternatives as:

Lambda Functions:

- Can be used in on-line versions of Excel that don’t support VBA
- No knowledge of VBA required

VBA User Defined Functions:

- Useful functions can be created with minimal VBA knowledge
- A good way to learn useful VBA skills, without getting bogged down in the details of the complex Excel VBA object model
- Much easier debugging and documentation for complex functions
- Link to existing VBA libraries
- Link to compiled functions for maths intensive functions
- Save as an add-in to use across other workbooks