Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

import pytest 

import simpy 

import shapely.geometry 

import logging 

import numpy as np 

import pint 

import datetime 

import time 

import digital_twin.core as core 

import digital_twin.model as model 

 

 

def type_to_class_name(type): 

type_names = type.split() 

capitalized = [x.capitalize() for x in type_names] 

return ''.join(capitalized) 

 

def compute_v_linear(v_empty, v_full): 

return lambda x: x * (v_full - v_empty) + v_empty 

 

@pytest.fixture 

def env(): 

simulation_start = datetime.datetime(2019, 1, 1) 

my_env = simpy.Environment(initial_time = time.mktime(simulation_start.timetuple())) 

my_env.epoch = time.mktime(simulation_start.timetuple()) 

return my_env 

 

@pytest.fixture 

def available_equipment(env): 

# should be read from database 

equipment_data = [ 

{ 

'id': 'EGG123', 

'type': 'Side stone dumping vessel', 

'speed loaded': 6.5, 

'tonnage': 2601, 

'tags': ['mover'] 

}, 

{ 

'id': 'Boaty McBoatStone', 

'type': 'Multi purpose support vessel', 

'speed loaded': 7.0, 

'tonnage': 1824, 

'capacity': 10.3, 

'tags': ['loader', 'mover'] 

}, 

{ 

'id': 'Loady McLoader', 

'type': 'Simple Loading Crane', 

'capacity': 13.2 

}, 

{ 

'id': 'Unloady McUnloader', 

'type': 'Simple Loading Crane', 

'capacity': 12.1 

} 

] 

 

type_to_mixins_mapping = { 

'Side stone dumping vessel': (core.Identifiable, core.Log, core.ContainerDependentMovable, core.HasResource), 

'Multi purpose support vessel': (core.Identifiable, core.Log, core.ContainerDependentMovable, core.Processor, core.HasResource), 

'Simple Loading Crane': (core.Identifiable, core.Log, core.Processor, core.HasResource, core.Locatable) 

} 

 

ship_mixins = {} 

for data in equipment_data: 

ship_type = data['type'] 

mixin_classes = type_to_mixins_mapping[ship_type] 

klass = type(type_to_class_name(ship_type), mixin_classes, {}) 

ureg = pint.UnitRegistry() 

kwargs = dict(env=env, name=data['id']) 

if issubclass(klass, core.Processor): 

processing_speed = (data['capacity'] * ureg.ton / ureg.minute).to_base_units() 

kwargs['loading_func'] = (lambda x: x / processing_speed.magnitude) 

kwargs['unloading_func'] = (lambda x: x / processing_speed.magnitude) 

if issubclass(klass, core.HasResource): 

kwargs['nr_resources'] = 1 

if issubclass(klass, core.HasContainer): 

tonnage = (data['tonnage'] * ureg.metric_ton).to_base_units() 

kwargs['capacity'] = tonnage.magnitude 

if issubclass(klass, core.Locatable): 

# todo change this to something read from the database 

kwargs['geometry'] = shapely.geometry.Point(4.066045, 51.985577) 

if issubclass(klass, core.Movable): 

speed_loaded = (data['speed loaded'] * ureg.knot).to_base_units().magnitude 

if issubclass(klass, core.ContainerDependentMovable): 

kwargs['compute_v'] = compute_v_linear(speed_loaded * 2, speed_loaded) 

else: 

kwargs['v'] = speed_loaded 

 

ship = klass(**kwargs) 

ship_mixins[data['id']] = ship 

return ship_mixins 

 

 

# simple test to see if equipment list is generated correctly 

def test_ship_list(available_equipment): 

print('') 

for id in available_equipment: 

print(id + ': ' + str(available_equipment[id])) 

 

@pytest.fixture 

def available_sites(env): 

# should be read from database 

# example of data expected for simple "point" sites 

site_data = [ 

{ 

'id': 'Den Oever', 

'lat': 52.94042293840172, 

'lon': 5.054676856441372, 

'tonnage': 20_000 

}, 

{ 

'id': 'Kornwerderzand', 

'lat': 53.06686424241725, 

'lon': 5.294877712236641, 

'tonnage': 30_000 

}, 

{ 

'id': 'Stockpile', 

'lat': 52.94239823421129, 

'lon': 5.019298185633251, 

'tonnage': 100_000 

} 

] 

 

site_mixins = {} 

for data in site_data: 

klass = type('Site', (core.Identifiable, core.Log, core.Locatable, core.HasContainer, core.HasResource), {}) 

kwargs = dict(env=env, name=data['id'], geometry=shapely.geometry.Point(data['lon'], data['lat'])) 

 

ureg = pint.UnitRegistry() 

tonnage = (data['tonnage'] * ureg.metric_ton).to_base_units() 

kwargs['capacity'] = tonnage.magnitude 

 

site = klass(**kwargs) 

site_mixins[data['id']] = site 

return site_mixins 

 

 

# simple test to see if site list is generated correctly 

def test_site_list(available_sites): 

print('') 

for site in available_sites: 

print(site + ': ' + str(available_sites[site])) 

 

 

def test_activity_fill_destination(env, available_equipment, available_sites): 

origin = available_sites['Stockpile'] 

origin.container.put(origin.container.capacity) # fill the origin container 

destination = available_sites['Den Oever'] 

assert destination.container.capacity < origin.container.capacity 

 

kwargs = dict(env=env, name='MyFirstActivity', 

origin=origin, destination=destination, 

loader=available_equipment['Loady McLoader'], mover=available_equipment['EGG123'], 

unloader=available_equipment['Unloady McUnloader']) 

model.Activity(**kwargs) 

 

assert origin.container.level == origin.container.capacity 

assert destination.container.level == 0 

 

env.run() 

print('Simulation completed in: ' + str(datetime.timedelta(seconds=env.now))) 

 

assert origin.container.level == origin.container.capacity - destination.container.capacity 

assert destination.container.level == destination.container.capacity 

 

 

def test_activity_empty_origin(env, available_equipment, available_sites): 

origin = available_sites['Den Oever'] 

origin.container.put(origin.container.capacity) # fill the origin container 

destination = available_sites['Stockpile'] 

assert origin.container.capacity < destination.container.capacity 

 

kwargs = dict(env=env, name='MyFirstActivity', 

origin=origin, destination=destination, 

loader=available_equipment['Loady McLoader'], mover=available_equipment['EGG123'], 

unloader=available_equipment['Unloady McUnloader']) 

model.Activity(**kwargs) 

 

assert origin.container.level == origin.container.capacity 

assert destination.container.level == 0 

 

env.run() 

print('Simulation completed in: ' + str(datetime.timedelta(seconds=env.now))) 

 

assert origin.container.level == 0 

assert destination.container.level == origin.container.capacity 

 

 

def test_activity_hopper(env, available_equipment, available_sites): 

origin = available_sites['Kornwerderzand'] 

origin.container.put(origin.container.capacity) # fill the origin container 

destination = available_sites['Den Oever'] 

assert destination.container.capacity < origin.container.capacity 

 

hopper = available_equipment['Boaty McBoatStone'] 

kwargs = dict(env = env, name='MyHopperActivity', origin=origin, destination=destination, 

loader=hopper, mover=hopper, unloader=hopper) 

model.Activity(**kwargs) 

 

assert origin.container.level == origin.container.capacity 

assert destination.container.level == 0 

 

env.run() 

print('Simulation completed in: ' + str(datetime.timedelta(seconds=env.now))) 

 

assert origin.container.level == origin.container.capacity - destination.container.capacity 

assert destination.container.level == destination.container.capacity