Перейти к содержимому

Рецепты выражений

Готовые решения под типовые задачи расчёта. Имена колонок условные, вместо них подставляются свои.

Пропуски в выражениях ниже закрываются функцией coalesce по месту. Почему пустое значение опаснее нуля и как оно ведёт себя в арифметике — Работа с NULL.

Синтаксис самих выражений разобран в статье Основы выражений, полный перечень функций и операторов — в справочнике. Подстановки вида $имя описаны в Константах.

Выражения опираются на колонки самой модели. Для расчёта ценности обычно нужны:

  • Содержание полезного компонента — grade
  • Объёмный вес — density, для перевода объёма в тоннаж
  • Извлечение — recovery, долей единицы. Бывает и константой, если одинаково для всей рудной массы
  • Тип породы или сорт руды — sort числовым кодом, rock текстовым, zone кодом зоны. По нему руда отделяется от породы и задаются разные ставки добычи

Геометрия блока — центр xc, yc, zc и размеры xinc, yinc, zinc.

xinc * yinc * zinc -- volume, м³
volume * density -- tonnage, т

Объёмный вес заполнен не везде, и вместо пропуска подставляется константа:

volume * coalesce(density, $density_default)

Одной константы мало, когда плотность заметно разная по типам пород:

case rock
when 'oxide' then coalesce(density, $density_oxide)
when 'sulphide' then coalesce(density, $density_sulphide)
else coalesce(density, $density_default)
end

Из числового кода типа перебором веток case when:

case
when sort = 0 then false
else true
end

Результат логический, дальше на него ссылаются как is_ore. Когда кодов породы несколько, короче перечислить их списком через not in, чем расписывать case:

sort not in (0, 7, 9) -- числовые коды
rock not in ('waste', 'overburden', 'air') -- текстовые коды

Разделение по содержанию вместо кода типа:

coalesce(grade, 0) >= $cutoff_grade

Бортовое содержание держат константой, тогда сценарий меняется без правки формулы.

Несколько уровней содержания плюс проверка на породу. Ветки case when проверяются сверху вниз, поэтому порядок от высоких сортов к низким:

case
when sort = 0 then 'waste'
when grade is null then 'untested'
when grade >= 2.0 then 'high_grade'
when grade >= 0.8 then 'medium_grade'
when grade >= $cutoff_grade then 'low_grade'
else 'subgrade'
end

Эта же колонка потом выбирается в отчёте как типы руды, и запасы раскладываются по сортам отдельными колонками — Отчёт по оболочкам.

Часть величин считается только для руды. Масса металла есть лишь у рудных блоков, а у породы её обнуляют, даже если ценный компонент в них есть.

case
when is_ore then grade * tonnage
else 0
end

Получается metal_mass, масса металла до извлечения. Извлекаемая часть:

recovery * metal_mass -- recovered_metal

Единицы содержания и цены должны быть согласованы, при grade в г/т цена задаётся за грамм.

grade_au + grade_cu * $cu_to_au_factor

Коэффициент пересчёта зависит от цен и извлечения по каждому металлу, поэтому его удобно держать константой и пересматривать вместе с ценой.

Базовая ставка зависит от того, руда это или порода, а к ней добавляется доплата за каждую ступень углубления ниже отметки $z_incr_start:

tonnage * (
steps_below(zc, $z_incr_start, $z_incr_step) * $mining_incr
+ case
when is_ore then $mining_ore
else $mining_waste
end
)

Функция steps_below считает, на сколько ступеней высотой $z_incr_step блок ушёл ниже границы — описание.

Порода не перерабатывается, её затраты равны нулю:

case
when is_ore then $processing_ore * tonnage
else 0
end
recovered_metal * $metal_price -- revenue
revenue - processing_cost - mining_cost -- block_value

block_value и выбирается при запуске как колонка ценности. Размерности стоит свести до расчёта.

Координата zc — центр блока, границы считаются через его высоту:

zc + zinc / 2 -- кровля
zc - zinc / 2 -- подошва

Отметить блоки внутри прямоугольной области

Заголовок раздела «Отметить блоки внутри прямоугольной области»

Лицензионный участок, зона детального бурения, площадка промобъекта. Диапазон по каждой оси задаёт between:

xc between $zone_x_min and $zone_x_max
and yc between $zone_y_min and $zone_y_max
and zc between $zone_z_min and $zone_z_max

Результат логический, поэтому та же колонка годится и как признак зоны в отчёте, и как колонка ограничений при запуске.

Колонка ограничений выбирается при запуске. Истинное значение запрещает к выемке сам блок и все блоки ниже, в конус которых он попадает — Запуск.

rock = 'protected'

Запрет по площади задаётся координатами, например охранная зона реки или отвала:

xc between $keep_x_min and $keep_x_max
and yc between $keep_y_min and $keep_y_max

Действующие выработки и промплощадку закрывают тем же приёмом, добавив условие по отметке.

Сократить расчёт до перспективной области

Заголовок раздела «Сократить расчёт до перспективной области»

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

Запас по краям нужен обязательно. Борт карьера должен помещаться в границы урезанной модели, иначе контур упрётся в край — Запуск.

case when tonnage > 0 then revenue / tonnage else null end

Ветка else возвращает NULL, то есть «значение неизвестно». Ноль тут поставил бы неверный смысл, будто удельная выручка посчитана и равна нулю — Работа с NULL.

Ветку можно и опустить, результат тот же. Когда ни одно условие не сработало, а else не написан, case возвращает NULL:

case when tonnage > 0 then revenue / tonnage end

Строки из разных источников приходят в разном регистре и с пробелами, поэтому перед сравнением их выравнивают строковыми функциями. trim срезает пробелы по краям, lower переводит в нижний регистр, так что ' Oxide ' и 'OXIDE' дают одно и то же 'oxide':

case lower(trim(rock))
when 'ore' then 'ore'
when 'oxide' then 'ore'
when 'sulph' then 'ore'
when 'waste' then 'waste'
when 'oxw' then 'waste'
else 'unknown'
end
grade / 1000 -- г/т -> кг/т
grade_pct * 10000 -- % -> г/т