Spaces:
Configuration error
Configuration error
Upload 9 files
Browse files- .gitattributes +1 -0
- .gitignore +5 -0
- .gitpod.yml +11 -0
- README.md +0 -11
- app.py +201 -0
- container-optimization.ipynb +132 -0
- container_app_shorter_cropped.gif +3 -0
- requirements.txt +2 -0
- solver.py +50 -0
- viktor.config.toml +1 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
container_app_shorter_cropped.gif filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
venv/
|
| 2 |
+
tests
|
| 3 |
+
CHANGELOG.md
|
| 4 |
+
.gitpod.yml
|
| 5 |
+
__pycache__/
|
.gitpod.yml
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
tasks:
|
| 2 |
+
- name: VIKTOR
|
| 3 |
+
init: |
|
| 4 |
+
curl -Lo viktor-cli 'https://sys.viktor.ai/api/v1/get-cli/?platform=linux&format=binary'
|
| 5 |
+
chmod +x viktor-cli
|
| 6 |
+
mv viktor-cli /workspace/viktor-cli
|
| 7 |
+
command: |
|
| 8 |
+
echo "export PATH=\$PATH:/workspace" >> ~/.bashrc
|
| 9 |
+
source ~/.bashrc
|
| 10 |
+
|
| 11 |
+
|
README.md
CHANGED
|
@@ -1,11 +0,0 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: Containers
|
| 3 |
-
emoji: 🔥
|
| 4 |
-
colorFrom: gray
|
| 5 |
-
colorTo: green
|
| 6 |
-
sdk: docker
|
| 7 |
-
pinned: false
|
| 8 |
-
license: apache-2.0
|
| 9 |
-
---
|
| 10 |
-
|
| 11 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from viktor.core import ViktorController
|
| 2 |
+
from viktor.geometry import SquareBeam, Group, Material
|
| 3 |
+
from viktor.parametrization import ViktorParametrization, Text, IntegerField, OptionField, DynamicArray
|
| 4 |
+
from viktor.views import SVGView, SVGResult, GeometryResult, GeometryView
|
| 5 |
+
from viktor import Color
|
| 6 |
+
from io import StringIO
|
| 7 |
+
from matplotlib.patches import Rectangle
|
| 8 |
+
|
| 9 |
+
from solver import solver
|
| 10 |
+
import pandas as pd
|
| 11 |
+
import numpy as np
|
| 12 |
+
from rectpack import newPacker
|
| 13 |
+
import rectpack.packer as packer
|
| 14 |
+
import matplotlib.pyplot as plt
|
| 15 |
+
import random
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class Parametrization(ViktorParametrization):
|
| 19 |
+
title = Text('# Container Loading Optimization')
|
| 20 |
+
|
| 21 |
+
bin_type = OptionField('What type of container to fill?', options=["20'", "40'"], default="20'", flex=90)
|
| 22 |
+
|
| 23 |
+
# dynamic array with pallet inputs
|
| 24 |
+
array = DynamicArray('Select pallet types and quantities',
|
| 25 |
+
default=[{'pallet_dim': '120 x 80 cm: Standard Euro Pallet', 'pallet_quantity': 1}])
|
| 26 |
+
array.pallet_dim = OptionField('Pallet Type',
|
| 27 |
+
options=['120 x 80 cm: Standard Euro Pallet', '60 x 80 cm: Half Euro Pallet',
|
| 28 |
+
'60 x 40 cm: Display Pallet', '120 x 100 cm: Standard Block Pallet',
|
| 29 |
+
'60 x 100 cm: Half Block Pallet', '60 x 50 cm: HST mini-pallet'], flex=70,
|
| 30 |
+
default='120 x 80 cm: Standard Euro Pallet')
|
| 31 |
+
array.pallet_quantity = IntegerField('Quantity', default=1, flex=25)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class Controller(ViktorController):
|
| 35 |
+
label = 'My Container'
|
| 36 |
+
parametrization = Parametrization(width=35)
|
| 37 |
+
|
| 38 |
+
@SVGView("container", duration_guess=1)
|
| 39 |
+
def create_svg_result(self, params, **kwargs):
|
| 40 |
+
'''This function creates the output view plotting the pallets and container.'''
|
| 41 |
+
|
| 42 |
+
# initialize figure
|
| 43 |
+
fig = plt.figure(figsize=(4, 12))
|
| 44 |
+
|
| 45 |
+
# Set Containers size
|
| 46 |
+
bins20 = [(235, 590)] # 20' container
|
| 47 |
+
bins40 = [(235, 1203)] # 40' container
|
| 48 |
+
# https://www.dsv.com/fr-fr/nos-solutions/modes-de-transport/fret-maritime/dimensions-de-conteneurs-maritimes/dry-container
|
| 49 |
+
# https://www.icontainers.com/help/how-many-pallets-fit-in-a-container/
|
| 50 |
+
|
| 51 |
+
# Set Pallet Dimensions
|
| 52 |
+
bx = 5 # buffer x
|
| 53 |
+
by = 5 # buffer y
|
| 54 |
+
|
| 55 |
+
pal_128 = [120 + bx, 80 + by]
|
| 56 |
+
pal_68 = [60 + bx, 80 + by]
|
| 57 |
+
pal_64 = [60 + bx, 40 + by]
|
| 58 |
+
pal_1210 = [120 + bx, 100 + by]
|
| 59 |
+
pal_610 = [60 + bx, 100 + by]
|
| 60 |
+
pal_65 = [60 + bx, 50 + by]
|
| 61 |
+
|
| 62 |
+
# How many of each pallet type is selected
|
| 63 |
+
n_128, n_68, n_64, n_1210, n_610, n_65 = 0, 0, 0, 0, 0, 0
|
| 64 |
+
|
| 65 |
+
for pallet in params.array:
|
| 66 |
+
if pallet.pallet_dim == '120 x 80 cm: Standard Euro Pallet':
|
| 67 |
+
n_128 += pallet.pallet_quantity
|
| 68 |
+
if pallet.pallet_dim == '60 x 80 cm: Half Euro Pallet':
|
| 69 |
+
n_68 += pallet.pallet_quantity
|
| 70 |
+
if pallet.pallet_dim == '60 x 40 cm: Display Pallet':
|
| 71 |
+
n_64 += pallet.pallet_quantity
|
| 72 |
+
if pallet.pallet_dim == '120 x 100 cm: Standard Block Pallet':
|
| 73 |
+
n_1210 += pallet.pallet_quantity
|
| 74 |
+
if pallet.pallet_dim == '60 x 100 cm: Half Block Pallet':
|
| 75 |
+
n_610 += pallet.pallet_quantity
|
| 76 |
+
if pallet.pallet_dim == '60 x 50 cm: HST mini-pallet':
|
| 77 |
+
n_65 += pallet.pallet_quantity
|
| 78 |
+
|
| 79 |
+
# Plot Container
|
| 80 |
+
if params.bin_type == "20'":
|
| 81 |
+
bin_type = bins20
|
| 82 |
+
plt.plot([0, 235, 235, 0, 0], [0, 0, 590, 590, 0], linewidth=2.5, color="k")
|
| 83 |
+
else:
|
| 84 |
+
bin_type = bins40
|
| 85 |
+
plt.plot([0, 235, 235, 0, 0], [0, 0, 1203, 1203, 0], linewidth=2.5, color="k")
|
| 86 |
+
|
| 87 |
+
# all_rects, all_pals = solver(params.n_812, params.n_1012, bin_type)
|
| 88 |
+
all_rects, all_pals = solver(n_128, n_68, n_64, n_1210, n_610, n_65, bin_type)
|
| 89 |
+
|
| 90 |
+
# Loop all rect
|
| 91 |
+
for rect in all_rects:
|
| 92 |
+
b, x, y, w, h, rid = rect
|
| 93 |
+
# Pallet type colours. If included also add color in plot below.
|
| 94 |
+
if [w, h] == pal_128 or [h, w] == pal_128:
|
| 95 |
+
color = 'pink'
|
| 96 |
+
if [w, h] == pal_68 or [h, w] == pal_68:
|
| 97 |
+
color = 'brown'
|
| 98 |
+
if [w, h] == pal_64 or [h, w] == pal_64:
|
| 99 |
+
color = 'olive'
|
| 100 |
+
if [w, h] == pal_1210 or [h, w] == pal_1210:
|
| 101 |
+
color = 'orange'
|
| 102 |
+
if [w, h] == pal_610 or [h, w] == pal_610:
|
| 103 |
+
color = 'blue'
|
| 104 |
+
if [w, h] == pal_65 or [h, w] == pal_65:
|
| 105 |
+
color = 'purple'
|
| 106 |
+
plt.gca().add_patch(Rectangle((x, y), w, h, facecolor=color, edgecolor='k', fill=True, lw=2))
|
| 107 |
+
|
| 108 |
+
plt.axis('equal')
|
| 109 |
+
plt.axis('off')
|
| 110 |
+
fig.tight_layout()
|
| 111 |
+
|
| 112 |
+
# save figure
|
| 113 |
+
svg_data = StringIO()
|
| 114 |
+
fig.savefig(svg_data, format='svg')
|
| 115 |
+
plt.close()
|
| 116 |
+
|
| 117 |
+
return SVGResult(svg_data)
|
| 118 |
+
|
| 119 |
+
@GeometryView("3D container", duration_guess=1)
|
| 120 |
+
def visualize_container(self, params, **kwargs):
|
| 121 |
+
|
| 122 |
+
# generate container
|
| 123 |
+
length_x = 2.35
|
| 124 |
+
length_z = 2.6
|
| 125 |
+
if params.bin_type == "20'":
|
| 126 |
+
length_y = 5.90
|
| 127 |
+
bin_type = [(235, 590)]
|
| 128 |
+
else:
|
| 129 |
+
length_y = 12.03
|
| 130 |
+
bin_type = [(235, 1203)]
|
| 131 |
+
container = SquareBeam(length_x, length_y, length_z)
|
| 132 |
+
container.material = Material('iron', threejs_opacity=0.5)
|
| 133 |
+
container.translate([(length_x / 2), (length_y / 2), (length_z / 2)])
|
| 134 |
+
|
| 135 |
+
# Set Pallet Dimensions
|
| 136 |
+
bx = 5 # buffer x
|
| 137 |
+
by = 5 # buffer y
|
| 138 |
+
|
| 139 |
+
pal_128 = [120 + bx, 80 + by]
|
| 140 |
+
pal_68 = [60 + bx, 80 + by]
|
| 141 |
+
pal_64 = [60 + bx, 40 + by]
|
| 142 |
+
pal_1210 = [120 + bx, 100 + by]
|
| 143 |
+
pal_610 = [60 + bx, 100 + by]
|
| 144 |
+
pal_65 = [60 + bx, 50 + by]
|
| 145 |
+
|
| 146 |
+
# How many of each pallet type is selected
|
| 147 |
+
n_128, n_68, n_64, n_1210, n_610, n_65 = 0, 0, 0, 0, 0, 0
|
| 148 |
+
|
| 149 |
+
for pallet in params.array:
|
| 150 |
+
if pallet.pallet_dim == '120 x 80 cm: Standard Euro Pallet':
|
| 151 |
+
n_128 += pallet.pallet_quantity
|
| 152 |
+
if pallet.pallet_dim == '60 x 80 cm: Half Euro Pallet':
|
| 153 |
+
n_68 += pallet.pallet_quantity
|
| 154 |
+
if pallet.pallet_dim == '60 x 40 cm: Display Pallet':
|
| 155 |
+
n_64 += pallet.pallet_quantity
|
| 156 |
+
if pallet.pallet_dim == '120 x 100 cm: Standard Block Pallet':
|
| 157 |
+
n_1210 += pallet.pallet_quantity
|
| 158 |
+
if pallet.pallet_dim == '60 x 100 cm: Half Block Pallet':
|
| 159 |
+
n_610 += pallet.pallet_quantity
|
| 160 |
+
if pallet.pallet_dim == '60 x 50 cm: HST mini-pallet':
|
| 161 |
+
n_65 += pallet.pallet_quantity
|
| 162 |
+
|
| 163 |
+
# all_rects, all_pals = solver(params.n_812, params.n_1012, bin_type)
|
| 164 |
+
all_rects, all_pals = solver(n_128, n_68, n_64, n_1210, n_610, n_65, bin_type)
|
| 165 |
+
|
| 166 |
+
pallets = []
|
| 167 |
+
for i, pallet in enumerate(all_rects):
|
| 168 |
+
b, x, y, w, h, rid = pallet
|
| 169 |
+
length_x = w / 100
|
| 170 |
+
length_y = h / 100
|
| 171 |
+
length_z = random.uniform(1, 2) # random pallet heights
|
| 172 |
+
|
| 173 |
+
# create pallet
|
| 174 |
+
pallet_box = SquareBeam(length_x=length_x - 0.1, length_y=length_y - 0.1,
|
| 175 |
+
length_z=length_z) # add 0.1 loose space between pallets
|
| 176 |
+
|
| 177 |
+
# move pallet to right location (defining the center of the pallet)
|
| 178 |
+
pallet_box.translate([(x / 100 + 0.5 * length_x), (y / 100 + 0.5 * length_y), (0.5 * length_z)])
|
| 179 |
+
|
| 180 |
+
# set Material
|
| 181 |
+
if [w, h] == pal_128 or [h, w] == pal_128:
|
| 182 |
+
color = Color(227, 119, 194) # pink
|
| 183 |
+
elif [w, h] == pal_68 or [h, w] == pal_68:
|
| 184 |
+
color = Color(140, 86, 75) # brown
|
| 185 |
+
elif [w, h] == pal_64 or [h, w] == pal_64:
|
| 186 |
+
color = Color(188, 189, 34) # olive
|
| 187 |
+
elif [w, h] == pal_1210 or [h, w] == pal_1210:
|
| 188 |
+
color = Color(255, 127, 14) # orange
|
| 189 |
+
elif [w, h] == pal_610 or [h, w] == pal_610:
|
| 190 |
+
color = Color(31, 119, 180) # blue
|
| 191 |
+
elif [w, h] == pal_65 or [h, w] == pal_65:
|
| 192 |
+
color = Color(148, 103, 189) # purple
|
| 193 |
+
pallet_box.material = Material('plastic', color=color)
|
| 194 |
+
|
| 195 |
+
# add to pallet list
|
| 196 |
+
pallets.append(pallet_box)
|
| 197 |
+
pallets = Group(pallets)
|
| 198 |
+
|
| 199 |
+
container_system = Group([container, pallets])
|
| 200 |
+
|
| 201 |
+
return GeometryResult(container_system)
|
container-optimization.ipynb
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": null,
|
| 6 |
+
"metadata": {},
|
| 7 |
+
"outputs": [],
|
| 8 |
+
"source": [
|
| 9 |
+
"import pandas as pd\n",
|
| 10 |
+
"import numpy as np\n",
|
| 11 |
+
"from rectpack import newPacker\n",
|
| 12 |
+
"import rectpack.packer as packer\n",
|
| 13 |
+
"import matplotlib.pyplot as plt"
|
| 14 |
+
]
|
| 15 |
+
},
|
| 16 |
+
{
|
| 17 |
+
"cell_type": "code",
|
| 18 |
+
"execution_count": null,
|
| 19 |
+
"metadata": {},
|
| 20 |
+
"outputs": [],
|
| 21 |
+
"source": [
|
| 22 |
+
"def plot_solution(all_rects, pal_812, pal_1012):\n",
|
| 23 |
+
" # Plot\n",
|
| 24 |
+
" plt.figure(figsize=(10,10))\n",
|
| 25 |
+
" # Loop all rect\n",
|
| 26 |
+
" for rect in all_rects:\n",
|
| 27 |
+
" b, x, y, w, h, rid = rect\n",
|
| 28 |
+
" x1, x2, x3, x4, x5 = x, x+w, x+w, x, x\n",
|
| 29 |
+
" y1, y2, y3, y4, y5 = y, y, y+h, y+h,y\n",
|
| 30 |
+
"\n",
|
| 31 |
+
" # Pallet type\n",
|
| 32 |
+
" if [w, h] == pal_812:\n",
|
| 33 |
+
" color = '--k'\n",
|
| 34 |
+
" else:\n",
|
| 35 |
+
" color = '--r'\n",
|
| 36 |
+
"\n",
|
| 37 |
+
" plt.plot([x1,x2,x3,x4,x5],[y1,y2,y3,y4,y5], color)\n",
|
| 38 |
+
" \n",
|
| 39 |
+
" plt.show()"
|
| 40 |
+
]
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"cell_type": "code",
|
| 44 |
+
"execution_count": null,
|
| 45 |
+
"metadata": {},
|
| 46 |
+
"outputs": [],
|
| 47 |
+
"source": [
|
| 48 |
+
"# Function Solver\n",
|
| 49 |
+
"def solver(n_812, n_1012, bins):\n",
|
| 50 |
+
" # Pallets to load\n",
|
| 51 |
+
" rectangles = [pal_812 for i in range(n_812)] + [pal_1012 for i in range(n_1012)]\n",
|
| 52 |
+
" \n",
|
| 53 |
+
" # Build the Packer\n",
|
| 54 |
+
" pack = newPacker(mode = packer.PackingMode.Offline, bin_algo = packer.PackingBin.Global,\n",
|
| 55 |
+
" rotation=True)\n",
|
| 56 |
+
"\n",
|
| 57 |
+
" # Add the rectangles to packing queue\n",
|
| 58 |
+
" for r in rectangles:\n",
|
| 59 |
+
" pack.add_rect(*r)\n",
|
| 60 |
+
"\n",
|
| 61 |
+
" # Add the bins where the rectangles will be placed\n",
|
| 62 |
+
" for b in bins:\n",
|
| 63 |
+
" pack.add_bin(*b)\n",
|
| 64 |
+
"\n",
|
| 65 |
+
" # Start packing\n",
|
| 66 |
+
" pack.pack()\n",
|
| 67 |
+
" \n",
|
| 68 |
+
" # Full rectangle list\n",
|
| 69 |
+
" all_rects = pack.rect_list()\n",
|
| 70 |
+
"\n",
|
| 71 |
+
" # Pallets with dimensions\n",
|
| 72 |
+
" all_pals = [sorted([p[3], p[4]]) for p in all_rects]\n",
|
| 73 |
+
"\n",
|
| 74 |
+
" # Count number of 80 x 120 \n",
|
| 75 |
+
" p_812, p_1012 = all_pals.count(pal_812), all_pals.count(pal_1012)\n",
|
| 76 |
+
" print(\"{:,}/{:,} Pallets 80 x 120 (cm) | {:,}/{:,} Pallets 100 x 120 (cm)\".format(p_812, n_812, p_1012, n_1012))\n",
|
| 77 |
+
" \n",
|
| 78 |
+
" return all_rects, all_pals"
|
| 79 |
+
]
|
| 80 |
+
},
|
| 81 |
+
{
|
| 82 |
+
"cell_type": "code",
|
| 83 |
+
"execution_count": null,
|
| 84 |
+
"metadata": {},
|
| 85 |
+
"outputs": [],
|
| 86 |
+
"source": [
|
| 87 |
+
"# Pallets Count\n",
|
| 88 |
+
"#-- 80 x 120 cm\n",
|
| 89 |
+
"\n",
|
| 90 |
+
"bx = 0\n",
|
| 91 |
+
"by = 0 \n",
|
| 92 |
+
"pal_812 = [80 + bx, 120 + by]\n",
|
| 93 |
+
"#-- 100 x 120 cm\n",
|
| 94 |
+
"bx = 0\n",
|
| 95 |
+
"by = 0\n",
|
| 96 |
+
"pal_1012 = [100 + bx, 120 + by]\n",
|
| 97 |
+
"\n",
|
| 98 |
+
"# Number of pallets\n",
|
| 99 |
+
"n_812 = 14\n",
|
| 100 |
+
"n_1012 = 0 # 100 x 120 cm\n",
|
| 101 |
+
"rectangles = [pal_812 for i in range(n_812)] + [pal_1012 for i in range(n_1012)]\n",
|
| 102 |
+
"\n",
|
| 103 |
+
"# Container size\n",
|
| 104 |
+
"bins20 = [(236, 595)] \n",
|
| 105 |
+
"bins40 = [(236, 1203.396)] \n",
|
| 106 |
+
"# https://www.dsv.com/fr-fr/nos-solutions/modes-de-transport/fret-maritime/dimensions-de-conteneurs-maritimes/dry-container\n",
|
| 107 |
+
"# https://www.icontainers.com/help/how-many-pallets-fit-in-a-container/"
|
| 108 |
+
]
|
| 109 |
+
},
|
| 110 |
+
{
|
| 111 |
+
"cell_type": "code",
|
| 112 |
+
"execution_count": null,
|
| 113 |
+
"metadata": {},
|
| 114 |
+
"outputs": [],
|
| 115 |
+
"source": [
|
| 116 |
+
"# Test 1\n",
|
| 117 |
+
"n_812 = 8\n",
|
| 118 |
+
"n_1012 = 2\n",
|
| 119 |
+
"all_rects, all_pals = solver(n_812, n_1012, bins20)\n",
|
| 120 |
+
"plot_solution(all_rects, pal_812, pal_1012)"
|
| 121 |
+
]
|
| 122 |
+
}
|
| 123 |
+
],
|
| 124 |
+
"metadata": {
|
| 125 |
+
"language_info": {
|
| 126 |
+
"name": "python"
|
| 127 |
+
},
|
| 128 |
+
"orig_nbformat": 4
|
| 129 |
+
},
|
| 130 |
+
"nbformat": 4,
|
| 131 |
+
"nbformat_minor": 2
|
| 132 |
+
}
|
container_app_shorter_cropped.gif
ADDED
|
Git LFS Details
|
requirements.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
viktor==13.5.1
|
| 2 |
+
rectpack
|
solver.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import numpy as np
|
| 4 |
+
from rectpack import newPacker
|
| 5 |
+
import rectpack.packer as packer
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
# Function Solver
|
| 10 |
+
def solver(n_128, n_68, n_64, n_1210, n_610, n_65, bins):
|
| 11 |
+
''' For optimizing how pallets should be placed in the container. The inputs are the quantities of each pallet type and the container size.'''
|
| 12 |
+
|
| 13 |
+
# Set Pallet Buffer
|
| 14 |
+
bx = 5 # buffer x
|
| 15 |
+
by = 5 # buffer y
|
| 16 |
+
pal_128 = [120 + bx, 80 + by]
|
| 17 |
+
pal_68 = [60 + bx, 80 + by]
|
| 18 |
+
pal_64 = [60 + bx, 40 + by]
|
| 19 |
+
pal_1210 = [120 + bx, 100 + by]
|
| 20 |
+
pal_610 = [60 + bx, 100 + by]
|
| 21 |
+
pal_65 = [60 + bx, 50 + by]
|
| 22 |
+
|
| 23 |
+
# Create Rectangles / Pallets to load
|
| 24 |
+
rectangles =[pal_128 for i in range(n_128)] + [pal_68 for i in range(n_68)] + [pal_64 for i in range(n_64)] +\
|
| 25 |
+
[pal_1210 for i in range(n_1210)] + [pal_610 for i in range(n_610)] + [pal_65 for i in range(n_65)]
|
| 26 |
+
|
| 27 |
+
# Build the Packer
|
| 28 |
+
pack = newPacker(mode = packer.PackingMode.Offline, bin_algo = packer.PackingBin.Global,
|
| 29 |
+
rotation=True)
|
| 30 |
+
|
| 31 |
+
# Add the rectangles to packing queue
|
| 32 |
+
for r in rectangles:
|
| 33 |
+
pack.add_rect(*r)
|
| 34 |
+
|
| 35 |
+
# Add the bins where the rectangles will be placed
|
| 36 |
+
for b in bins:
|
| 37 |
+
pack.add_bin(*b)
|
| 38 |
+
|
| 39 |
+
# Start packing
|
| 40 |
+
pack.pack()
|
| 41 |
+
|
| 42 |
+
# Full rectangle list
|
| 43 |
+
all_rects = pack.rect_list()
|
| 44 |
+
|
| 45 |
+
# Pallets with dimensions
|
| 46 |
+
all_pals = [sorted([p[3], p[4]]) for p in all_rects]
|
| 47 |
+
|
| 48 |
+
return all_rects, all_pals
|
| 49 |
+
|
| 50 |
+
|
viktor.config.toml
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
app_type = 'editor'
|