def update(self): """Update the simulation by one timestep.""" stept = self._timing.routine('simulation step') stept.start('determining tile movements') old = set([t for shape in self._shapes for t in shape.tiles]) new = dict() overlapping = {} for t in self._indexedtiles: overlapping[t] = [] for i in range(len(self._shapes)): speed = norm(self._shapes[i].v) group, v = move(self._indexedtiles, self._shapes[i].tiles, self._shapes[i].v, self._tileadj, self._index) self._shapes[i] = Group(list(group.keys()), v) for dest, sources in group.items(): if dest in new: new[dest].append(TileMovement(sources, speed)) else: new[dest] = [TileMovement(sources, speed)] overlapping[dest].append(i) stept.start('applying tile movements') collisions = {} newe = {} seen = set() for dest, movements in new.items(): # get all the source tiles contributing to this one newe[dest] = NextTileValue(movements) if not dest in seen: try: old.remove(dest) except KeyError: # calculate the amount to build up the leading edge newe[dest].build(self._build * sum([m.speed for m in movements])/len(movements)) seen.add(dest) for pair in combinations(overlapping[dest], 2): if pair in collisions: collisions[pair] += 1 else: collisions[pair] = 1 # apply the new values for t, e in newe.items(): e.apply(t) # clear out abandoned tiles for t in old: t.emptyocean(self.seafloor()) # record each continent's total pre-erosion above-sea size heights = [sum([t.elevation for t in s.tiles]) for s in self._shapes] if self.hasatmosphere: stept.start('"simulating" climate') seasons = [0.1*v for v in list(range(-10,10,5)) + list(range(10,-10,-5))] c = climate(self.tiles, self.adj, seasons, self.cells, self.spin, self.tilt, self.temprange, 0.5, self.haslife, self._climatemappings, self._climateprof) if self._climateprof: self._climateprof.dump_stats('climate.profile') for v, tile in self.tiles.items(): tile.climate = c[v]['classification'] tile.seasons = c[v]['seasons'] stept.start('determining erosion') erosion = erode(self.tiles, self.adj) for t in self.tiles.values(): t.erode(erosion, self._erode) for t in self.tiles.values(): # if the tile is in at least one shape, apply the erosion materials if len(overlapping[t]) > 0: if len(erosion[t].materials) > 0: t.deposit(sedimentary.deposit(erosion[t].materials, self.haslife, False, t.climate)) # otherwise, require a certain threshold elif sum([m.amount for m in erosion[t].materials]) > 1.5: t.deposit(sedimentary.deposit(erosion[t].materials, self.haslife, True, t.climate)) sourceshapes = set() for e in erosion[t].sources: for shape in overlapping[e]: sourceshapes.add(shape) for s in sourceshapes: if not t in self._shapes[s].tiles: self._shapes[s].tiles.append(t) overlapping[t] = list(sourceshapes) if self._lifeticks: self._lifeticks -= 1 else: self._atmosphereticks -= 1 stept.start('applying isostatic effects') for s, h in zip(self._shapes, heights): dh = (h - sum([t.elevation for t in s.tiles]))/float(len(s.tiles)) for t in s.tiles: t.isostasize(dh) stept.start('performing random intrusions') for t in self.tiles.values(): if t.subduction > 0: if random.random() < 0.1: t.intrude(igneous.intrusive(max(0, min(1, random.gauss(0.85, 0.15))))) t.transform(metamorphic.contact(t.substance[-1], t.intrusion)) stept.start('applying regional metamorphism') for t in self.tiles.values(): t.transform(metamorphic.regional(t.substance[-1], t.subduction > 0)) for t in self.tiles.values(): t.cleartemp() stept.start('merging overlapping shapes') # merge shapes that overlap a lot groups = [] for pair, count in collisions.items(): if count > min([len(self._shapes[i].tiles) for i in pair])/10: for group in groups: if pair[0] in group: group.add(pair[1]) break elif pair[1] in group: group.add(pair[0]) break else: groups.append(set(pair)) gone = [] for group in groups: largest = max(group, key=lambda i: len(self._shapes[i].tiles)) tiles = list(self._shapes[largest].tiles) v = array(self._shapes[largest].v) * len(tiles) for other in group: if other != largest: v += array(self._shapes[other].v) * len(self._shapes[other].tiles) tiles += self._shapes[other].tiles gone.append(self._shapes[other]) self._shapes[largest].tiles = list(set(tiles)) v /= len(tiles) self._shapes[largest].v = v for s in gone: self._shapes.remove(s) stept.start('randomly splitting shapes') # occaisionally split big shapes for i in range(len(self._shapes)): if random.uniform(0,1) > self._splitnum / len(self._shapes[i].tiles): self._shapes[i:i+1] = [Group(ts, self._shapes[i].v + v * self._dp) for ts, v in split(self._shapes[i].tiles)] stept.done() self.dirty = True
def test_deep_contact_no_granulite(self): layers = metamorphic.contact([layer({ 'type': 'X' }, 50)], (25, 26)) self.assertEqual(layers[-2]['rock']['name'], 'hornfels') self.assertEqual(layers[-3]['rock']['type'], 'X') self.assertEqual(layers[-4]['rock']['name'], 'hornfels')
def test_contact_aureole_depends_on_intrusion_size(self): thicklayers = metamorphic.contact([layer({ 'type': 'X' }, 50)], (5, 6)) thinlayers = metamorphic.contact([layer({ 'type': 'X' }, 50)], (5.4, 5.6)) self.assertLess(thinlayers[-2]['thickness'], thicklayers[-2]['thickness'])
def test_contact_hornfels_below(self): layers = metamorphic.contact([layer({ 'type': 'X' }, 50)], (5, 6)) self.assertEqual(layers[-6]['rock']['name'], 'hornfels')
def test_contact_granulite_below(self): layers = metamorphic.contact([layer({ 'type': 'X' }, 50)], (5, 6)) self.assertEqual(layers[-5]['rock']['name'], 'granulite')
def test_contact_intrusion_left_alone(self): layers = metamorphic.contact([layer({ 'type': 'X' }, 50)], (5, 6)) self.assertEqual(layers[-4]['rock']['type'], 'X')
def test_contact_leaves_top_alone(self): layers = metamorphic.contact([layer({ 'type': 'X' }, 50)], (5, 6)) self.assertEqual(layers[-1]['rock']['type'], 'X')
def test_contact_succeeds(self): metamorphic.contact([layer({}, 50)], (5, 6))