Титан и его сплавы являются важными конструкционными материалами. Титановые сплавы имеют высокую удельную прочность, высокую жаропрочность и коррозионную стойкость. Они широко применяются в космической технике, авиа- и автомобилестроении, строительстве, медицине и других отраслях промышленности [1, 2]. Россия является крупным производителем титана, который производится ПАО «Корпорация ВСМПО-АВИСМА», - единственной в мире титановой компанией, осуществляющей полный цикл производства от переработки сырья до выпуска конечной продукции. В составе корпорации две промышленные площадки: «ВСМПО» в городе Верхняя Салда Свердловской области и «АВИСМА», филиал в городе Березники Пермского края, которые связаны между собой единой технологической цепочкой. От поставок титана из России зависят крупные машиностроительные компании. Так, Boeing получает от ВСМПО около 35 % титана, необходимого для производства гражданских самолетов, а самолет AirbusA380 на 60 % сделан из уральского титана [3]. Следовательно, повышение эффективности и решение проблем титанового производства в России является важной задачей общегосударственного уровня. Производство губчатого титана осуществляется химической реакцией TiCl4 + 2Mg = Ti + 2MgCl2, определяющей круговорот магния в производстве. Металлический магний в расплаве вакуумными ковшами подается из основного производственного участка № 3 (ОПУ-3) на восстановление тетрахлорида титана на ОПУ-2. В ходе восстановления образуется хлорид магния, который порционно возвращается на электролиз в ОПУ-3. При этом из-за ошибок в планировании процессов возникает необходимость слива хлорида магния в короба с последующей длительной и дорогостоящей переработкой в ОПУ-1, что приводит к удорожанию производства (рис. 1). Рис. 1. Схема процессов, протекающих между ОПУ-2 и ОПУ-3 Описание объекта моделирования Процессы восстановления и электролиза в производстве титана можно отнести к классу сложных объектов управления [4, 5]. Одним из возможных инструментов исследования таких производственных систем является имитационное моделирование систем массового обслуживания (СМО, queuing system) [6]. Многочисленные зарубежные [7, 8] и российские [9, 10] источники показывают эффективность применения имитационного моделирования при изучении и оптимизации деятельности предприятий и их подразделений. В частности, показано, что мультиагентное моделирование, являющееся развитием моделей массового обслуживания, служит эффективным средством поддержки принятия решений по управлению производством [11]. Напомним, что мультиагентная система (МАС) - это, в сущности, имитационная модель, в которой объектам («агентам») свойственна некоторая автономность, децентрализованность и ограниченность представлений [12]. Предельная автономность и ограниченность свойственны каналам и транзактам СМО. Программные агенты МАС описываются алгоритмами собственных действий и взаимодействия с другими программными агентами. Совместная имитационная модель деятельности двух участков позволила бы установить причины, приводящие к отсутствию в нужный момент ковшей в ОПУ-2, вплоть до полного исключения ОПУ-1 из технологической схемы. Для построения такой модели в 2016-17 гг. были выполнены измерения продолжительности основных процессов, представленных на рис. 1. Кроме этого, для объектов технологической схемы (ковш, вакуум-ковш, печь восстановления, электролизер) определены их особые состояния, условия перехода из одного состояния в другое и т. д. В укрупненном виде они представлены на рис. 2. В задаче совместного моделирования двух производственных участков, описанной выше, имеется кольцевое движение между участками транспортных средств, перевозящих порции химических веществ. В этой ситуации не слишком понятно, что выбрать в качестве «классического» канала, а также каким образом совместить модель СМО и модель технологического процесса, в ходе которого вырабатываются требуемые вещества, тем более, что выборка веществ из десятков реакторов осуществляется по довольно сложному алгоритму. Таким образом, в данной системе не возникает постоянной связи между реакторами и средствами транспортировки. В то же время набор реакторов нельзя рассматривать как обычную многоканальную СМО, т. к. производительность реакторов несколько отличается, и необходимо будет моделировать каждый отдельно. Рис. 2. Имитационная модель взаимодействия участков электролиза и восстановления губчатого титана Подобная модель может быть создана средствами среды AnyLogic. Однако она предназначена главным образом для моделирования систем с одинаковыми транзактами, неразличимыми по сути задачи - например, пассажиропотока в аэропорту. Кроме того, общедоступная бесплатная версия среды ограничена по времени моделирования и количеству транзактов и устройств в модели. А по условию задачи требуется очень продолжительное моделирование совместной работы участков (недели и месяцы непрерывно). Поэтому в данном конкретном случае целесообразна разработка специального средства имитационного моделирования на одном из общецелевых языков программирования. Это, собственно, является целью данной работы. Выбор языка несущественен, для этой реализации был выбран С++ (freeware реализация TDM GCC). Программная часть модели делится на универсальную основу и конкретную реализацию устройств конкретной модели. Универсальная основа упрощена по сравнению с «полноценными» средствами имитационного моделирования в том смысле, что в ней не предусмотрено непосредственное программирование связей между устройствами (стабильных потоков транзактов). Вместо этого метод, реализующий продвижение времени для класса конкретного объекта, содержит алгоритм проверки изменения состояния произвольного количества других объектов, с которыми ситуативно связан данный. Такой подход усложняет реализацию конкретных классов: выражаясь упрощенно, вместо оператора входа в 1-й канал SEIZE 1 (в GPSS) необходимо для устройства № 1 описать на общецелевом языке программирования все связи, существующие в данный момент. Зато существенно упрощается глобальный метод продвижения времени, как это описано ниже. Разработка средства моделирования Разработанное средство имитационного моделирования выполнено в виде иерархии классов и их дружественных функций. Вспомогательным классом является event - класс состояния устройства, содержащий строчный список описания состояний и их номера. Базовым классом для всех устройств является абстрактный класс obj. Он содержит общие для всех возможных устройств переменные в защищенной (protected) области, такие как строковое имя объекта, целочисленный номер текущего состояния объекта now_event, модельное время, в которое наступит смена состояния next_time, справочный номер следующего состояния объекта (если он используется) next_event, количество возможных состояний объекта len_event и др. Большинство данных класса обрабатываются конструкторами производных классов и функцией продвижения времени. Класс также содержит указатель на двухпараметрическую функцию генератора случайных чисел (ГСЧ) и его параметры для конкретного объекта. Генератор случайных чисел с равномерным статистическим распределением моделируется библиотечной функцией С++ rand (), остальные ГСЧ (нормальное, экспоненциальное распределения, распределение Пуассона и др.) реализованы методом обратной функции. Конструктор базового класса присваивает каждому объекту уникальный номер и инициализирует некоторые данные. Конструктор каждого производного класса должен: 1) завершить изменение всех необходимых этому объекту внутренних переменных базового и производного классов (например, объект «очередь» LIFO или FIFO будет, очевидно, содержать список объектов в этой очереди, который надо инициализировать); 2) добавить в список необходимое количество своих возможных состояний (их количество передается конструктору obj::obj () перед выполнением конструктора производного класса); 3) указать, какой ГСЧ использовать, и его параметры; 4) возможно, установить время первого события с этим экземпляром класса (иначе obj::obj () установит next_time в 0, и первый же вызов функции обработки продвижения времени констатирует особое состояние); 5) добавить этот экземпляр объекта производного класса в односвязный список list объектов типа obj*, реализованный очевидным образом в виде класса. Порядок объектов в этом списке определяет порядок их перебора при продвижении времени. Класс obj содержит также переменную state_change, позволяющую реализовывать обработку продвижения времени до тех пор, пока изменения состояний одних объектов будут вызывать изменения состояний других объектов. Главная функция продвижения времени main_run (), дружественная (friend) к классу obj, реализована следующим образом. Порядок перебора объектов в ней - в том порядке, в котором они были добавлены в список, а не по противотоку. Это, конечно, вызывает дополнительную погрешность моделирования. Так как в ядре этой системы моделирования вообще не предусмотрено понятие «связь между объектами», то противоток и невозможен в принципе. Поэтому цикл изменения и опроса состояний вызывается, пока хоть один объект в списке сообщает, что он изменился сейчас. Количество повторов цикла опроса всех объектов схемы ограничено переменной max_cycle. Изменился или нет, решает метод run (), обязательно переопределенный в объекте (класс obj является абстрактным, функция run () в нем чистая виртуальная и не имеет реализации). Если run () обнаруживает, что между прошлым и наступившим моментом времени произошло изменение состояния (т. е. obj::next_time < now_time), он устанавливает obj::state_changed = 1, изменяет obj::now_event и делает все, что ему необходимо с внутренними переменными, в том числе обязательно устанавливает новое значение obj::next_time так, чтобы оно было больше now_time (даже если его ГСЧ выдает меньше): нельзя несколько раз за шаг менять состояние. Иначе, если run () обнаруживает, что obj::state_changed, он просто сбрасывает его в 0. Затем, если state_changed == 0, run () проходит по всему списку объектов *list и проверяет, есть ли объекты, у которых их state_changed == 1. Метод run () должен отреагировать на изменение состояния тех объектов, которые ему известны как связанные с данным объектом. Если изменение состояния какого-то объекта вызывает изменение состояния другого объекта, то и в нем производятся необходимые изменения (точно меняется obj::now_event, возможно, меняются obj::next_time и obj::next_event; возможно, меняются внутренние параметры (типа длины очереди), а после этого obj::state_changed устанавливается в 1. Методу run () достаточно вернуть state_changed, чтобы сообщить, нужен ли еще один проход по списку объектов в главной функции. Однако чтобы все это не было слишком долго, количество проходов в главной функции ограничено целочисленной переменной max_cycle. Для проверки реализуем на созданном средстве моделирования элементарную ячейку СМО с неограниченной очередью и производительностью канала 11 ± 2 единицы модельного времени на транзакт, которая нагружена потоком транзактов, поступающих каждые 12 ± 5 единицы модельного времени (ЕМВ). Результатом моделирования в течение 1 млн ЕМВ является обработка в канале 83 356 транзактов, загрузка канала 0,916 (что близко к очевидному теоретическому значению 11/12 = 0,916 66…), максимальное количество транзактов в очереди - 5, среднее время пребывания в очереди 3,268 ЕМВ. Для моделирования в С++ понадобились классы, производные от класса obj: генератор, транзакт, очередь, канал. Терминатор, собственно, не нужен, т. к. время моделирования будет задано непосредственно в main (), а функцию удаления из памяти транзактов назначим каналу в момент окончания обработки транзакта. Схема реализована, получен результат прогона продолжительностью 1 млн ЕМВ. В результате моделирования получена максимальная длина очереди 5, загрузка канала 0,916 018. Количество транзактов от GPSS отличается на 0,9 %. Максимальная длина очереди та же самая. Загрузка канала отличается от теоретической на 0,7 % (под загрузкой подразумевается отношение времени, в течение которого канал обрабатывает транзакты, к общему времени моделирования). Возникает вопрос - какое максимальное количество циклов опроса за шаг необходимо установить? Ведь увеличение глубины опроса может существенно увеличить время моделирования. В этой элементарной системе все достаточно просто, т. к. в системе всего три устройства, и значение max_cycle > 3 избыточно. Однако проверим все же это экспериментально, изменяя глубину опроса и контролируя значения результатов моделирования (табл.). Исследование влияния значения переменной max_cycle на результаты моделирования Max_cycle Средняя глубина опроса Максимальная глубина опроса Длина очереди Загрузка канала 10 000 0,215 2 5 0,916 1 000 0,215 2 5 0,916 100 0,215 2 5 0,915 10 0,215 2 5 0,915 2 0,215 2 5 0,915 1 0,234 1 57 0,914 Очевидно, что в глубине опроса более 2 нет необходимости. Однако как только потребная глубина опроса перестает достигаться, модель выдает неадекватные результаты. В исходной задаче моделирования участков электролиза магния и восстановления губчатого титана количество устройств (донные ковши, вакуумные ковши, электрокары, электролизеры, печи восстановления, монтажный участок и т. д.) будет исчисляться сотнями, поэтому в ней необходимы будут дополнительные исследования зависимости погрешности моделирования от максимальной глубины опроса. Заключение В итоге поставленная в данной статье цель достигнута. Создано и опробовано средство, позволяющее моделировать системы массового обслуживания, в том числе те, которые сложно реализовать в стандартных имитационных средствах. Показано соответствие результатов моделирования в новой системе и в языке GPSS. Определены потенциальные проблемы моделирования сложных систем с помощью созданного ПО. Очевидно, что ПО может использоваться не только для классических СМО (queuing system), но и для активных, и для многоагентных систем, которые будут отличаться только реализацией метода run () в соответствующих классах.