[Из песочницы] Моделирование планеты маргариток
- Лирика
- Описание интерфейса
- Декомпозиция
- Модель
- Логика
1. Лирика
Давным-давно, не помню где, я узнал об одном удивительном эксперименте, начало описания было такое: Представьте себе планету, примерно такую, как Земля, которая вращается вокруг звезды примерно такой, как Солнце. На планете есть жизнь, но не очень разнообразная — всего два вида маргариток — черные и белые. Представили? Какой заход! Автор красавчик, почти Экзюпери.
Далее описывался эксперимент: В начальной точке маргаритки есть только в виде семян, а планета настолько холодна, что на экваторе температура на грани выживания. Тем не менее прорастает несколько маргариток черных и белых, и они даже начинают цвести. Белая маргаритка, из-за своей большой отражательной способности отражает больше света, чем зеленые листики и коричневая земля, в этом месте локально становится холодней, и она замерзает. Вокруг черной маргаритки, наоборот возникает небольшая прогретая зона, так как она поглощает больше энергии от звезды. Маргаритка начинает чувствовать себя лучше, цветет, а потом, в прогретую землю бросает свои семена. Из этих семян прорастают еще несколько черных маргариток (потому что родитель у них черный), цветение этих маргариток прогревает еще больший участок, на котором может расти еще больше маргариток, и они тоже будут черными, потому что здесь оставляют семена только черные маргаритки.
Таким образом, потепление, вызванное цветением черных маргариток, провоцирует еще большее распространение черных маргариток, которые в свою очередь прогревают больше площади вокруг себя, и так по кругу. Таким образом черные маргаритки захватывают экваториальную область, но дальше распространяться не могут — там слишком холодно.
Однако такое бурное цветение черных маргариток уменьшает отражательную способность всей планеты (альбедо), это повлияет на баланс поглощенной/отраженной энергии в планетарном масштабе, что приводит к повышению эффективной температуры планеты, то есть на всей планете становится теплей. После этого черные маргаритки еще немного расширяют свои территории на юг и на север, что в свою очередь приводит к еще большему глобальному потеплению. Таким образом черные маргаритки пробираются к полюсам, а планета разогревается.
Но не нужно забывать — на планете стало теплее, и теперь есть все условия для роста и белых маргариток тоже. Более того, по мере прогрева, на экваторе становится все жарче, а черные маргаритки еще сильнее утепляют землю вокруг себя, и в этом районе им становится все более некомфортно. Не то, что белым маргариткам, которые отражают больше энергии, и создают вокруг себя приятную прохладную зону, в которой им хорошо расти, и туда же отбрасывать семена. Ну вы понели — белые теснят с экватора черных. Но тут сюрприз — альбедо планеты снова увеличивается из-за больших площадей белых маргариток, планета холодеет и в конечном итоге наступает некоторое равновесие.
Далее автор не намерен оставлять планету в этом унылом для него, и комфортном для маргариток состоянии. Происходит вот что — Звезда, как у них это обычно бывает, начинает светиться ярче, и на планету падает больше энергии. Реакция планеты — увеличение площади распространения белых маргариток, увеличение альбедо — и снижение эффективной температуры, переход в новое равновесное состояние. Почти то же самое происходит при снижении светимости звезды, только появляется больше черных маргариток.
Вывод, который делает автор — биосфера сама себе создает комфортные температурные условия на планете в широких диапазонах светимости звезды. Авторов этого замечательного эксперимента зовут Джеймс Лавлок и Эндрю Уотсон. Кому интересны все выводы и более подробное описание см. Википедия — Маргаритковый мир, там написано больше и лучше. Смысл этого долгого вступления не пересказать статью из вики, а подвести к самому интересному — непосредственно проведению эксперимента. Автор статьи, описывающей эксперимент, задавал риторический вопрос: Рассуждения выглядят логично, но как провести такой эксперимент, где взять планету со звездой, с подходящими условиями, и как доставить туда семена маргариток? Даже если кому-то взбредет в голову использовать для этого Марс то у него ничего не получиться. Я был очень опечален таким поворотом потому, что большой фанат терраформирования и крутых экспериментов. Но автор, к моей большой радости, сказал, что такой эксперимент возможен с помощью компьютерного моделирования.
Ну конечно!!! Этот эксперимент, как бы собственно для компьютерного моделирования и был придуман, и даже достиг своей цели (ну то есть посрамил каких-то там двух мудрецов, см. вики). На этом отрезке повествования об эксперименте у меня прям сперло дыхание, потому что я подумал о счастливчиках, которые смотря на это описание, каким-то образом, превращают идею во что-то наглядное, доступное для того, чтобы посмотреть в реальном времени, «потрогать руками». При том, интересен был как сам процесс моделирования, так и проведение экспериментов. Прочитать эксперимент и подивиться выводам это одно, а «поиграть» с этой планетой, поэкспериментировать с разными условиями, это совсем другое. Но тогда я еще не мог программировать :(, но настали нынешние времена, к счастью, и поэтому предлагаю перейти от длинной и скучной лирики, к содержательной части. Для тех, кто просто хочет поиграть с планетой может переходить к описанию интерфейса, кого увлекает процесс моделирования к части под названием Декомпозиция и далее.
2. Описание интерфейса
В левой части расположено два блока Populator и Updater, в центральной части визуальное отображение планеты и некоторых параметров, в правой части графики изменения эффективной температуры и Звездной постоянной.
Управление осуществляется в блоках Populator и Updater.
В блоке Populator можно задать соотношение белых маргариток, черных маргариток и пустых ячеек, после нажатия кнопки Populate, планета будет заселена соответствующим образом. Заселить планету можно как в начале эксперимента, так и в любой другой момент, если понадобится.
Блок Updater отвечает за обновление модели планеты, в нем можно задать количество обновлений за раз, а также значение (допускаются отрицательные) и периодичность инкрементирования звездной постоянной (например, обновить 100 раз, при этом увеличивать звездную постоянную на 1 каждые две итерации).
Визуальное отображение планеты:
Белые кружочки — это районы с белыми маргаритками, черные — с черными, оливковые — пустые ячейки.
В левом верхнем углу текущие значения эффективной температуры по кельвину, звездной постоянной, альбедо и номер итерации.
Справа от планеты две цветовые шкалы: левая — для черных маргариток, правая для белых. Каждая точка шкалы соответствует температуре расположенного напротив пояса планеты. Легенда такая: Синий — слишком холодно для жизни, Желтый — жить можно, Зеленый — жить не только можно, но и комфортно, Красный — слишком жарко для жизни.
3. Декомпозиция
В приложении использован паттерн MVC, хотя в JavaFX, как известно вью, и контролер немного слиплись, зато границы модели очерчены совершенно отчетливо.
Модель, в свою очередь, также разбита на часть, отвечающую за состояние, и часть, отвечающую за поведение, в лучших традициях, так сказать. Еще есть часть, отвечающая за сохранение результатов в БД, работает вроде нормально, но в интерфейс и в настройки не стал выносить за ненадобностью. Вид и слой персистентности описывать не буду, ниже все про модель и поведение.
4. Модель
Модель представлена шестью обычными классами Planet, Starr, Zone, Conditions, BlackDaizy, WhiteDaizy, NoDaizy, одним абстрактным Daizy, и одним перечислением Type. Хотя можно было и меньшим числом классов, я подумал, что звезда настолько крупная и важная вещь, что оставлять ее без класса было бы некрасиво. А для маргариток, в программе под названием «Планета Маргариток» была выстроена целая небольшая иерархия классов, и даже введено перечисление. Таким образом, основные классы, определяющие состояние это, Planet, Conditions и Zone.
Planet — является основным агрегирующим классом, экземпляр этого класса в приложении играет роль модели, из него контроллеры получают состояние для вью, а классы, отвечающие за поведение, получают ссылку на экземпляр Planet для изменения состояния.
Класс Planet содержит поля, характеризующие состояние планеты: это
/** Отражающая способность, характеризуется отношением отраженного света к падающему*/
private double albedo = 0;
/** Эффективная температура: зависит от поглощаемой энергии,
* вычисляется как температура абсолютно черного тела по закону Стефана-Больцмана*/
private double temperature = 0;
/** Да планета имеет размер*/
private double radius = Conditions.getInstance().radius;
/** количество поясов на которое разбито одно полушарие, локальная температура вычисляется для каждого пояса, на всей площади одного пояса — одинаковая температура*/
private int halfZonation = Conditions.getInstance().halfZonation;
/** фрагментация пояса: максимальное количество маргариток на пояс,
на всех поясах - количество маргариток одинаковое, это не ограничение программы, а геометрический закон, ибо пояса с одинаковой высотой имеют одинаковую площадь,
* так - то*/
private long daiziesPerZone = Conditions.getInstance().daiziesPerZone;
/** эффективная площадь - проекция планеты на плоскость перпендикулярную направлению излучения идущему от звезды к планете, используется для расчета альбедо */
private double effectiveArea;
/** площадь пояса. у всех поясов area - одинаковая
S = 2πrh,
r – радиус сферы,
h – высота сферического пояса.
*/
private double zoneArea;
/** условная площадь одной маргаритки, справочный параметр*/
private double daisyArea;
Кроме того, класс Planet содержит ссылку на звезду Starr, массив экземпляров класса Zone, каждый элемент которого характеризует отдельный пояс, счетчик апдейтов, признак inhabited ну, то есть обитаемая ли планета. А также ссылки на классы, отвечающие за поведение
PopulatorImpl populator = new PopulatorImpl();
AlbedoCalculator albedoCalculator = new AlbedoCalculatorImpl();
о которых речь пойдет ниже.
Второй важный класс — это Conditions, в нем собраны все константы. Вот некоторые
public double Kelvin = -273.15;
// Planet constants
public double radius = 1000.0;
public int halfZonation = 90;
public long daiziesPerZone = 100;
/**
эмпирическая отсебятина: разница температур на экваторе и на полюсах, если вычислять в зависимости от широты
то на полисе окажется нулевая по кельвину, что противоречит здравому смыслу
величина должна зависеть от теплопроводности планеты, миграции тепла по атмосфере, и т.п.
к тому же температура на поверхности должна быть выше расчетной из-за того, что поверхность первой прогревается,
и из-за парникового эффекта, если, конечно, есть подходящая атмосфера
*/
public double planetDeltaTemper = 70.0;
/* используются для вычисления большого альбедо*/
public double blackDaisyAlbedo = 0.1;
public double whiteDaisyAlbedo = 0.9;
public double noDaisyAlbedo = 0.5;
/* Значение комфортной температуры для жизни маргариток
Собственно, реализация прогрева и охлаждения почвы вокруг маргариток,
*/
public double blackComfortableTemper = 18 + (-Kelvin);
public double whiteComfortableTemper = 22 + (-Kelvin);
/**Звездная постоянная - суммарная мощность излучения звезды, проходящего через единичную площадку,
* ориентированную перпендикулярно потоку, на расстоянии одной астрономической единицы от Звезды
* для Солнца и Земли равняется 1367 Вт/м²*/
public double StarConstant = 1367.0;
/** Значение постоянной Стефана-Больцмана
* */
public double StephanBoltsmanConst = 5.67e-8;
И третий важный, но не большой класс модели Zone:
/** высота границы ближайшей к экватору */
private double latitude;
/** эффективная площадь - проекция половины шарового слоя на плоскость,
* используется для расчета альбедо */
private double effectiveArea;
/** высота шарового слоя*/
private double height;
/** температура на поверхности в данной zone*/
private double localTemperature;
// количество маргариток
private long numBlackDaisies;
private long numWhiteDaisies;
private long numEmptyCells;
Обратите внимание на метод установки локальной температуры вычисляет температуру, как описано выше:
public void setLocalTemperature(double globalTemperature) {
this.localTemperature = globalTemperature + Conditions.getInstance().planetDeltaTemper*Math.cos(latitude);
}
В классе нет методов, вычисляющих состояние, значение всех полей передается в параметрах конструктора, либо устанавливаются сеттерами.
5. Логика
Почти всё поведение собрано в паке logic, и представлено следующими классами:
ZoneMaker — класс используется для создания всего массива зон планеты, с учетом того, что у разной планеты может быть разная фрагментация (количество зон), а у каждой зоны своя уникальная широта, соответствующая индексу в массиве
AlbedoCalculator — интерфейс, единственный метод
double calcAlbedo(Zone[] zones);
его реализации используются для вычисления альбедо всей планеты в зависимости от соотношения черных и белых маргариток и незанятого пространства, а также внимание, их расположения — так как маргаритки, растущие на разных широтах, вносят разный вклад в общее альбедо.
StephanBoltsman — класс для расчета эффективной температуры планеты.
Populator — интерфейс, отвечающий за заселения планеты маргариткам.
Метод
void populate(Zone zone, int whiteExpectance, int blackExpectance, int noneExpectance);
предназначен для заполнения одной, переданной в параметре зоны, параметры
int whiteExpectance, int blackExpectance, int noneExpectance
что-то вроде математических ожиданий, в зависимости от их соотношения, в случайном порядке по зоне будут посажены белые и черные маргаритки, или будет сделан пропуск, но общее число ячеек для маргариток в зоне всегда постоянно, и зависит от начальных настроек (задается Conditions.daiziesPerZone).
Этот метод используется в двух других методах, которые заполняют уже все зоны на планете:
void populateInitial(Zone[] zones, int whiteExpectance, int blackExpectance, int noneExpectance);
Заполняет все зоны с одинаковым соотношением. В конструкторе планеты стоят параметры 0,0,1. То-есть планета создается необитаемой.
Метод
void rePopulate(Planet planet);
является реализацией механизма вытеснения маргаритками маргариток, и заполнения пустого пространства. Он используется для перераспределения маргариток на планете, в зависимости от сложившихся на Планете условий. Метод работает приблизительно следующим образом: в нем определены три переменные, в первую очередь они устанавливаются в значения количества белых, черных маргариток и пустых ячеек очередной зоны.
Далее, если температура для какого-либо вида маргариток не соответствует жизненным условиям, соответствующий параметр обнуляется. Если соответствует жизненным условиям, то значение остается неизменным. Если температура находится в интервале комфорта, то значение удваивается. В случае, если температура впервые стала пригодна для жизни, для какого-либо вида маргариток, то соответствующий параметр устанавливается в среднее арифметическое от количества соответствующих маргариток из смежных зон.
И наконец, нужно описать метод update () класса Planet, который отвечает за жизненный цикл каждой итерации:
public void update() {
albedo = albedoCalculator.calcAlbedo(zones);
temperature = StephanBoltsman.countTemperature(albedo,star.getStarConstant());
updateLocalTempers();
populator.rePopulate(this);
iterationId++;
}
В первую очередь вычисляется альбедо планеты, исходя из количества и расположения маргариток. Затем вычисляется эффективная температура планеты используя только что найденное альбедо и звездную постоянную (значение которой также может измениться). Затем обновляются значения локальных температур в зонах исходя из найденной эффективной температуры. И в конце, используя новые данные локальных температур, осуществляется перераспределения маргариток.
Ссылка на гит. Сборка: gradlew build