Кольца Ньютона (5)
Кольца Ньютона
В этом задании Вам предстоит смоделировать классический эксперимент по интерференции света вокруг точки касания линзы и пластины. Схема эксперимента показана на рисунке:
Сверху по нормали падает монохроматическая плоская электромагнитная волна. Пройдя через линзу, среду и пластину, волна попадает на экран внизу. Из-за интерференции в оптической системе, на экране наблюдается картина из максимумов и минимумов интенсивности. Интерференция происходит между волной, прошедшей без отражений, и дважды отразившейся в малом промежутке d
волной. Мы пренебрегаем изменением направления и модуля амплитуды второй волны. Выражения для разности фаз интерферирующих волн и интенсивности изображения приведены на рисунке.
Базовая версия
Решите эту задачу рамках объектно-ориенториванного подхода. Базовая версия решения требует реализации классов NewtonRings
и NRPlotter
. Первый отвечает за вычисление распределения интенсивности на экране, а второй - за визуализацию. Минимальная сигнатура класса NewtonRings
:
class NewtonRings:
def __init__(self,
r:float, # радиус кривизны (м)
lam:float, # длина волны (нм)
n_lens:float, # показатель преломления линзы
n_plate:float, # показатель преломления пластины
n_medium:float) # показатель преломления среды
pass
def __call__(self,
x:np.ndarray, # x-координаты точек на экране (м)
y.np.ndarray # y-координаты точек на экране (м)
) -> np.ndarray:
""" Вычисляет интенсивность изображения на экране """
pass
Интенсивность изображения в максимуме должна быть равна 4
.
Класс NRPlotter
в базовой версии довольно простой:
class NRPlotter:
def __init__(self, nr:NewtonRings):
pass
def plot(self):
pass
Конструктор принимает объект класса NewtonRings
, а метод NRPlotter.plot
выполняет отрисовку изображения. Работа с этими классами должна происходить следующим образом:
import matplotlib.pyplot as plt
nr = NewtonRings(r=1000., lam=400., n_lens=1.25,
n_plate=1.3, n_medium=1.2)
nrp = NRPlotter(nr)
nrp.plot()
plt.show()
Подберите разумные значения для диапазона значений параметров x
и y
, предполагая, что длина падающей волны находится внутри видимого спектра, а радиус кривизны линзы очень большой, порядка километра. Интерференционная картина должна выглядить примерно так:
Указания 1
- Выражение для амплитуды волны можно преобразовать таким образом, что оно не будет содержать комплексных величин.
- При отражении от оптически более плотной среды фаза волны меняется на pi. Этот эффект необходимо учесть.
- Рассмотрите использование функции
matplotlib.pyplot.contourf
для визуализации интерференционной картины - Регулярную двумерную сетку можно создать с помощью функции
numpy.meshgrid
. Она возвращает массивы, которые можно подавать на вход фукнцииmatplotlib.pyplot.contourf
- Необходимо подобрать оптимальный шаг сетки так, чтобы контуры верно отображались и отрисовка не занимала слишком много времени (и памяти). Если сетка будет иметь слишком большой шаг, изображение будет содержать артефакты
- Рассмотрите использование функций
pyplot.axes('equal')
иpyplot.axes('off')
чтобы сделать масштабы по горизонтали и вертикали одинаковыми и убрать отрисовку элементов осей
Как проверять решение локально
Код решения и полученные изображения будут проверяться вручную. Работу класса NewtonRings
можно проверить локально с помощью обычной процедуры: устанавливаем необходимые пакеты (один раз):
pip install -r requirements.txt
Запускаем тесты:
pytest -vs
Продвинутая версия
В продвинутой версии мы реализуем графический интерфейс пользователя (GUI), с помощью которого можно будет изменять параметры системы (длину волны, показатели преломления и др.).
Реализовать GUI можно различными средствами. Например:
- Виджеты matplotlib. Этот вариант наиболее простой в реализации, но даже небольшое количество виджетов приводит к сильному замедлению работы программы
- Библиотека tkinter. Для этого варианта объект
matplotlib.Figure
необходимо будет встроить в окно Tk - Библиотека PyQt5. Эту библиотеку необходимо установить отдельно. В этом варианте также необходимо будет встроить объект
matplotlib.Figure
в окно Qt5. Больше примеров можно найти здесь.
Покажем несколько примером GUI. Виджеты matplotlib:
Библиотека tkinter:
Библиотека PyQy5:
Нет необходимости копировать эти примеры. Вы можете самостоятельно разработать интерфейс и его функциональность.
Указания 2
- Многократный вызов функции
pyplot.contourf
приведет к замедлению работы программы. Чтобы этого избежать необходимо перед вызовом функции удалять предыдущие объекты:
for coll in self.cnt.collections:
if coll in self.ax.collections:
self.ax.collections.remove(coll)
cnt = ax.contourf()
Указания для работы с виджетами matplotlib
В этой задаче могут оказаться полезными виджеты TextBox
, Button
, Slider
, RadioButtons
. Конструктор любого виджета matplotlib первым параметром принимает объект Axes
, который определяет размер и положение виджета.
Чтобы связать виджет с логикой программы необходимо связать с ним функции, которые должны вызываться при определенных событиях. Например:
- Методы
on_text_change
иon_submit
виджетаTextBox
принимают callable объект для вызова при изменеии текста и при нажатии клавишиEnter
, соответственно - Метод
on_clicked
виджетовButton
иRadioButtons
- Метод
on_changed
виджетаSlider
Ссылка на объект виджета должна быть доступна в течение времени исполнения программы, чтобы виджет был активен.
Подробное описание и примеры смотрите в документации.
Указания для работы с tkinter
Минимальный пример встраивания графика matplotlib
в программу с tkinter
находится в файле tkinter_example.py
. Рассмотрите использование виджетов tkinter.Label
, tkinter.Entry
, tkinter.Button
, tkinter.Scale
и tkinter.ttk.Combobox
. Виджеты tkinter.Scale
и tkinter.Button
можно связать с callback-функциями через параметр конструктора command
:
s = tkinter.Scale(master=master, command=callable)
b = tkinter.Button(master=master, text=text, command=callable)
Задать и прочитать содержимое виджета tkinter.Entry
можно с помощью методов insert
и get
, соответственно. Виджет tkinter.ttk.Combobox
имеет методы set
и get
.
Метод bind
позволяет связать виджет, событие и действие (callback-функцию). Так, например, можно связать главное окно программы, некоторое действие и нажатие клавиши Enter:
class Gui(tkinter.Tk):
def __init__(self):
super().__init__()
self.bind('<Return>', callable)
Указания для работы с PyQt5
Минимальный пример встраивания графика matplotlib
в программу с PyQt5
находится в файле pyqt_example.py
. Рассмотрите использование виджетов QPushButton
, QSlider
, QLabel
, QLineEdit
, QComboBox
из модуля PyQt5.QtWidgets
.
Связать эти виджеты с callback-функциями можно следующим образом:
button = QPushButton(label, parent)
button.clicked.connect(callable)
qle = QLineEdit(init, parent)
qle.textChanged.connect(callable)
qle.returnPressed.connect(callable)
sld = QSlider(Qt.Vertical, parent)
sld.valueChanged.connect(callable)
combo = QComboBox(self)
combo.addItems(items)
combo.activated.connect(fcn)
Класс FFPlotter
удобно сделать наследником класса FigureCanvasQTAgg
из модуля matplotlib.backends.backend_qt5agg
, как показано в примере. Класс FFGui
удобно сделать наследником класса QMainWindow
из модуля PyQt5.QtWidgets
.
В примере GUI выше реализовано сохранения окна в графический файл. С библиотекой PyQt5
снимок окна программы можно выполнить с помощью объекта QPixmap
из модуля PyQt5.QtGui
. Вот так может выглядеть метод класса FFGui для сохранения снимка окна:
class FFGui(QMainWindow):
# ...
def save(self):
pix = QPixmap(self.size())
self.render(pix)
pix.save('ffilter.png', 'png')