One of the difficulties with Excel routines to solve differential equations, or do numerical integration, is that the equations to be solved must either be coded as VBA functions (which is time consuming), or solved using the evaluate function (which is slow and limited). An alternative is to automatically generate the VBA function, using data on the spreadsheet; a process which can easily be fully automated so that it only requires the entry of the function to be solved in a suitable text format. The code generation process is not difficult, but strangely there is little written about it. The best source of information I have found is Chip Pearson’s site, where he provides detailed instructions, and many detailed examples. In this post I will describe the essentials to get the process working, and provide a working example (with open source code). Refer to Chip’s site for more details, and lots more open source code.
A setting in the Visual Basic Editor (VBE), and an Excel option need to be changed to allow the code generation routines to work:
- In the VBE choose Tools-References and select the Microsoft Visual Basic For Applications Extensibility 5.3 check box.
- From the spreadsheet, select the “Trust access to the VBA project object model” check box. This is under the Developer Tab (macro security) in Excel 2007 and 2010, and under Tools-Macros-Security in 2003 and earlier versions.
Having done that, the code shown below should work. I have adapted four procedures from Chip’s site:
- Sub AddProcedureToModule()
- Sub DeleteProcedure(ModName As String, ProcName As String)
- Sub AddModuleToProject(ModName As String)
- Function VBComponentExists(VBCompName As String, Optional VBProj As VBIDE.VBProject = Nothing) As Boolean
The basic is procedure is:
- Prompt for a selected spreadsheet range containing the code module name, procedure name, and procedure code.
- If the code module does not exist, create it.
- If the procedure already exists, delete it
- (Re)create the code
Sub AddProcedureToModule()
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Dim CodeMod As VBIDE.CodeModule
Dim LineNum As Long, CLine As Long
Dim CodeRange As Variant, i As Long, ModName As String, ProcName As String
Dim DefaultRange As String
If Selection.Rows.Count > 1 Then
DefaultRange = Selection.Address
End If
On Error GoTo Cancelled
Set CodeRange = Application.InputBox _
(Prompt:="Code range:", Title:="Select code range", Default:=DefaultRange, Type:=8)
CodeRange = CodeRange.Value
ModName = CodeRange(1, 1)
ProcName = CodeRange(2, 1)
If VBComponentExists(ModName) = False Then
AddModuleToProject ModName
End If
Set VBProj = ActiveWorkbook.VBProject
Set VBComp = VBProj.VBComponents(ModName)
Set CodeMod = VBComp.CodeModule
With CodeMod
On Error Resume Next
If .ProcCountLines(ProcName, vbext_pk_Proc) > 0 Then
DeleteProcedure ModName, ProcName
End If
LineNum = .CountOfLines + 1
For i = 1 To UBound(CodeRange) - 2
CLine = LineNum + i
.InsertLines CLine, CodeRange(i + 2, 1)
Next i
End With
Cancelled:
End Sub
Sub AddModuleToProject(ModName As String) Dim VBProj As VBIDE.VBProject Dim VBComp As VBIDE.VBComponent Set VBProj = ActiveWorkbook.VBProject Set VBComp = VBProj.VBComponents.Add(vbext_ct_StdModule) VBComp.Name = ModName End Sub
Sub DeleteProcedure(ModName As String, ProcName As String)
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Dim CodeMod As VBIDE.CodeModule
Dim StartLine As Long, NumLines As Long
Dim CodeRange As Variant, i As Long
CodeRange = Range("coderange").Value
ModName = CodeRange(1, 1)
If VBComponentExists(ModName) = False Then
Exit Sub
End If
Set VBProj = ActiveWorkbook.VBProject
Set VBComp = VBProj.VBComponents(ModName)
Set CodeMod = VBComp.CodeModule
With CodeMod
StartLine = .ProcStartLine(ProcName, vbext_pk_Proc)
NumLines = .ProcCountLines(ProcName, vbext_pk_Proc)
.DeleteLines StartLine:=StartLine, Count:=NumLines
End With
End Sub
Public Function VBComponentExists(VBCompName As String, Optional VBProj As VBIDE.VBProject = Nothing) As Boolean
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' This returns True or False indicating whether a VBComponent named
' VBCompName exists in the VBProject referenced by VBProj. If VBProj
' is omitted, the VBProject of the ActiveWorkbook is used.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim VBP As VBIDE.VBProject
If VBProj Is Nothing Then
Set VBP = ActiveWorkbook.VBProject
Else
Set VBP = VBProj
End If
On Error Resume Next
VBComponentExists = CBool(Len(VBP.VBComponents(VBCompName).Name))
End Function
The screenshots below show this code used to create a user defined function (UDF) to solve quadratic equations (included in the download file). Click on any image for full size view.



Pingback: Daily Download 33: Miscellaneous | Newton Excel Bach, not (just) an Excel Blog