July 31, 2020

Temperature V: Big Picture

To find the temperature, I created functions that more or less followed the guidelines from this post. The best way I found was to have slightly different functions for each category.

'normal': lambda x: -42.5 * (x / 90) ** 2 + 25 if x >=0 else -20 * (-x / 90) ** 2 + 25,
'hot': lambda x: -42.5 * (x / 90) ** 2 + 25 if x >=0 else -20 * (-x / 90) ** 4 + 25,
'cold': lambda x: -42.5 * (x / 90) ** 1.5 + 25 if x >=0 else -20 * (-x / 90) ** 1 + 25,
'cont': lambda x: -63 * (x / 90) ** 2 + 25 if x >=0 else -56.5 * (-x / 90) ** 2 + 25,
'cont+': lambda x: -63 * (x / 90) ** 2 + 25 if x >=0 else -20 * (-x / 90) ** 2 + 25

Then I just flip it around (reverse the latitude) in the opposite season. The common theme remains: the important thing is that this code exists and can be easily applied.
The next step is a small blur to smooth out the transitions, and then I lapse the temperature up based on height (-3.5 F per 1000 ft).

January
January

Looks reasonable to me, although I probably should fiddle with the map colors so that the hot and cold areas pop a bit more.

July 27, 2020

Temperature IV: Coastal Influence

Coastal and continental influences help make the temperature distribution more interesting.

With winds and current temperatures well in hand, should be easy enough to find these areas where cold and hot temperatures (here defined simply where the current is colder or warmer than the default temperature) are distributed near the coast.

In these plots, blue and red represent cold and hot influences. Yellow represents continental influence (large landmasses) and dark yellow is "continental plus."
January
July
...Quite a lot of cold current influence, to be perfectly honest. I was expecting more of a mix. This tells me I might need to do some work on my current temperature algorithm.

For now, however, this part of the code works. Garbage in, garbage out, so I will need to return to the "in garbage" at some point soon.

The other explanation is that the large landmass across the equator prevents true recirculation of the warmest waters. The poles, which generate the cold currents, are relatively unchallenged. So perhaps there is an explanation here where the code remains ok.

July 24, 2020

Rain II

Now we can begin to get closer to the meat of climate.

I begin with the base precipitation value at the coasts: \[P_h = p_0 \cdot \left(\exp\left(\frac{-\left(\ell - s\right)^2}{\sigma^2}\right) + \\ \frac{1}{2} \cdot \exp\left(\frac{-\left(\ell - 45 s\right)^2}{\sigma^2}\right) + \frac{1}{2} \cdot \exp\left(\frac{-\left(\ell + 45 - s\right)^2}{\sigma^2}\right)\right) \frac{(90 - \ell)(90 + \ell)}{90^2}\], where $p_0$ is the max precipitation value (127 mm/mo), $\ell$ is the latitude, $s$ is a seasonal parameter (15 in summer, -15 in winter), and $\sigma=19.5$.

Wet vs dry coasts (summer)
Throw in a box blur (n=3, radius=2) and baby, you got a stew goin'.


January
July

So far I like these results more than the first time around. Onward!

July 22, 2020

Wind VIII: Over the Land to Skye

Once we have established the wind pattern over the sea, we can determine the effect that the topography has on those winds.

First, I get a list of all winds blowing on or near the coasts. For each of those, I take the projected vector of the wind onto each neighboring hex. If the angle of the projected vector is $<90^\circ$, I know that it's at least in the right direction to catch a bit of that wind.

From there, I determine $dh$, which is the difference between the heights of the two hexes, source and target. If $dh$ is downhill ($dh<0$), there's no change. Otherwise, the turnaside angle $\theta$ is calculated as \[\theta = \frac{90 \exp\left(r \cdot \mathit{dh}\right)}{90 + \left(\exp\left(r \cdot \mathit{dh}\right) - 1\right)}\], where $r=2\log(89)/5000$ is calculated such that the wind will be totally turned aside $90^\circ$ when the slope is 5000 ft or more.

This is one of those areas that I've still not managed to tweak completely to my satisfaction, but it will be good enough to use for precipitation.

Angle of wind vectors (not speed) in summer

Wind VII

I'm looking over my old code and wondering what on earth I actually did to make this work.

Or whether it worked at all.

Allegedly, I used a curl function to boil the pressure down into a wind value (wind rotates around pressure centers, this makes sense). But I had gotten that initial pressure from a hand-drawn map on GIMP, and that's what I'm trying to get away from in this latest attack.

I tried to fiddle with getting the gradient but the implementation of a discrete gradient on a hex grid was somewhat problematic. However, this value may be useful later on in determining the length of the wind vector - winds on a steep pressure gradient will naturally be stronger.

The angle is another matter. This is where I really thought the gradient would help (and maybe it would on a different grid or even map layout). I ended up doing something much simpler: "walk" around the edge of the major pressure cells, correcting for direction based on the Coriolis effect, then (predictably) use IDW to fill in the rest of the map.

a quarter million objects has more of an effect on performance than you might think

For now, I am not box-blurring these values, so the transitions are still a little choppy. My plan is to apply the effect of the topography on the wind vectors and then smooth it out, so that everything lines up more nicer.

July 21, 2020

Pressure II

Update from here.

This time around, since I'm avoiding all manual input as much as possible, I can programmatically generate the pressure bands, at least a rough approximation of them.

The first step is pretty simple, I just apply the rules from this post to hexes which meet the correct criteria.

Then to smooth it out, I apply a box blur with a radius of 10 hexes to the pressure values (which are scaled between -1 and 1). I do this 3 times to approximate a Gaussian blur. Since I'm doing this on a hex grid, I haven't yet figured out any of the clever optimization tricks that are available for this approximation on a square grid.

January (winter in the north)

July (summer in the north)

For my next trick, I will try to avoid memory leaks while calculating wind flow.

July 17, 2020

Currents VI: The Hand of Franklin

With the current code updated, I tackled water temperature and icecap formation.

Warm water from one region will move to a colder region, and vice versa. Where does this happen?

First, I assign a temperature to all water cells based on season and latitude $\ell$. Compared to some of my other functions, it's a little boring: \[T_\ell = \begin{cases}-50 & \text{if $\ell < 0$} \\ -57 & \text{otherwise}\end{cases}\cdot\left(\frac{\ell}{90}\right)^{\begin{cases}4 & \text{if $\ell<0$} \\ 2 & \text{otherwise}\end{cases}}\] This is different from my calculation of air temperature.

Once that is done, I "push" that water around for 80 hexes - an arbitrary number, but whatever. Then I take the average of all the water that has entered a new cell, and there we have the new temperature.


light blue: water that is colder than expected

My next move is to check where the water temperature is below freezing, to see how the ice caps look. I have to do a bit of cleaning here because otherwise I get a whole bunch of unconnected floating bergs - which ain't terribly realistic. But after convincing the water of what I think it should be really doing instead, I get a nice permanent ice cap (dark blue), with a seasonal glaciation shown in light blue.

North Pole


South Pole

The shapes aren't mindblowingly realistic but they'll do for my purposes. Of particular interest are potential harbors which become walled off by icebergs during the cold season, as well as any effects the caps might have on navigational routes: it might be quicker to tempt the poles, but depending on the level of sailing technology it might be wise to steer well clear of even the temporary caps.

Next, I'll revisit wind generation, then return to sea temperature to see where the cold water is affecting coastal climate.