def test_dual_processors(env, geometry_a): # move content from two different limited containers to an unlimited container at the same time limited_container_1 = BasicStorageUnit(env=env, geometry=geometry_a, capacity=1000, level=0, nr_resources=1) limited_container_2 = BasicStorageUnit(env=env, geometry=geometry_a, capacity=1000, level=0, nr_resources=1) unlimited_container = BasicStorageUnit(env=env, geometry=geometry_a, capacity=2000, level=1000, nr_resources=100) processor1 = Processor( env=env, name="Processor 1", loading_func=model.get_loading_func(2), unloading_func=model.get_unloading_func(2), geometry=geometry_a, ) processor2 = Processor( env=env, name="Processor 2", loading_func=model.get_loading_func(1), unloading_func=model.get_unloading_func(1), geometry=geometry_a, ) processor1.ActivityID = "Test activity" processor2.ActivityID = "Test activity" env.process( processor1.process(limited_container_1, 400, unlimited_container)) env.process( processor2.process(limited_container_2, 400, unlimited_container)) env.run() np.testing.assert_almost_equal(env.now, env.epoch + 400) assert unlimited_container.container.level == 200 assert limited_container_1.container.level == 400 assert limited_container_2.container.level == 400 env.process( processor1.process(limited_container_1, 100, unlimited_container)) env.process( processor2.process(limited_container_2, 300, unlimited_container)) start = env.now env.run() time_spent = env.now - start np.testing.assert_almost_equal(time_spent, 150) assert unlimited_container.container.level == 600 assert limited_container_1.container.level == 100 assert limited_container_2.container.level == 300
def test_container_transfer_hub(env, geometry_a, Location, TransportProcessingResource): from_location = Location( env=env, name="From", geometry=geometry_a, capacity=1000, level=1000 ) transfer_location = Location( env=env, name="Transfer", geometry=geometry_a, capacity=500, level=0 ) to_location = Location( env=env, name="To", geometry=geometry_a, capacity=1000, level=0 ) delivery_vessel = TransportProcessingResource( env=env, name="Delivery", geometry=geometry_a, loading_func=model.get_loading_func(1), unloading_func=model.get_unloading_func(1), capacity=100, compute_v=(lambda x: 1), ) collection_vessel = TransportProcessingResource( env=env, name="Collection", geometry=geometry_a, loading_func=model.get_loading_func(2), unloading_func=model.get_unloading_func(2), capacity=100, compute_v=(lambda x: 1), ) model.Activity( env=env, name="Delivery Activity", origin=from_location, destination=transfer_location, loader=delivery_vessel, mover=delivery_vessel, unloader=delivery_vessel, stop_event=env.timeout(3000), ) model.Activity( env=env, name="Collection Activity", origin=transfer_location, destination=to_location, loader=collection_vessel, mover=collection_vessel, unloader=collection_vessel, stop_event=env.timeout(3000), ) env.run() assert from_location.container.level == 0 assert transfer_location.container.level == 0 assert to_location.container.level == 1000
def test_basic_processor(env, geometry_a): # move content from one container to another, then move some of it back again source = BasicStorageUnit( env=env, geometry=geometry_a, capacity=1000, level=1000, nr_resources=1 ) dest = BasicStorageUnit( env=env, geometry=geometry_a, capacity=1000, level=0, nr_resources=1 ) processor = Processor( env=env, loading_func=model.get_loading_func(2), unloading_func=model.get_unloading_func(2), geometry=geometry_a, ) processor.ActivityID = "Test activity" env.process(processor.process(source, 400, dest)) env.run() np.testing.assert_almost_equal(env.now, env.epoch + 300) assert source.container.level == 400 assert dest.container.level == 600 env.process(processor.process(dest, 300, source)) start = env.now env.run() time_spent = env.now - start np.testing.assert_almost_equal(time_spent, 150) assert source.container.level == 700 assert dest.container.level == 300
def test_processor(env, geometry_a, energy_use_sailing, energy_use_loading, energy_use_unloading): source = BasicStorageUnit(env=env, geometry=geometry_a, capacity=1000, level=1000, nr_resources=1) dest = BasicStorageUnit(env=env, geometry=geometry_a, capacity=1000, level=0, nr_resources=1) processor = type( "processor", (core.Processor, core.EnergyUse, core.Locatable, core.Log), {}) data_processor = { "env": env, "unloading_func": model.get_unloading_func(2), "loading_func": model.get_loading_func(2), "geometry": geometry_a, "energy_use_sailing": energy_use_sailing, "energy_use_loading": energy_use_loading, "energy_use_unloading": energy_use_unloading, } processor = processor(**data_processor) processor.ActivityID = "Test activity" # Log fuel use of the processor in step 1 start = env.now env.process(processor.process(source, 400, dest)) env.run() np.testing.assert_almost_equal(processor.log["Value"][-1], (env.now - start) * 4) # Log fuel use of the processor in step 2 start = env.now env.process(processor.process(dest, 300, source)) env.run() np.testing.assert_almost_equal(processor.log["Value"][-1], (env.now - start) * 4)
def test_sequential_activities( env, geometry_a, geometry_b, Location, TransportProcessingResource ): """ Test if activities only start after another one is finished. """ amount = 10_000 # Initialize from site with correct parameters data_from_location = { "env": env, # The simpy environment "name": "Location A", # The name of the "from location" "geometry": geometry_a, # The coordinates of the "from location" "capacity": amount, # The capacity of the "from location" "level": amount, } # The actual volume of the "from location" data_to_location = { "env": env, # The simpy environment "name": "Location B", # The name of the "to location" "geometry": geometry_b, # The coordinates of the "to location" "capacity": amount / 2, # The capacity of the "to location" "level": 0, } # The actual volume of the "to location" from_location = Location(**data_from_location) to_location_1 = Location(**data_to_location) to_location_2 = Location(**data_to_location) # make the vessel data_vessel = { "env": env, # The simpy environment "name": "Vessel", "geometry": geometry_a, # It is located at the "from location" "unloading_func": model.get_unloading_func( 1 ), # Unloading production is 1 amount / s "loading_func": model.get_loading_func(1), # Loading production is 1 amount / s "capacity": 1_000, # Capacity of the vessel "compute_v": (lambda x: 1), } # Speed is always 1 m / s
"capacity": 7_200, # Capacity of the hopper - "Beunvolume" "v": 1, # Speed always 1 m/s "compute_draught": compute_draught(4.0, 7.0), # Variable draught "waves": [0.5, 1], # Waves with specific ukc "ukc": [0.75, 1], # UKC corresponding to the waves "filling": None, } # The filling degree mover = Mover(**data) mover.ActivityID = "Test activity" data = { "env": env, # The simpy environment "name": "Quay Crane", # Name "geometry": geometry_a, # It starts at the "from site" "loading_func": model.get_loading_func(1.0), # Loading rate "unloading_func": model.get_unloading_func(1.0), } # Unloading rate crane = Processor(**data) crane.rate = crane.loading_func crane.ActivityID = "Test activity" # Initialize the LocationWeather data = { "env": env, # The simpy environment defined in the first cel "name": "Limited Location", # The name of the site "geometry": geometry_a, # Location "capacity": 500_000, # The capacity of the site "level": 500_000, # The actual volume of the site "dataframe": weather_data, # The dataframe containing the weather data
"name": "Location B", # The name of the "to location" "geometry": location, # The coordinates of the "to location" "capacity": 1_000, # The capacity of the "to location" "level": 0, } # The actual volume of the "to location" to_site = Location(**data_to_site) # make the processor with source terms data_processor = { "env": env, # The simpy environment "geometry": location, # The coordinates of the "processore" "unloading_func": model.get_unloading_func( 1 ), # Unloading production is 1 amount / s "loading_func": model.get_loading_func(1), } # Loading production is 1 amount / s processor = Processor(**data_processor) processor.ActivityID = "Test activity" # Log fuel use of the processor in step 1 env.process(processor.process(from_site, 0, to_site)) env.run() assert "fines released" in processor.log["Message"] fines = [ processor.log["Value"][i] for i in range(len(processor.log["Value"])) if processor.log["Message"][i] == "fines released" ] assert len(fines) == 2
def test_Processor_ContainerDependentMovable( env, geometry_a, geometry_b, locatable_a, locatable_b, energy_use_sailing, energy_use_loading, energy_use_unloading, ): source = BasicStorageUnit(env=env, geometry=geometry_a, capacity=1000, level=1000, nr_resources=1) dest = BasicStorageUnit(env=env, geometry=geometry_b, capacity=1000, level=0, nr_resources=1) # The generic class for an object that can process (a quay crane for example) ProcessingResource = type( "ProcessingResource", ( core.Identifiable, # Give it a name core.Locatable, # Allow logging of location core.Log, # Allow logging of all discrete events core.Processor, # Allow for loading and unloading core.HasResource, # Add information on serving equipment core.EnergyUse, ), # Add information on fuel {}, ) processor_1 = { "env": env, # The simpy environment "name": "Processor 1", # Name "geometry": geometry_a, # It is located at location A "unloading_func": model.get_unloading_func(1), # Unloading rate "loading_func": model.get_loading_func(2), # Loading rate "energy_use_loading": energy_use_loading, # Variable fuel use "energy_use_sailing": energy_use_sailing, # Variable fuel use "energy_use_unloading": energy_use_loading, } # Variable fuel use processor_2 = { "env": env, # The simpy environment "name": "Processor 2", # Name "geometry": geometry_b, # It is located at location B "unloading_func": model.get_unloading_func(1), # Unloading rate "loading_func": model.get_loading_func(2), # Loading rate "energy_use_loading": energy_use_unloading, # Variable fuel use "energy_use_sailing": energy_use_sailing, # Variable fuel use "energy_use_unloading": energy_use_unloading, } # Variable fuel use # The generic class for an object that can move an amount (a containervessel) mover = type( "Mover", (core.ContainerDependentMovable, core.EnergyUse, core.HasResource, core.Log), {}, ) data_mover = { "compute_v": lambda x: 1, "geometry": geometry_a, "env": env, "capacity": 1000, "energy_use_sailing": energy_use_sailing, "energy_use_loading": energy_use_loading, "energy_use_unloading": energy_use_unloading, } # The simulation objects processor_1 = ProcessingResource(**processor_1) processor_2 = ProcessingResource(**processor_2) containervessel = mover(**data_mover) processor_1.ActivityID = "Test activity" processor_2.ActivityID = "Test activity" containervessel.ActivityID = "Test activity" # Simulation starts with loading start = env.now env.process(processor_1.process(containervessel, 500, source)) env.run() np.testing.assert_almost_equal(processor_1.log["Value"][-1], (env.now - start) * 4) # Simulation continues with moving from A to B start = env.now env.process(containervessel.move(locatable_b)) env.run() np.testing.assert_almost_equal(containervessel.log["Value"][-2], (env.now - start) * 2.5, decimal=5) # Simulation ends with unloading start = env.now env.process(processor_2.process(containervessel, 0, dest)) env.run() np.testing.assert_almost_equal(processor_2.log["Value"][-1], (env.now - start) * 3)
def test_TransportProcessingResource( env, geometry_a, geometry_b, locatable_a, locatable_b, energy_use_sailing, energy_use_loading, energy_use_unloading, ): source = BasicStorageUnit(env=env, geometry=geometry_a, capacity=1000, level=1000, nr_resources=1) dest = BasicStorageUnit(env=env, geometry=geometry_b, capacity=1000, level=0, nr_resources=1) # The generic class for an object that can move and transport (a TSHD for example) TransportProcessingResource = type( "TransportProcessingResource", ( core.Identifiable, # Give it a name core.Log, # Allow logging of all discrete events core. ContainerDependentMovable, # A moving container, so capacity and location core.Processor, # Allow for loading and unloading core.HasResource, # Allow queueing core.EnergyUse, ), # Allow logging energy use {}, ) # TSHD variables data_hopper = { "env": env, # The simpy environment "name": "Hopper", # Name "geometry": geometry_b, # It starts at the "to site" "unloading_func": model.get_unloading_func(1), # Unloading rate "loading_func": model.get_loading_func(2), # Loading rate "capacity": 500, # Capacity of the hopper "compute_v": lambda x: 1, # Variable speed "energy_use_loading": energy_use_loading, # Variable fuel use "energy_use_sailing": energy_use_sailing, # Variable fuel use "energy_use_unloading": energy_use_unloading, } # Variable fuel use # The simulation object hopper = TransportProcessingResource(**data_hopper) hopper.ActivityID = "Test activity" # Simulation starts with moving to A start = env.now env.process(hopper.move(source)) env.run() # moving empty to energy use is 2 per second np.testing.assert_almost_equal(hopper.log["Value"][-2], (env.now - start) * 2, decimal=5) # Simulation continues with loading start = env.now env.process(hopper.process(hopper, 500, source)) env.run() # Duration should be amount / 2 # Energy use duration * 4 np.testing.assert_almost_equal(hopper.log["Value"][-2], (env.now - start) * 4) # Simulation continues with moving from A to B start = env.now env.process(hopper.move(locatable_b)) env.run() # moving full so energy use is 3 per second np.testing.assert_almost_equal(hopper.log["Value"][-2], (env.now - start) * 3, decimal=5) # Simulation ends with unloading hopper.rate = hopper.unloading_func start = env.now env.process(hopper.process(hopper, 0, dest)) env.run() np.testing.assert_almost_equal(hopper.log["Value"][-2], (env.now - start) * 3)
def available_equipment(env): # should be read from database equipment_data = [ { "id": "EGG123", "type": "Side stone dumping vessel", "speed loaded": 6.5, "tonnage": 2601, "lat": 52.94239823421129, "lon": 5.019298185633251, "tags": ["mover"], }, { "id": "Boaty McBoatStone", "type": "Multi purpose support vessel", "speed loaded": 7.0, "tonnage": 1824, "capacity": 10.3, "lat": 52.94239823421129, "lon": 5.019298185633251, "tags": ["loader", "mover"], }, { "id": "Loady McLoader", "type": "Simple Loading Crane", "lat": 52.94239823421129, "lon": 5.019298185633251, "capacity": 13.2, }, { "id": "Unloady McUnloader", "type": "Simple Loading Crane", "lat": 52.94042293840172, "lon": 5.054676856441372, "capacity": 12.1, }, ] type_to_mixins_mapping = { "Side stone dumping vessel": ( core.Identifiable, core.Log, core.ContainerDependentMovable, core.HasResource, core.Locatable, ), "Multi purpose support vessel": ( core.Identifiable, core.Log, core.ContainerDependentMovable, core.Processor, core.HasResource, core.Locatable, ), "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"] = model.get_loading_func(processing_speed.magnitude) kwargs["unloading_func"] = model.get_unloading_func( 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(data["lon"], data["lat"]) 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
def test_dual_processors_with_limit(env, geometry_a): # move content into a limited container, have two process wait for each other to finish unlimited_container_1 = BasicStorageUnit(env=env, geometry=geometry_a, capacity=1000, level=1000, nr_resources=100) unlimited_container_2 = BasicStorageUnit(env=env, geometry=geometry_a, capacity=1000, level=1000, nr_resources=100) unlimited_container_3 = BasicStorageUnit(env=env, geometry=geometry_a, capacity=2000, level=0, nr_resources=100) limited_container = BasicStorageUnit(env=env, geometry=geometry_a, capacity=2000, level=0, nr_resources=1) processor1 = Processor( env=env, name="Processor 1", loading_func=model.get_loading_func(1), unloading_func=model.get_unloading_func(1), geometry=geometry_a, ) processor2 = Processor( env=env, name="Processor 2", loading_func=model.get_loading_func(1), unloading_func=model.get_unloading_func(1), geometry=geometry_a, ) processor1.ActivityID = "Test activity" processor2.ActivityID = "Test activity" env.process( model.single_run_process( processor1, env, unlimited_container_1, limited_container, processor1, unlimited_container_3, processor1, )) env.process( model.single_run_process( processor2, env, unlimited_container_2, limited_container, processor2, unlimited_container_3, processor2, )) env.run() # Simultaneous accessing limited_container, so waiting event of 1000 seconds np.testing.assert_almost_equal(env.now, env.epoch + 3000) assert limited_container.container.level == 2000 assert unlimited_container_1.container.level == 0 assert unlimited_container_2.container.level == 0