Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e .
python -m pip install -e ".[dev]"
- name: Build and test docs
run: |
if [ ${{ github.ref }} == "refs/heads/main" ]; then
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test_package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11']
python-version: ['3.10', '3.11', '3.12', '3.13']

steps:
- uses: actions/checkout@v3
Expand All @@ -25,7 +25,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e .
python -m pip install -e ".[dev]"
- name: Test with pytest
run: |
python -m pytest
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class SimpleLine(Line):

# Wire them with buffers
source.connect_to_output(station=process, capacity=3)
process.connect_to_output(station=process, capacity=2)
process.connect_to_output(station=sink, capacity=2)


line = SimpleLine()
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.2
0.1.0
119 changes: 47 additions & 72 deletions docs/userguide/core_concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,76 +205,51 @@ updated, this is directly visible in the state of the line.

## Parts and Carriers

In `LineFlow`, every part is transported by a carrier as it
moves through the production line. Hence, carriers act as mobile containers: once a
[`Source`][lineflow.simulation.stations.Source] (or a
[`Magazine`][lineflow.simulation.stations.Magazine]) creates a new carrier, it puts a predefined
sequence of initial parts onto the newly created carrier.
At each station the carrier visits, new parts can be added or existing parts removed, typically at
[`Assembly`][lineflow.simulation.stations.Assembly] stations.

Parts can show different behaviors at different stations. For instance, a certain part may require
an additional processing time at a given process or has to statisfy a certain condition before it
can be assembled. These specification are fixed when creating the part at the source and can be
given in a `carrier_spec`. For instance, the following parameter can be given when creating a source
station:

### Carriers
In LineFlow, every individual Part is always transported by a Carrier as it
moves through the production line. Carriers act as mobile containers: once a
Source station creates a new Part (or set of Parts), it places them onto a
Carrier, which then follows the sequence of Buffers and Stations. At each step,
the Carrier carries its load of Parts downstream—whether through processing,
assembly, or inspection—until it reaches a Sink. Because Parts never traverse
the line on their own, all material flow, blocking behavior, and routing logic
hinge on Carrier movement and occupancy.

### Parts

1. **Define Part Specifications**:
- Part specifications are defined as a list of dictionaries, where each dictionary contains the attributes of a part. For example:
```python
part_specs = [
{"attribute1": value1, "attribute2": value2},
{"attribute1": value3, "attribute2": value4},
]
```

2. **Initialize the Source Station**:
- When initializing a [`Source`][lineflow.simulation.stations.Source]
station, pass the part specs list to the part_specs parameter:
```python
source = Source(
name="source_name",
part_specs=part_specs,
# other parameters
)
```

3. **Create Parts**:
- The [`create_parts`][lineflow.simulation.stations.Source.create_parts] method of the
[`Source`][lineflow.simulation.stations.Source] class is responsible for
creating parts based on the part_specs attribute. This method iterates
over each dictionary in the part_specs list and creates a [`Part`][lineflow.simulation.movable_objects.Part] object
for each specification:
```python
def create_parts(self):
parts = []
for part_spec in self.part_specs:
part = Part(
env=self.env,
name=self.name + '_part_' + str(self.part_id),
specs=part_spec,
)
self.part_id += 1
part.create(self.position)
parts.append(part)
return parts
```

4. **Assemble Parts on Carrier**:
- Once the parts are created, they can be assembled onto a carrier using the
[`assemble_parts_on_carrier`][lineflow.simulation.stations.Source.assemble_parts_on_carrier]
method:
```python
def assemble_parts_on_carrier(self, carrier, parts):
for part in parts:
carrier.assemble(part)
```

5. **Derive Actions from Part Specifications**:
- Actions can be derived from the new state of the parts. The [`apply`][lineflow.simulation.stations.Station] method in the [`Station`][lineflow.simulation.stations.Station] class can be used to apply these actions:
```python
def apply(self, actions):
self._derive_actions_from_new_state(actions)
self.state.apply(actions)
```

By following these steps, you can create new parts with specific attributes, initialize them in a source station, and derive actions based on their specifications.
```python
carrier_spec = {
"CarrierA": {
"Part1": {
"P1": {"extra_processing_time": 5},
"A1": {"assembly_condition": 10},
}
"Part2": {
"P1": {"extra_processing_time": 1},
"P2": {"extra_processing_time": 3},
"A1": {"assembly_condition": 8},
}
},
"CarrierB": {
"Part1": {
"A1": {"assembly_condition": 10},
}
"Part2": {
"P2": {"extra_processing_time": 3},
}
"Part3": {
"P1": {"extra_processing_time": 1},
"P2": {"extra_processing_time": 3},
"A1": {"assembly_condition": 8},
}
}
}
```

Here, the source creates (randomly) either a carrier of type `CarrierA` or of `CarrierB`. `CarrierA`
has two parts, `Part1` and `Part2`, each with their own specifications. For instance, `Part1`
consues an additional processing time of 5 time stepts at station `P1` and needs to be assembled at
station `A1` within `10` time steps from its creation. Similarly, `Part2` has a processing time of 1
at `P1`, 3 at `P2`, and an assembly condition of 8 at `A1`.
5 changes: 3 additions & 2 deletions docs/userguide/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class SimpleLine(Line):
Source(
name='Source',
processing_time=5,
unlimited_carriers=True,
buffer_out=buffer,
position=(100, 300),
)
Expand All @@ -30,9 +31,9 @@ from lineflow.simulation import Line, Source, Sink
class SimpleLine(Line):

def build(self):
source = Source(name='Source', processing_time=5, position=(100, 300))
source = Source(name='Source', processing_time=5, position=(100, 300), unlimited_carriers=True)
sink = Sink('Sink', position=(600, 300))
sink.connect_to_output(station=source, capacity=6)
source.connect_to_output(station=sink, capacity=6)
```


Expand Down
3 changes: 2 additions & 1 deletion lineflow/examples/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
from lineflow.examples.worker_assignment import WorkerAssignment #noqa
from lineflow.examples.double_source import DoubleSource #noqa
from lineflow.examples.waiting_time import WaitingTime #noqa
from lineflow.examples.complex_line import ComplexLine #noqa
from lineflow.examples.complex_line import ComplexLine #noqa
from lineflow.examples.part_dependence import PartDependentProcessLine #noqa
10 changes: 7 additions & 3 deletions lineflow/examples/complex_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,13 @@ def build(self):
unlimited_carriers=True,
carrier_capacity=1,
actionable_waiting_time=True,
part_specs=[{
"assembly_condition": self.assembly_condition,
}],
carrier_specs={
'Carrier': {
'Part': {
f'A{i}': {"assembly_condition": self.assembly_condition} for i in range(self.n_assemblies)
}
}
}
)

switch = Switch(
Expand Down
23 changes: 15 additions & 8 deletions lineflow/examples/component_assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,18 @@ def build(self):
'A1',
processing_time=4,
position=(500, 300),
part_specs=[{
"assembly_condition": 40
}],
carrier_specs={
'A': {
'ComponentA': {
'C21': {"assembly_condition": 40},
'C22': {"assembly_condition": 40},
}
}
},
unlimited_carriers=True,
carrier_capacity=1,
)



switch1 = Switch('S1', alternate=True, position=(200, 300),)
switch2 = Switch('S2', alternate=True, position=(300, 300))
switch3 = Switch('S3', alternate=True, position=(580, 300))
Expand Down Expand Up @@ -95,9 +98,13 @@ def build(self):
'A2',
processing_time=5,
position=(600, 400),
part_specs=[{
"assembly_condition": 400
}],
carrier_specs={
'B': {
'Component_B': {
'C5': {"assembly_condition": 400},
}
}
},
unlimited_carriers=True,
carrier_capacity=1,
)
Expand Down
48 changes: 48 additions & 0 deletions lineflow/examples/part_dependence.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from lineflow.simulation import (
WorkerPool,
Source,
Sink,
Line,
Process,
)


class PartDependentProcessLine(Line):
def build(self):
source = Source(
name='Source',
processing_time=10,
position=(100, 200),
unlimited_carriers=True,
carrier_capacity=1,
carrier_min_creation=10,
carrier_specs={
'Type_A': {
'Part1': {
'P1': {'extra_processing_time': 20},
'P2': {'extra_processing_time': 0}
}
},
'Type_B': {
'Part1': {
'P1': {'extra_processing_time': 0},
'P2': {'extra_processing_time': 20}
}
},
},
)

pool = WorkerPool(name='Pool', n_workers=4)

p1 = Process('P1', processing_time=10, position=(350, 200), worker_pool=pool)
p2 = Process('P2', processing_time=10, position=(700, 200), worker_pool=pool)
sink = Sink('Sink', position=(850, 200))

p1.connect_to_input(source, capacity=15)
p2.connect_to_input(p1, capacity=15)
sink.connect_to_input(p2)


if __name__ == '__main__':
line = PartDependentProcessLine(realtime=True, factor=0.8)
line.run(simulation_end=1000, visualize=True)
Loading