VBA Solver Loop Keeps Only Last Loop Results - vba

Sorry, this is another VBA Solver looping problem. I've read many of the other questions/answers posted here and elsewhere, but being new to VBA (this is the first thing I am attempting), I'm unable to pinpoint my error.
I wish to set cell Ii to 0 while changing cells Ji and Ki (keeping results), where i are rows 3 to 21.
My current code does not come up with any errors, but the results only keep on the last row of the loop- please advise! I've tried using range() and range.offset (from other examples) instead of cells(), and also setting the active worksheet to no avail.
I am using Excel 2011 for Mac.
Sub SolveTwo()
'Not sure if this is necessary
Dim row As Integer
'Begin loop
For row = 3 To 21
'Test code shows it is stepping through loop
Cells(row, "U").Value = row
'Grab starting values from other columns
Cells(row, "J").Value = Cells(row, "S").Value
Cells(row, "K").Value = Cells(row, "T").Value
'Solver Code
SolverReset
SolverOptions Precision:=1e-05
SolverOk SetCell:=Cells(row, "I").Address, _
MaxMinVal:=3, ValueOf:=0, _
ByChange:=Cells(row, "J").Address & "," & Cells(row, "K").Address, _
Engine:=1, EngineDesc:="GRG Nonlinear"
SolverSolve UserFinish:=True
SolverFinish KeepFinal:=1
'Not sure if below is necessary
'SolverSave SaveArea:=Cells(row, "J").Address & "," & Cells(row,"K").Address
Next row
End Sub

Had the same problem, i.e. only retaining the solutions to the last solver call.
It's caused by Excel for Mac's solver operating asynchronously, and the solver macro only starts once the calling code has completed. Hence the solver parameters are reset repeatedly by the calling code, but the solver doesn't run until the last iteration.
There is no solution forthcoming currently but here are two workarounds. The first one is to have two modules: a regular module calling solver once, and a second class module which fires whenever the sheet calculates (solver kicks off a re-calc when finishing), and calls the second one. Iterate back and forth in a loop. See here for great solution by J Peltier which I've admittedly not tried: solution 1
Solution 2 which I used is to call solver from an Apple Script. Here's an example. The control flow in the macro uses worksheet cells for the loop counters etc, and my macro was called by shift-opt-cmd-O. My solver usually finished in 10 sec, so I waited 15.
on run {input, parameters}
-- Click “Microsoft Excel” in the Dock.
set timeoutSeconds to 2.0
set uiScript to "click UI Element \"Microsoft Excel\" of list 1 of application process \"Dock\""
my doWithTimeout(uiScript, timeoutSeconds)
-- Press ⇧⌥⌘O
repeat 496 times
set timeoutSeconds to 2.0
set uiScript to "keystroke \"Ø\" using {shift down, option down, command down}"
my doWithTimeout(uiScript, timeoutSeconds)
delay 15
say "done"
end repeat
return input
end run
Hope that helps!

Try something like this:
Sub SolveTwo()
Dim myRow As Integer
For myRow = 3 To 21
With Worksheets(2)
.Cells(myRow, "U") = myRow
.Cells(myRow, "J") = Worksheets(1).Cells(myRow, "S")
.Cells(myRow, "K") = Worksheets(1).Cells(myRow, "T")
End With
'add your solver code here.
Next myRow
End Sub
It will generate some results in Worksheets(2), if the sheet you are executing is the first one. Furthermore, do not use Row as a variable name, because it is used in the VBEditor.

Related

Microsoft Excel: Macro to repeat a specific action multiple times

My task is to use Excel to manipulate a large set of data and I had heard of using a Macro but I'm not very code-savvy. I recorded the steps that are required using the macro function, but I need to add more lines of code to utilize looping and making sure it advances by 2 after every repeat.
I've posted my steps below:
Range("A5:C5").Select
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Range("A5").Select
ActiveCell.FormulaR1C1 = "=R[-1]C+0.1"
Range("B7:C7").Select
Selection.Copy
Range("B5").Select
ActiveSheet.Paste
Application.CutCopyMode = False
Basically, select 3 cells (A5:C5) insert cells and shift cells down. Use a formula in the newly empty A5 to add 0.1 to A4 and copy values from B7:C7 and paste into B5:C5.
The following image shows a before and after of what I'm talking about to make things more clear.
Before
After
The next step would be:
Range("A7:C7").Select
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Range("A7").Select
ActiveCell.FormulaR1C1 = "=R[-1]C+0.1"
Range("B9:C9").Select
Selection.Copy
Range("B7").Select
ActiveSheet.Paste
Application.CutCopyMode = False
and so on.
Any help with this would be greatly appreciated. I also apologize if what I'm asking is still confusing or not clear in any way.
[Prologue:]
Hi, I'll provide you with an answer and I tried to comment the heck out of it to make it as beginner friendly as possible, but the truth of the matter is:
I can explain to you how it's done, but you will never properly understand why it's done until you properly understand basic programming methodologies such as looping and that is something only you and you alone have to sit down to and fully comprehend
[The gaps in logic:]
Probably the biggest issue is, you have not specified what happens
when your data reaches empty cells (what I mean under that) - if in
your loop you were on row 10 (7, M, N) you would have no longer any
letters to add, as the next 2 rows (12) no longer contain eny data.
Given this, I modified the loop to start at the n-2th row instead to prevent this from happening (that means in your example it will end (or start to be more precise) at 6.1 as it's the last row that can retrieve the data)
In general, I'd recommend posting not only a picture of input data, but rather than picture of current result in this case a properly explained expected result would be much more to our benefit and understanding and would have saved me a lot of the guesswork here.
[The theoretical part of your question:]
I'll save you a bit of googling time here with few useful facts.
If you're looking to repeat a specific action, you should always be looking to utilize one of the 2 (or 3 depending on how you classify them) loops for and do (while/until)
Better yet, if you're looking to loop a variant amount of actions for repeated amount of times, you should utlize either a procedure Sub or a function Function so you can use arguments that act as a variable for the loop.
Generally when adding or removing rows it's important to loop from Bottom to Top (from Last to First). Why? Because if you add an extra row, it's going to mess up your Row order.
With all that in mind, the procedure itself could look something like this:
[The solution itself:]
You can use the following procedure every time for a specified range.
Option Explicit 'prevents typo. errors, undeclared variables and so on
Private Sub extra_row(ByVal rng As Range) 'declaration of procedure
Dim i As Long
' we loop for the n-th - 2 row (last row - 2) to the pre-first (added) row.
For i = (rng.Rows.Count + rng.Row - 2) To rng.Row + 1 Step -1
'why the -2? ^ Because if you add the letters from next 2 rows,_
the last 2 would have no to grab _
eg. Row 10 and 11 in your original data would have no data under them to grab
' a bit harder section to comprehend, if it's the first data entry _
we need to account for the fact that we have not added any extra rows yet_
hence we also need to decrement the row from which we receive the data by 1 _
it 's bit of difficult to word, i'd highly recommend debugging yourself _
and seeing what happens with or without it
Dim fp As Integer
If (i - 2 = rng.Rows.Count) Then
fp = 1
Else
fp = 0
End If
' now we just add the extra rows where we can
Rows(i).Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Cells(i, 1) = Cells(i, 1).Offset(-1, 0) + 0.1 'we add 0.1 to cell one above
Cells(i, 2) = Cells(i + 3 - fp, 2) ' similar case, with letters, but two below
Cells(i, 3) = Cells(i + 3 - fp, 3) ' similar case, with letters, but two below
Next i 'and we loop for every cell in our specified range
End Sub
Eg. in your case, you could run the procedure with the following command:
Call extra_row(Range("A4:A11"))
[Practical use]
While the solution itself should work, in a real world example it probably might be smarter not to use a specific range to call for each procedure. Especially if that means use has to look at the worksheet, check the range and count the rows manually.
That's one of the reasons we created a procedure here to begin with. So I created one more procedure akin to main() in most programming languages which detects the last active row and applies the procedure to your data range by detecting it automatically.
So in the end, your could should look something like this:
Option Explicit
Private Sub extra_row(ByVal rng as Range)
'... code from the answer here
End Sub
Private Sub rundata()
Dim lr As Long
lr = Sheets("Your Sheet Name").Cells(Rows.Count, 1).End(xlUp).Row
'detects the last active (nonempty) row _
rememeber to change the Sheets("") to wherever your data is stored
Dim mydata As Range
Set mydata = Range("A4:A" & lr) 'under presumption your data always begins with A4
Call extra_row(mydata)
End Sub
Now, whenever you would run (manally) or Call the run_data() procedure it would automatically detect the range and apply the procedure we defined to it.
[Expected result visualization and closing words:]
We started with this:
After running the procedure:
Now I know, it may seem like there's a lot of novel concepts here, but truth of the matter is, all of them are fairly easy once you buckle down and try to comprehend the code line by line. Most of it is simple mathematical operations.
If you still have trouble comprehending anything here, do your own research first and then post comment here or better yet, create a new question (if it warrants it).
Good luck on your coding journey! :)
Rawrplus
This code should do the trick.
The code gives you an InputBox in which you can type in the number of times to run the code.
Sub test()
Application.ScreenUpdating = False
Dim Grab As Range
Dim RunTimes As Long
On Error GoTo Get_Out
RunTimes = InputBox("How many times shall the code run?", "Run times")
On Error GoTo 0
For x = 1 To RunTimes * 1.5 + 3 Step 2
Set Grab = ActiveSheet.Range("A" & x + 4)
Grab.EntireRow.Insert
Grab.Offset(-1, 0).Value = Grab.Offset(-2, 0).Value + 0.1
Grab.Offset(-1, 1).Value = Grab.Offset(1, 1).Value
Grab.Offset(-1, 2).Value = Grab.Offset(1, 2).Value
Next x
MsgBox "Succes"
Get_Out:
Application.ScreenUpdating = True
End Sub
Let me know if you have any questions about the code or if you want me to explain it further :)

Advise Solver Loop VBA

I am new to VBA and have a question.
in Excell, I estimated 3 values by using solver. The objective was set to an exact value, including there were 6 simple constraints.
Now I want to re-estimate these 3 values by only incrementally increasing the objective value (thus constraints stay the same).
I am thinking a using a loop in VBA but I have no idea where to start since I am no Pro in VBA.
can anyone help?
Ciao!
you can make a loop to adapt the target value for the solver via vba. In the vba editor you need to add reference to the solver (menu tools-> references-> check solver).
in the loop i is used to set the target, once the solution is found, the corresponding range B9:D9 is copied as from column F.
Dim i
For i = 1 To 40
SolverOk SetCell:="B21", MaxMinVal:=3, ValueOf:=i, ByChange:="B9:D9", Engine:= _
1, EngineDesc:="GRG Nonlinear"
SolverSolve True
Range("B9:D9").Copy Cells(i, "F")
Next i

Excel Solver slow due to second workbook

I've noticed something new today that will cause me problems in the future regarding Excel's Solver, so I want to get a jump on it.
I'm using VBA to sequentially apply solver moving down a sheet (let's say this sheet belongs to 'workbook 1'). This all works fine and I'm happy with the results of Solver. Yay... My concern is this: When I have a second rather large workbook open (let's call this 'workbook 2'), WHICH HAS ABOSOLUTELY NO LINKS TO MY CURRENT WORKBOOK, Solver takes around 20 times as long to run.
Naturally the most reasonable thing to do is not have 'workbook 2' open when Solver is running. For now that's my solution, however, in the very near future I will need to have both workbooks open simultaneously. So I pose the following question:
Why would solver take longer to run with two books open even though it's not even touching one of them?
In case skeptics are concerned that my code is doing something unusual here it is:
Sub ExampleSeqSolver()
Dim Iter As Long
Dim Time0 As Double
Dim Duration As Double
Application.ScreenUpdating = False
' Solver requires that it is working on the 'active sheet', silly but w/e
Sheets("Sheet1").Activate
' watch optimization efficiency
Time0 = Timer
For Iter = 2 To 13
' my data are sometimes related to adjacent rows so I was considering supplying an initial solution to reduce the number of runs
' Sheets("Sheet1").Range("$AC$" & ITER & ":$AI$" & ITER) = _
' Sheets("Sheet1").Range("$AC$" & ITER - 1 & ":$AI$" & ITER - 1)
If Cells(Iter, 1) <> 0 Then
SolverReset
SolverOptions AssumeNonNeg:=True, Iterations:=100
SolverAdd CellRef:="$AK" & Iter, Relation:=2, FormulaText:="1"
SolverOK SetCell:="$AW$" & Iter, MaxMinVal:="2", ByChange:=Sheets("Sheet1").Range("$AC$" & Iter & ":$AI$" & Iter), Engine:=1
SolverSolve True
End If
Next Iter
' report optimization duration
Duration = Round(Timer - Time0, 2)
MsgBox "Optimization finished in " & Duration & " seconds", vbInformation
Application.ScreenUpdating = True
End Sub
The presence of 'workbook 2' is correlated but not causal. In my case there is one particular workbook that causes the solver slowdown; most others don't. The workbook that causes the slowdown is about 4mb. Much larger workbooks do not cause a slowdown. The one causing the slowdown does not to the best of my knowledge contain either macros or VB. I recommend that this problem be shown to Frontline systems who developed the original solver.

MS Excel 2013 - crash after editing cell value

I am currently facing a very anoying issue with my Excel workbook, and I would like to ask you for help.
My workbook works as a quotation tool and generates a PDF afterwards.
First, you need to enter data from different sources (for e.g. PDFs, Excel Workbooks, etc.) to a Worksheet called Master. You can do some calculation with simple formulas and can export the results into another sheet called Calc_Overview. In this process, the data is also formatted to the right font, color and size.
With ActiveWorkbook.Worksheets("Master")
For i = 12 To .UsedRange.rows.Count
If .Cells(i, 2) <> "" Then
.Cells(i, 2).Copy Destination:=Target.Cells(k, 4)
.Range(.Cells(i, 4), .Cells(i, 5)).Copy _
Target.Range("F" + k, "G" + k).PasteSpecial xlPasteValues
.Range(.Cells(i, 6), .Cells(i, 7)).Copy _
Destination:=Target.Range("I" + k, "J" + k)
.Cells(i, 8).Copy _
Destination:=Target.Cells(k, 8)
k = k + 1
End If
Next i
End With
After this operation, the user can see a summary of his entered and calculated data. With another macro, which copies the rows into 3 different worksheets (depending on the kind), the calculation process is finished.
This is my copy function :
Function CopyTable(Typ As String, Counter As Integer, Count As Integer) As Integer
With Worksheets("Calc_Overview")
.Range("A" & Count & ":" & "D" & Count).Copy _
Destination:=Sheets(Typ).Range("A" & 5 + Counter)
.Range("G" & Count & ":" & "H" & Count).Copy _
Destination:=Worksheets(Typ).Range("E" & 5 + Counter)
End With
CopyTable = Counter + 1
End Function
Now my problem:
If you try to edit the data which was transfered into the final worksheet, it often happens that Excel crashes without any reason. After editing a cell value,pressing enter or clicking another cell will let Excel crash.
I already turned off the 'Automatically Flash Fill' and 'Show Quick Analysis' option, but the problem still occurs..
Has anyone a clue what might cause this problem? I already replaced the final worksheets by new ones without copying anything from the 'broken' ones - also without any success. I am using a Windows 10 PC (x64) with an 32-Bit Microsoft Office 2013.
I am really looking fowards to any tips or solutions regarding this problem!
Many thanks
Moritz
I have an update for you:
changing the copy method does not change anything - the problem still occurs.
This is how is looks after you edit the cell - the cell expands and excel is freezing without any reason with a very high cpu load

VBA in Excel is skipping 2nd Do Until Loop Entirely

I have been working on some code which contains several Do Until Loops but when I run it the first loop works as expected but the second and other loops are completely bypassed.
I have run the individual loops in independent sub routines and they worked as needed.
I have also checked previous threads and can't seem to find an example of the same problem.
I initially tried re-declaring i = 2 for the second loop thinking i still valued > than lastRow from the first loop.
On top of this I have also tried changing the variable from i to j for the second loop but this also made no difference.
Here an example of the code I am using:
Dim i As Long
i = 2
Do Until i > lastRow
Cells(i, 16).Select
If ActiveCell.Offset(rowOffset:=0, columnOffset:=-13) <> "Invoice" Then
ActiveCell = ""
ElseIf ActiveCell.Offset(rowOffset:=0, columnOffset:=-12) <> "" Then
ActiveCell = ""
ElseIf ActiveCell.Offset(rowOffset:=0, columnOffset:=-9) <> "Usage Actual" Then
ActiveCell = ""
ElseIf ActiveCell.Offset(rowOffset:=0, columnOffset:=-3) = "Final" Then
ActiveCell = ""
Else: ActiveCell. _
FormulaR1C1 = ' vlookup formula to data source
End If
i = i + 1
Loop
' Additional code to paste out formula and save work book
Dim j As Long
j = 2
Do Until j > lastRow
' from here the process skips right past the next loop to the Additional code to paste out formula and save work book noted below
Cells(j, 17).Select
If ActiveCell.Offset(rowOffset:=0, columnOffset:=-1) = "Yes" Then
ActiveCell.FormulaR1C1 = ' vlookup formula to data source
Else: ActiveCell = ""
End If
j = j + 1
Loop
' Additional code to paste out formula and save work book
I have been working on this for several days and have run out of ideas.
I have finally managed to get the code to work.
The method I ended up using involved 2 changes.
Firstly I gave each loop it's own Do Until variable - where the code posted above started with i as the first variable, the next became j, then k and so on.
This on it's own did not initially resolve the issue until I added additional processes between each loop.
For example, following the first loop I added code to perform a lookup in another column and to copy that lookup down to lastRow but without using a loop to do it. I followed this with the next loop and followed that with further additional code, also not requiring a loop to complete.
Fortunately I had enough additional processes to break up all of the loops in the project. Although I still believe running multiple loops one after another shouldn't be a problem, I have yet to find a more effective solution.