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

Но по мере роста вашего опыта и списка задач возникает дефицит времени. В какой-то момент начинает казаться, что с офиса можно не уходить. Тогда стать продуктивнее нам поможет автоматизация. Под катом будет небольшой опыт проектировщика антенной техники.

Введение


В прошлом и первом своем посте на Хабре я рассказывал тебе дорогой читатель, как управлять САПР CST MWS с помощью кода Matlab. Пускай отклика в комментариях эта статья не получила, но зато есть отзывы моих коллег и друзей. И самый главный вопрос: «А ты покажешь конкретные варианты применения?» Отмазаться, что варианты применения ограничены лишь вашей фантазией и функционалом пакета не получилось. По этому по просьбе трудящихся сегодня мы рассмотрим пример управления CST MWS из вне.

В данном случае я хотел заставить машину выполнять задачи пока меня нет, либо я занят чем-то более важным и полезным на текущий момент времени. Поэтому и не только было решено копаться в любимых пакетах по электродинамическому моделированию (CST Microwave Studio и Ansys HFSS) на предмет управления из вне. В идеале с помощью инструмента, который мне хорошо знаком, коим является Mathworks Matlab.

В первую очередь хочу отметить, что статья не претендует на супер уникальность и в каких- то частях будет состоять из перевода справки по CST, да и вещи, освещенные ниже, кому-то покажутся слишком очевидными.

Работа с параметрами


Я обожаю параметры. По неопытности раньше я разрабатывал непараметрические модели, но теперь я параметризую почти всё. Почему я это делаю? А мне так нравится. Таким образом я могу перестроить любую геометрию за секунды безошибочно, если я сделал хорошую модель. Нет проблем с геометрией = нет проблем с расчетной сеткой = нет проблем с оптимизацией, поэтому всегда можно быстро перенести проект на другие частоты, в другие корпуса и многое другое.

Очень часто возникает ситуация, когда необходимо провести серию расчетов с изменением параметра и анализом поведения структуры при его изменении. Встроенная утилита Parameter Sweep предлагает предлагает нам изменять параметры по линейному или логарифмическому закону, выбирая числ�� точек или шаг между ними. Также есть возможность ввести конкретные значения параметра. А если закон изменения параметра хочется видеть другой?

Параметры в CST

Стратегия


В общем случае наша задача достаточно проста, сводится она к расчету значений параметра и передаче CST для расчета. Что бы было не так скучно, прикрутим вывод каких-нибудь графиков. Ниже представлена простенькая блок-схема того, что будем делать.

Рисунок 1 - Блок схема скрипта

Единственное, что хочу отметить, выводом результата я назвал его экспорт в текстовом формате, а этап представления результатов моделирования в Matlab запихнул в блок «Постобработка».

Начнем


Для примера будем использовать мой 5-минутный проект прямоугольной патч-антенны.

Патч-антенна

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

Пусть параметр L изменяется по квадратичному закону закону в пределах от 10 до 100, количество значений 5:

x = sqrt(10):(10-sqrt(10))/5:10;
y = x.^2;

На всякий случай приведу график, хоть все понимают как он должен выглядеть.

Парабола

Запуск��ем CST и открываем наш проект, а также привяжем к переменным все необходимые нам объекты CST:

cst = actxserver('CSTStudio.Application');
mws = cst.invoke('NewMWS');
mws.invoke('OpenFile','path\to\file.cst');
solv = mws.invoke('Solver');
export = mws.invoke('ASCIIExport');
plot1d = mws.invoke('Plot1D');

Кратко о том, что эти 6 строк представляют в порядке их написания:

  1. Привязка объекта приложения CST Studio к переменной cst
  2. Привязка окна CST MWS к переменной mws
  3. Открытие в окне CST MWS файла
  4. Привязка объекта вычислителя к переменной solv
  5. Привязка объекта Экспорта в ASCII к переменной export
  6. Привязка объекта одномерных графиков к переменной plot1d

Далее введем промежуточную переменную, которая пригодится для формирования путей к файлам результатов:

f_cell = cell(length(y),1);

Чего мы можем добиться от CST о параметрах используемых в проекте?
  • Можем удалить параметр командой
    mws.invoke('DeleteParameter','ParameterName');
  • Узнать число параметров
    mws.invoke('GetNumberOfParameters');
  • Узнать имя параметра по его номеру
    mws.invoke('GetParameterName','ParameterNumber');
  • Узнать значения параметра по его номеру в double
    mws.invoke('GetParameterNValue','ParameterNumber');
  • Узнать значения параметра по его номеру в string
    mws.invoke('GetParameterNValue','ParameterNumber');
  • Узнать значения параметра по его имени в string
    mws.invoke('RestoreParameter','ParameterName');
  • Узнать значения параметра по его имени в double
    mws.invoke('RestoreDoubleParameter','ParameterName');
  • Узнать выражение, с помощью которого вычисляется параметр, по его имени в string
    mws.invoke('RestoreParameterExpression','ParameterName');
  • Создать новый параметр строку (или заменить существующий) с его описанием
    mws.invoke('StoreParameterWithDescription','ParameterName', 'ParameterValue','ParameterDescription');
  • Создать новый параметр строку (или заменить существующий) без описания
    mws.invoke('StoreParameter','ParameterName', 'ParameterValue');
  • Создать новый параметр double (или заменить существующий) без описания
    mws.invoke('StoreDoubleParameter','ParameterName', 'ParameterValue');
  • Убедиться не занято ли имя параметра, если незанято создать новый с указанным значениям, иначе не трогать существующий
    mws.invoke('MakeSureParameterExists','ParameterName', 'ParameterValue');
    
  • Добавить описание к существующему параметру по имени
    mws.invoke('SetParameterDescription','ParameterName');
  • Выдернуть массивы по номеру запуска расчета с именами параметров и их значениями
    mws.invoke('GetParameterCombination','ResultId','ParameterNames','ParametersValues');

Последнюю функцию в списке я как-то не смог прикрутить к матлабу. Но не суть, в данном случае она нам и не нужна.

Так как задачу я сильно упростил, то из представленного в спойлере списка интересна только одна функция:

mws.invoke('StoreDoubleParameter','ParameterName', 'ParameterValue');

В теле цикла нам надо будет сделать всего 4 операции: передать параметр, обновить модель, запустить расчет и экспортировать результаты.

Открываем цикл длиной от 1 до количества значений параметра y:

for i=1: length(y)

Передаем параметр:

mws.invoke('StoreDoubleParameter','L', y(i));

Обновляем модель:

mws.invoke('Rebuild');

Запускаем расчет:

solv.invoke('Start');

Далее подготавливаем строковые переменные путей сохранения файлов (первой строкой переводим текущее значения параметра в строку, второй — создаем строку пути сохранения файла, третьей — записываем в массив ячеек адрес текущего файла результатов:

str_y = num2str(y(i));
f_name = strcat('C:\Results_patch\patch_z_im_y=', str_y,'.txt');
f_cell(i,1) = {f_name};

В качестве результата для экспорта я выбрал график мнимой части входного сопротивления, поэтому далее следует выбор соответствующей ветки дерева результатов, процедура экспорта в ASCII, описанные в предыдущем посте, а также окончание цикла.

mws.invoke('SelectTreeItem','1D Results\Z Matrix\Z1,1');
plot1d.invoke('PlotView','imaginary');
export.invoke('Reset');
export.invoke('FileName',f_name);
export.invoke('Mode','FixedNumber');
export.invoke('Step','1001');
export.invoke('Execute')
end

После проделанных действий мы имеем набор файлов с результатом расчета мнимой части входного сопротивления. Но что еще делать с частотно-зависимыми величинами? Выводить в виде графика.

Так как все результаты вывода в ASCII из CST имеют заголовок, нам надо от него избавиться, в нашем случае это две строки в начале каждого файла:

        Frequency / MHz                Z1,1/imaginary
----------------------------------------------------------------------

Для этого воспользуемся простенькой функцией, приведенной под спойлером ниже, которую я давненько написал. Входной параметр для функции — путь к фалу для удаления заголовка.

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

Удаление заголовка
function [f_cell] = h_remove(ishod)
    ffile = fopen(ishod,'rt');
    f_cell=cell(1003,1);
    i=1;
    for i=1:length(f_cell)
    %while feof(Farfield) ==0
        f_cell{i,1} = fgetl(ffile);
        %i = i+1;
    end
    f_cell = f_cell(3: end, 1);
    fclose(ffile);
    ffile = fopen(ishod, 'w+');
    for i = 1: length(f_cell)
        fprintf(ffile, '%s\r\n', f_cell{i,1});
    end
    fclose(ffile);
    f_dat = load(ishod);
end


С помощью указанной выше функции грузим в переменные результаты расчетов, выводим график и слегка причесываем его.

y1 = h_remove(f_cell{1,1});
y2 = h_remove(f_cell{2,1});
y3 = h_remove(f_cell{3,1});
y4 = h_remove(f_cell{4,1});
y5 = h_remove(f_cell{5,1});
y6 = h_remove(f_cell{6,1});
plot(y1(:,1),y1(:,2), '-r', y1(:,1),y2(:,2), '--r',y1(:,1),y3(:,2), '-.r',y4(:,1),y1(:,2), '-b',y1(:,1),y5(:,2), '--b',y1(:,1),y6(:,2), '-.b')
grid on
xlabel('Частота, МГц')
ylabel('Сопротивление, Ом')
legend(num2str(y(1)),num2str(y(2)),num2str(y(3)),num2str(y(4)),num2str(y(5)),num2str(y(6)))

Итоговый график

Выводы


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

Вы можете сказать: Э«то все можно сделать и без Matlab'а! Что ты тут выдумал есть же встроенный редактор макросов на VBA». И я отвечу: «Да, вы правы. Можете писать всё на VBA, если вам так нравится».

Но дальнейшая обработка результатов в Matlab может быть полезна. Также можно использовать Report Generator. Тогда становится возможным оставить машину выполнять расчет в ваше отсутствие, а потом останется только распечатать и положить уже готовый отчет на стол руководителя.

Ссылки


Ниже размещаю тест скрипта, разобранного здесь, и функции h_remove для ваших экспериментов, а также ссылку на файл проекта для тех кто не заметил ссылку в тексте:

Проект антенны

Скрипт
%% Расчет значений параметра
x = sqrt(10):(10-sqrt(10))/5:10;
y = x.^2;
%% Запуск и привязка объектов CST
%cst = actxserver('CSTStudio.Application');
%mws = cst.invoke('NewMWS');
%mws.invoke('OpenFile','C:\patch.cst');
solv = mws.invoke('Solver');
export = mws.invoke('ASCIIExport');
plot1d = mws.invoke('Plot1D');
f_cell = cell(length(y),1);
%% Цикл для расчета
for i=1: length(y)
mws.invoke('StoreDoubleParameter','L', y(i));
mws.invoke('Rebuild');
solv.invoke('Start');
str_y = num2str(y(i));
f_name = strcat('C:\Results_patch\patch_z_im_y=', str_y,'.txt');
f_cell(i,1) = {f_name};
mws.invoke('SelectTreeItem','1D Results\Z Matrix\Z1,1');
plot1d.invoke('PlotView','imaginary');
export.invoke('Reset');
export.invoke('FileName',f_name);
export.invoke('Mode','FixedNumber');
export.invoke('Step','1001');
export.invoke('Execute')
end
%% Постобработка
y1 = h_remove(f_cell{1,1});
y2 = h_remove(f_cell{2,1});
y3 = h_remove(f_cell{3,1});
y4 = h_remove(f_cell{4,1});
y5 = h_remove(f_cell{5,1});
y6 = h_remove(f_cell{6,1});
plot(y1(:,1),y1(:,2), '-r', y1(:,1),y2(:,2), '--r',y1(:,1),y3(:,2), '-.r',y4(:,1),y1(:,2), '-b',y1(:,1),y5(:,2), '--b',y1(:,1),y6(:,2), '-.b')
grid on
xlabel('Частота, МГц')
ylabel('Сопротивление, Ом')
legend(num2str(y(1)),num2str(y(2)),num2str(y(3)),num2str(y(4)),num2str(y(5)),num2str(y(6)))


h_remove
function [f_dat] = h_remove(ishod)
%Грузим файл который надо править
    ffile = fopen(ishod,'rt');
%Создаем временную переменную для хранения строк исходнового текста
    f_cell=cell(1003,1);
 %Цикл читает построчной исходный файл и записывает во временную переменную
    i=1;
    for i=1:length(f_cell)
    %while feof(Farfield) ==0
        f_cell{i,1} = fgetl(ffile);
        %i = i+1;
    end
    f_cell = f_cell(3: end, 1);
    fclose(ffile);
    ffile = fopen(ishod, 'w+');
    for i = 1: length(f_cell)
        fprintf(ffile, '%s\r\n', f_cell{i,1});
    end
    fclose(ffile);
    f_dat = load(ishod);
end