VBA: медленный цикл решения

У меня есть код, который упрощенно считает доменные газы за 168 часов (например, за неделю)

Он считывает некоторые входные значения и химические значения и рассчитывает молярные массы в системе. После этого решатель вычисляет, в какой химической форме (в основном CO, CO2) выходят газы. Проблема в том, что он очень медленный. Если у меня открыта эта книга Excel, это занимает несколько минут, и я даже не удосужился запустить ее до конца, когда открыто больше книг.

Я очень новичок в VBA, но я ожидаю, что он будет немного быстрее, если бы я мог настроить его для решения уравнений в VBA вместо того, чтобы значения «итерировались» туда и обратно между рабочим листом Excel и решателем VBA, путем постепенного решения значений ячеек - если бы я только знал, как, ЕСЛИ это возможно или хорошая идея.

Код в своей сущности, сначала общие расчеты:

   Sub BFgas()

Datamatrix = Range(Cells.Find("Datamatrix").Offset(1, 0).Address, Cells.Find("Datamatrix").Offset(21, 0).Address)
ReDim BFoutput(1 To 168, 1 To 3) As Double



M_pigiron_Matrix = Range(Cells.Find("BF1 Pig iron production").Offset(1, 0).Address, Cells.Find("BF1 Pig iron production").Offset(168 + 1, 0).Address)
Bf_blast_Matrix = Range(Cells.Find("BF 1 - Blast").Offset(1, 0).Address, Cells.Find("BF 1 - Blast").Offset(168 + 1, 0).Address)
Bf_oxygen_Matrix = Range(Cells.Find("BF 1 - Oxygen").Offset(1, 0).Address, Cells.Find("BF 1 - Oxygen").Offset(168 + 1, 0).Address)

M = 1
Do

M_pigiron = M_pigiron_Matrix(M, 1) 'Tons of pig iron
Bf_blast = Bf_blast_Matrix(M, 1) 'Nm3
Bf_oxygen = Bf_oxygen_Matrix(M, 1) 'Nm3

If Bf_blast = 0 Or Bf_oxygen = 0 Then
Do
M_pigiron = M_pigiron_Matrix(M, 1) 'Tons of pig iron
Bf_blast = Bf_blast_Matrix(M, 1) 'Nm3
Bf_oxygen = Bf_oxygen_Matrix(M, 1) 'Nm3
M = M + 1
Loop While Bf_oxygen = 0 Or Bf_blast = 0
End If



n_N2_blast = Bf_blast * Datamatrix(19, 1) / Datamatrix(17, 1) 'kmol
n_O2_blast = Bf_blast * Datamatrix(18, 1) / Datamatrix(17, 1) 'kmol
n_O2_oxygenintake = Bf_oxygen / Datamatrix(17, 1) 'kmol
n_total_O_in = (n_O2_blast + n_O2_oxygenintake) * 2 'kmol




'Calculates the amounts of coke, briquettes and scrap

Cokeratio = Cells.Find("Input data").Offset(1, 1).Value2
Briqratio = Cells.Find("Input data").Offset(2, 1).Value2
Scrapratio = Cells.Find("Input data").Offset(3, 1).Value2
m_oil = Cells.Find("Input data").Offset(4, 1).Value2


m_coke = Cokeratio * M_pigiron * 1000 'kg
m_briq = Briqratio * M_pigiron 'kg
m_scrap = Scrapratio * M_pigiron 'kg


'Fe/Iron calculations
'Calculates the molar masses of iron and coal in pig iron, briqettes and scrap

n_Fe_pigiron = Datamatrix(3, 1) * M_pigiron * 1000 / Datamatrix(15, 1) 'kmol

n_Fe_briq = Datamatrix(12, 1) * m_briq / Datamatrix(15, 1) 'kmol

n_Fe_scrap = Datamatrix(13, 1) * m_scrap / Datamatrix(15, 1) 'kmol

'Calculates how many kmol is needed from pellets

n_Fe_pellets = n_Fe_pigiron - n_Fe_briq - n_Fe_scrap
m_pellets = n_Fe_pellets / Datamatrix(11, 1) * Datamatrix(15, 1) 'Divides by the iron content 0.72, to get the total mass



'O/Oxygen calculations
'Calculates the total incoming oxygen
'(m_pel*x_pellets,O + m_briq*x_O,briq)/M_O + n_blast,O2*2 + n_Oxygen,O2*2

Oxygen_in = m_pellets * Datamatrix(10, 1) / Datamatrix(16, 1) + m_briq * Datamatrix(9, 1) / Datamatrix(16, 1) + n_total_O_in 'kmol
Cells.Find("Solutions").Offset(0, 1).Value = Oxygen_in

'C/Coal calculations
'Calculates the incoming coal minus what comes out with the pig iron, leaving what comes out with the bf-gases
'm_coke,*x_C,coke + m_oil*x_C,oil + m_br*x_br,C = m_rj*x_C,rj + V_tg*(y,co + y,co2)

Coal_for_bf_gas = (m_coke * Datamatrix(4, 1) / Datamatrix(14, 1) + m_oil * Datamatrix(5, 1) / Datamatrix(14, 1) + m_briq * Datamatrix(6, 1) / Datamatrix(14, 1)) - M_pigiron * 1000 * Datamatrix(1, 1) / Datamatrix(14, 1)
Cells.Find("Solutions").Offset(0, 2).Value = Coal_for_bf_gas

'N/Nitrogen
'Nitrogen is mainly what comes in with the blast

N2_for_bf_gas = n_N2_blast
Cells.Find("Solutions").Offset(0, 3).Value = N2_for_bf_gas



'Sets in the hydrogen just in case
'H/hydrogen
'H_for_bf_gas = m_coke * Datamatrix(21, 1) / Datamatrix(20, 1) + m_oil * Datamatrix(7, 1) / Datamatrix(20, 1)

Решающая часть:

SolverReset 'Code solves the problem for a specific set of lines, in this case meaning hours
SolverOptions Precision:=1, Iterations:=100, AssumeNonNeg:=True
SolverOk setCell:=Cells.Find("Differences").Offset(1, 0).Address, MaxMinVal:=3, ValueOf:="0", ByChange:=Range(Cells.Find("Testing here").Offset(0, 1).Address, Cells.Find("Testing here").Offset(0, 3).Address)

SolverAdd cellRef:=Range(Cells.Find("Testing here").Offset(0, 2).Address, Cells.Find("Testing here").Offset(0, 3).Address), _
relation:=3, _
formulaText:=0.1

SolverAdd cellRef:=Range(Cells.Find("Testing here").Offset(0, 2).Address, Cells.Find("Testing here").Offset(0, 3).Address), _
relation:=1, _
formulaText:=0.4

SolverAdd cellRef:=Cells.Find("Testing here").Offset(0, 1).Address, _
relation:=3, _
formulaText:=(Bf_blast + Bf_oxygen) * 1.2

SolverAdd cellRef:=Cells.Find("Testing here").Offset(0, 1).Address, _
relation:=1, _
formulaText:=(Bf_blast + Bf_oxygen) * 2

SolverSolve userFinish:=True

BFoutput(M, 1) = Cells.Find("Testing here").Offset(0, 1).Value
BFoutput(M, 2) = Cells.Find("Testing here").Offset(0, 2).Value
BFoutput(M, 3) = Cells.Find("Testing here").Offset(0, 3).Value



M = M + 1
Loop While M < 169

Cells.Find("BF1 - Output data").Offset(2, 0).Resize(UBound(BFoutput, 1), 3).Value = BFoutput

person user2703642    schedule 03.10.2013    source источник


Ответы (1)


Я не инженер-химик, поэтому я не знаю уравнений, которые вы пытаетесь решить.

Я предполагаю, что они нелинейны, преходящи и итеративны. 168 * 3 = 504 степени свободы не кажутся мне такими уж большими, но это может быть много работы, если у вас есть много маленьких временных шагов с итерациями для каждого.

Я не могу сказать, решаете ли вы проблему с переходным или устойчивым состоянием из кода, который вы опубликовали.

Численные задачи, с которыми я больше знаком (механика твердого тела и теплопередача), очень чувствительны к алгоритму. Уравнения могут быть подвержены ограничениям шага по времени из соображений устойчивости, в зависимости от выбранной схемы интегрирования.

Если вы решаете нелинейную задачу устойчивого состояния, применимы те же комментарии, за исключением размера шага итерации вместо размера шага по времени.

Я не могу много узнать об этом из вашего кода VB, но я дам следующие рекомендации:

  1. Напишите свои уравнения, выполните преобразование Фурье и посмотрите, есть ли какие-либо ограничения устойчивости.
  2. Подумайте о наборе инструментов, таком как Matlab. У них есть готовые реализации, которые могут быть более оптимизированы, чем ваш пользовательский код.
  3. Мне ничего не известно о каких-либо возможностях профилирования для VB или Excel, но вы не можете решить проблему без данных. Я бы посмотрел, смогу ли я получить некоторую информацию о том, на что тратится время, прежде чем выдвигать гипотезу о решении.
person duffymo    schedule 03.10.2013
comment
Спасибо за предложения! Я посмотрю, что я могу сделать, в течение ближайшей недели. Это не полная катастрофа, если это так медленно, но это может немного больше раздражать других пользователей, чем меня. - person user2703642; 03.10.2013
comment
Неделя - большой срок для любого анализа. Однажды я провел один анализ методом конечных элементов, который длился месяц. Но это была большая проблема формования листового металла из листового металла. Я не верю, что ваша проблема приближается к этому по размеру или сложности. - person duffymo; 03.10.2013
comment
Это то, о чем я задавался вопросом - это довольно простая проблема, которая не требует много времени для настройки в Excel, если вы просто хотите решить ее за один час. В этой упрощенной версии у него 3 неизвестных: CO, CO2 и объем газа, поэтому я был очень удивлен, когда это заняло так много времени, даже когда я попытался немного ограничить интервал. Но я посмотрю на предложения, которые я получил. - person user2703642; 03.10.2013