Рецепты выражений
Готовые решения под типовые задачи расчёта. Имена колонок условные, вместо них подставляются свои.
Пропуски в выражениях ниже закрываются функцией 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 trueendРезультат логический, дальше на него ссылаются как 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 0endПолучается 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 0endПолучить выручку и ценность блока
Заголовок раздела «Получить выручку и ценность блока»recovered_metal * $metal_price -- revenuerevenue - processing_cost - mining_cost -- block_valueblock_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 -- % -> г/т