Sustainable Supply Chain Optimizer
- Official service title: Sustainable Supply Chain Optimizer
- Provider: LMS, Laboratory for Manufacturing Systems and Automation, University of Patras
- Project: EnerTEF, industrial (IND) testing and experimentation facility (TEF)
This page describes an optimization service, not a trained machine learning model. There are no model weights to download.
The service is a decision-support tool for multi-stage production chains. It plans how materials flow through the chain and when each step happens, and its plans respect physical limits such as resource capacity and stock. It works in two stages:
- A planning model looks at orders, products, routes, resources and stock. It decides which route and which resource handle each step, and in what quantities, at the lowest cost.
- A scheduling method then turns that plan into a time-indexed schedule for each resource: when each step starts and ends.
Both stages save energy at the steps of the chain. When the plan picks routes and resources, it counts both their running cost and the material they waste, and the schedule does not leave resources idle while work is waiting. How the service saves energy explains each point.
Transport between sites is planned with the same building blocks as production: a transport leg is a step that moves a product from one site to the next, run by a transport resource such as a truck, a train or a van. The captured example plans a three-site network this way; see How the service saves energy, part 5.
This repository holds this page, an example captured from one real run of the service, and a short Python script that summarizes the example.
The optimization service itself is not in this repository and is not covered by its license. It is a hosted service of LMS, University of Patras.
What problem it solves
Products are made in stages: raw materials become intermediate products, which become finished products. Each product can be made through one or more routes. (The service's application programming interface, or API, calls a route a "process plan".) A route is a chain of jobs, and each job is a set of tasks. A task can often run on one of several resources. (The API calls a resource a "machine".) Each resource has its own speed, cost per hour and waste rate. Resources only work during their shifts, and materials are limited to the stock on hand.
For a planning window, for example two or three weeks, the service answers three questions:
- Which route makes each ordered quantity, and how much material flows through each step?
- Which resource runs each step, and for how long?
- When does each step start and end on its resource?
Stage 1 answers the first two questions at the lowest cost. Stage 2 answers the third.
In the captured example
One order asks for 200 t of a finished product at a customer site, Site-C. The raw material starts at Site-A. It can travel to the plant at Site-B by truck or by train. At the plant it is made into a semi-finished product on one of two production lines, then finished. The finished product goes to Site-C either directly by truck or through a hub: by train to the hub, then by van.
- Stage 1 chose the train for the first leg, the production line that loses less, and the route through the hub. It returned
OPTIMAL, with a cost of 154.40 per tonne delivered and a profit of 49,120.07. The direct route made nothing. - Stage 2 put the five steps of the hub route on their resources. It kept them in chain order and worked around each resource's non-working periods. The work runs from 1 September 08:00 to 14 September 14:54, in a planning window of 1 to 18 September 2026.
All names and numbers are invented for the example. See Example for the files.
How the service saves energy
The service cuts energy use along the production chain in five ways. Parts 1 to 4 work at the steps of the chain. Part 5 covers transport between sites, which the service plans as ordinary steps. The energy each resource uses per step is not yet a separate input (see Limitations).
1. Cheaper resource time (stage 1)
The planning model looks for the plan with the lowest total cost. Part of that cost is resource time: for every task, the hours on its resource multiplied by that resource's cost per hour. The hours follow from the resource's speed, so a faster resource needs fewer hours for the same work.
If each resource's cost per hour includes its energy cost, the model weighs energy in every choice it makes: which route makes each product, and which resource runs each step. A resource that uses more energy then costs more per hour, and the model uses it only where that pays off.
In the captured example, the first leg can run on a truck or on a train. The truck costs 90 per hour and moves 4 t per hour. The train costs 450 per hour and moves 25 t per hour. Per tonne the train costs less, 18 against 22.5, so the model chose it although its hourly cost is five times the truck's.
2. Less material waste along the chain (stage 1)
Each resource has a waste rate for each task it can run: the share of the task's input that is lost. In a multi-stage chain, a loss at one step is paid for at that step and at every step before it: they all have to process extra material to make up for it, and the chain needs more raw material. The model balances material through all the stages of a route at once, so it counts that whole extra cost and favors the routes and resources that lose less. Material that ends up as scrap has already used energy at every step it went through, so losing less of it saves that energy.
In the captured example, the semi-finished product can be made on two lines with the same speed. Line-1 costs 100 per hour and loses 6% of its input. Line-2 costs 125 per hour and loses 1.5%. The model chose Line-2. Every tonne lost on the line has to be bought at the source and carried to the plant again. That costs more than Line-2's higher rate per hour.
3. No idle resources while work is waiting (stage 2)
The scheduling heuristic builds the timetable one decision at a time. Each time a resource becomes free, it looks at the tasks that are ready to start. It only considers options in which no free resource is left empty while a ready task it can run is still waiting. A resource left on with nothing to do still uses energy. The only idle gaps the heuristic leaves are those where nothing is ready for the resource, for example while the previous step of the chain is still running on another resource.
In the captured schedule, every step after the first starts the moment the step before it finishes.
4. Fewer set-ups and fewer interrupted steps (stage 2)
When the heuristic has a choice between options, it compares them by an estimate of how long they keep the resources occupied. The estimate adds up three times, and the lowest total wins:
- Processing time: when a task can run on more than one resource (possible when you call stage 2 directly), the faster resource scores better.
- Set-up time: the changeover a resource needs between two tasks, read from a set-up matrix in the stage 2 input. A resource being set up uses energy but makes nothing, so the heuristic favors task orders with fewer and shorter changeovers.
- Down time: the part of a resource's non-working periods, from its shift calendar, that falls inside a task. The step stops at the end of one shift and resumes in the next, and the heuristic prefers orders with less of this paused time.
When only one task is ready for a free resource, there is nothing to choose: the heuristic starts it at once, even if a shift end will interrupt it. The captured example is a single chain, so that is always the case there. The example data has no set-up times.
5. Transport between sites (stage 1)
Transport between sites is planned with the same building blocks as production, and the captured example shows how. Each site holds its own copy of a product: "P1@Site-A" and "P1@Site-B" are two products. A leg is a task that consumes the product at one site and produces it at the next. It runs on a transport resource, such as a truck, a train or a van. Like any resource, that resource has a rate in tonnes per operating hour, a cost per operating hour and a loss rate. Choosing a transport mode means choosing a resource. Choosing a path, direct or through a hub, means choosing a route.
In the example, the plan carries the raw material by train rather than by truck, and delivers through the hub, by train and then van, rather than by direct truck. Its transport resources work 41.38 hours in total. We also gave the service the same input without the train on the first leg and without the hub route. In the plan it returned for this road-only variant, the transport resources work 152.29 hours. That plan also carries more raw material from the source: 207.16 t, against 206.54 t in the chosen plan. Both plans are in the example. The plan picks the train and the hub because they cost less per tonne in the example data. The model looks for the lowest cost, not the fewest hours or the least energy. In this example the cheaper options also need fewer hours.
If a transport resource uses a steady amount of fuel or electricity per operating hour, fewer operating hours mean less energy. The service does not calculate energy itself.
How it works
Stage 1 uses mixed-integer programming (MIP). Stage 2 uses a scheduling heuristic called IMPACT.
orders, products, routes, resources
|
v
Stage 1: MIP plan -> [service converts the plan] -> Stage 2: IMPACT heuristic -> timed schedule
Stage 1: a plan from a MIP model
MIP is a mathematical method for finding the best plan when some decisions are yes-or-no choices (for example, "does resource 5 run this task?") and others are amounts (for example, "how many units go through this route?"). A program called a MIP solver searches for the best plan and can prove that it is the best.
The model looks at the whole planning window at once. It decides how much of each order goes through each route, which resource runs each task, and how long each task takes. The material flows, waste and costs follow from those choices. The model has no time axis: a resource's capacity is simply its total working hours in the window. The exact rules are in Objective and constraints.
Between the stages (inside the service)
The service turns the plan into job orders and task orders, and builds the stage 2 input from them:
- It creates one task order for each task of each route the plan uses.
- Each task order gets one resource, the one the MIP chose, and the task duration from the MIP (converted to seconds and rounded down).
- Each resource gets its non-working periods for the window, taken from its shift calendar.
- Precedence constraints link tasks of the same job. A precedence constraint says that one task must finish before another can start.
- Every job gets the start of the window as its arrival date and the end of the window as its due date.
This conversion uses data stored inside the service, such as the resources' shift calendars, and the service does not offer it as an endpoint. For that reason the example includes the stage 2 input that the service built itself in the same run.
Stage 2: a timetable from the IMPACT heuristic
A heuristic is a method that finds a good answer quickly by following rules, without proving that the answer is the best possible one. IMPACT is the name of a multi-criteria dispatching heuristic developed at LMS. "Dispatching" means it builds the schedule one decision at a time, each time choosing which waiting task to start next on a free resource.
At each decision point, IMPACT:
- generates alternative assignments of waiting tasks to free resources, never leaving a free resource empty while a waiting task could run on it;
- for each alternative, simulates a few further decisions along randomly sampled continuations;
- scores each alternative and keeps the best one.
The service scores alternatives with a single, time-based criterion: an estimate of the processing time, set-up time and down time of the sampled continuations, where down time is non-working time that falls inside a task. Lower is better. How the service saves energy explains what this means for energy.
The continuations are random, so two runs on the same input can return different schedules. Both respect the same constraints.
In the service's own pipeline each task arrives with one resource already chosen, so the heuristic decides order and timing, not resource choice. The input format allows several candidate resources per task. If you call stage 2 directly with several, the heuristic also chooses among them.
Example
The example/ folder holds one linked run, captured from the service. For it we set up a small transport network in a deployment of the service, ran it once and removed it again. All names and numbers are invented. The four files of that run are in JSON, a common plain-text format for structured data:
| File | What it is |
|---|---|
stage1_mip_input.json |
The stage 1 input that the service built from the example network and order |
stage1_mip_output.json |
The plan that stage 1 returned in that run |
stage2_heuristic_input.json |
The stage 2 input that the service built from that plan, in the same run |
stage2_heuristic_output.json |
The schedule that stage 2 produced in that run |
Nobody outside the service computed the stage 2 input. The service built it from the stage 1 plan, and that conversion is not exposed. Because the heuristic samples at random, a new run on the same stage 2 input can give a different schedule.
The folder also holds a road-only variant: the same stage 1 input without the train on the first leg and without the hub route (variant_road_only_stage1_input.json), and the plan the service returned for it (variant_road_only_stage1_output.json). names.json maps the ids in the files to the names of the products, resources, routes and steps. That file is ours, not service output. example/README.md describes the network, explains how the files link and lists the one label that was changed.
Run the example
The offline mode needs Python 3.8 or later and no extra packages:
python run_example.py
It prints a short summary of both stages: the result status, the objective (profit), the resource hours, the number of tasks and assignments, and the first few assignments.
Live access
Live access to the hosted service is available on request through the EnerTEF project or LMS, University of Patras (https://lms.mech.upatras.gr). If the request is approved, you receive an account and instructions for getting an access token. Accounts and tokens of the production scheduling service do not work here; each service has its own sign-in.
With a token, the script can send the example to the hosted service. The live mode also needs the requests package. Three optional settings tune the stage 2 search: --dh (decision horizon, DH), --mna (maximum number of alternatives, MNA) and --sr (sampling rate, SR). Heuristic parameters explains them.
python -m pip install requests
export TEF_IND_ACCESS_TOKEN='<your token>' # PowerShell: $env:TEF_IND_ACCESS_TOKEN = '<your token>'
python run_example.py --live # optional: --dh 2 --mna 100 --sr 10
The script sends the stage 1 input and prints the plan summary. Then it sends the stage 2 input and prints the schedule summary. Stage 2 uses the example's stage 2 input, not one built from the new plan, because the conversion runs inside the service. The script reads the token only from the TEF_IND_ACCESS_TOKEN environment variable, never from the command line. Treat the token like a password: do not share it and do not commit it to a repository.
The same calls with curl:
curl -X POST https://api.scm.tef-ind.eu/api/scheduling/planFromJSONNODB \
-H "Authorization: Bearer $TEF_IND_ACCESS_TOKEN" -H "Content-Type: application/json" \
--data-binary @example/stage1_mip_input.json
curl -X POST "https://api.scm.tef-ind.eu/api/scheduling/scheduleFromJSONNODB?dh=2&mna=100&sr=10" \
-H "Authorization: Bearer $TEF_IND_ACCESS_TOKEN" \
-F "file=@example/stage2_heuristic_input.json;type=application/json"
Limits of the hosted service
The service answers over HTTP (Hypertext Transfer Protocol, the protocol of the web) and reports problems as standard HTTP status codes, such as 403 or 429.
- Your account must be allowed to run optimizations. Accounts that sign in through the EnerTEF federation start read-only. They get HTTP 403 from these endpoints until access is granted.
- The service runs one optimization at a time, across all its users and features. If another one is running, you get HTTP 429 with
Retry-After: 30and the message "Another optimization is already running. Please try again later." Requests are not queued. Wait and try again later. Do not retry in a tight loop. - Keep inputs small. Each input may be at most 2 MB (megabytes).
- A run has no time limit of its own, but the gateway in front of the service gives up after about 60 seconds and answers HTTP 504. The run then continues on the server and keeps the optimization slot, so other requests get 429 until it ends. The example instance finishes well within this limit.
- The two endpoints do not read or change the data stored in the service. The planning component does write each stage 1 input, and the plan when one is found, to its process log on the server. Do not send confidential or personal data.
Limitations
- No built-in notion of suppliers, locations, distances, transport modes, lead times, emissions or energy use. The captured example represents places as separate products and legs as steps on transport resources. That is a modeling convention. The service knows rates and costs per hour, not distances, trip sizes, departures, timetables or empty runs. Transport flows continuously: there are no vehicle loads or trips.
- There is one objective: cost (profit, with demand fixed). There is no trade-off with emissions or service level.
- It does not return inventory levels over time or delivery schedules.
- Due dates and priorities are not used. Orders carry quantities and prices only. Every job gets the whole planning window as its arrival-to-due interval.
- A route with several jobs, where an earlier job makes an intermediate product for a later one, fails to solve today. Put the whole chain in one job, as the captured example does. Precedence between jobs would not reach stage 2 anyway: the stage 2 input links only tasks of the same job.
- In the pipeline each task has one resource. Stage 2 orders and times the MIP's choices; it does not revisit them.
- Stage 2 is randomized. Repeated runs can differ.
- Stage 1 has no time axis. Capacity is the total hours in the window. Whether the work also fits the shift pattern only shows up in stage 2.
- Infeasibility is not explained. An infeasible instance (one with no valid plan) comes back as
INFEASIBLEwith empty results. The service does not say which constraint failed. - Input checks are basic. Missing, unknown and
nullfields get precise messages. Deeper inconsistencies, such as a missing waste rate, get a generic HTTP 400. Some inconsistent inputs returnINFEASIBLE,UNKNOWNor an empty schedule instead of an error. - Runs have no time limit (see Limits of the hosted service).
- The example is tiny. It shows the formats and the method. It is not a benchmark.
Intended use
- Trying cost-optimal planning and scheduling of multi-stage material flow on small instances.
- Learning the input and output formats before connecting to the hosted service.
- Experiments within EnerTEF.
Out of scope: supplier selection, vehicle routing, transport timetables, fleet sizing and emissions planning (not modeled), supply chain decisions without an agreement with LMS, large benchmarks, confidential or personal data, and safety-critical use.
Technical reference
This section is for developers who want to build their own inputs.
Stage 1 input: POST /api/scheduling/planFromJSONNODB
A JSON object (Content-Type: application/json, at most 2 MB). All 26 fields are required and must not be null. Empty lists and objects are allowed. All IDs (identifiers) are strings. Use one quantity unit per product and one currency throughout (the example uses metric tons). In the field names, "machine" means any resource with limited capacity, and "process plan" means a route.
| Field | Meaning |
|---|---|
order_ids |
Orders to plan |
product_ids |
Every product involved: ordered products, intermediates and raw materials |
order_products |
Order β products it asks for |
product_quantity |
Order β product β quantity to deliver (must be above zero) |
product_selling_price_per_unit |
Order β product β selling price per unit |
product_cost_per_unit |
Product β cost per unit (what a raw material costs to buy) |
product_stock |
Product β stock at the start of the window |
product_process_plans |
Product β routes that can make it |
process_plan_jobs |
Route β its jobs |
job_tasks |
Job β its tasks |
task_predecessors, task_successors |
Task β tasks right before / after it |
task_material_flow_ratio |
Task β next task β share of the next task's input that comes from this task |
task_consumes, task_consumes_qty |
Task β materials it consumes, and consumption per unit of task input |
task_produces, task_produces_qty |
Task β products it makes, and production per unit of task output |
job_consumes, job_produces |
Job β materials it consumes / products it makes |
job_successors |
Job β later job β products passed between them |
machine_ids |
Resources |
machine_capacity |
Resource β working hours available in the window (must be above zero) |
machine_cost_per_hour |
Resource β cost per working hour |
task_machines |
Task β resources that can run it |
machine_nominal_rate |
Resource β task β units of task input processed per hour |
machine_waste_rate |
Resource β task β fraction of task input lost as waste |
Every pair in task_machines needs a nominal rate and a waste rate, and every product in product_ids needs a stock and a cost. If one is missing, the solve fails and the service answers HTTP 400 with "The planning service could not solve this instance." Every product in product_ids must also be consumed or produced by a task of one of the ordered products' routes. Otherwise the solve fails too.
The smallest valid instance has one order, one task and one resource. The service returns OPTIMAL with objective 490 for it:
{"order_ids":["1"],"product_ids":["10","20"],"order_products":{"1":["10"]},"machine_ids":["100"],
"machine_capacity":{"100":40.0},"machine_cost_per_hour":{"100":50.0},"product_stock":{"10":0.0,"20":1000.0},
"product_cost_per_unit":{"10":5.0,"20":1.0},"product_selling_price_per_unit":{"1":{"10":100.0}},
"product_quantity":{"1":{"10":10.0}},"product_process_plans":{"10":["1000"]},"process_plan_jobs":{"1000":["2000"]},
"job_tasks":{"2000":["3000"]},"task_machines":{"3000":["100"]},"task_consumes":{"3000":["20"]},
"job_consumes":{"2000":["20"]},"task_produces":{"3000":["10"]},"job_produces":{"2000":["10"]},
"task_predecessors":{},"task_successors":{},"job_successors":{"2000":{}},
"task_consumes_qty":{"3000":{"20":1.0}},"task_produces_qty":{"3000":{"10":1.0}},"task_material_flow_ratio":{},
"machine_waste_rate":{"100":{"3000":0.0}},"machine_nominal_rate":{"100":{"3000":1.0}}}
Stage 1 output
feasible is OPTIMAL, INFEASIBLE, OTHER or UNKNOWN. objective_value is the profit. It is 0 when the status is not OPTIMAL. An infeasible instance still returns HTTP 200, with empty result maps.
There are 17 result maps. Apart from machine_usage, they are nested by order β product β route β job β task, as deep as each one needs:
| Field | Keyed by | Content |
|---|---|---|
process_plan_production |
order, product, route | Quantity made through each route |
task_assignment |
..., task, resource | true for the resource chosen |
task_duration |
..., task | Duration in hours |
task_total_consumption, task_total_production, task_waste |
..., task | Quantity into, out of, and lost in the task |
task_consumes, task_produces |
..., task, product | Quantity of each material or product |
tasks_flow |
..., task, next task | Quantity passed on |
job_consumes, job_produces |
..., job, product | Quantity of each material or product |
task_total_cost, job_total_cost, process_plan_total_cost |
..., task / job / route | Cost |
process_plan_cost_per_unit |
order, product, route | Cost per unit made |
product_cost_per_unit |
order, product | Cost per unit delivered |
machine_usage |
resource | Hours used |
Every route of an ordered product appears. Routes the solution does not use show zero quantities.
Objective and constraints (stage 1)
The objective is what the model tries to maximize. Here it is profit: for every ordered item, quantity Γ (selling price β production cost per unit). The ordered quantities are fixed, so in practice the model finds the lowest total cost: resource hours Γ cost per hour, plus purchased material Γ cost per unit. There is no separate emissions or energy term; energy cost can be included in the cost per hour.
The model decides how much of each ordered quantity goes through each route; one resource per task; each task's input, output, waste and duration; the flows between tasks; and each resource's working hours.
The constraints, in plain words:
- Demand: for each order and product, the quantities made through its routes add up exactly to the ordered quantity.
- One resource per task: each task runs on exactly one of its eligible resources.
- Duration: task input = duration Γ the chosen resource's nominal rate.
- Capacity: the hours used on each resource stay within its available hours in the window.
- Waste: waste = the chosen resource's waste rate Γ task input. Task output = input β waste.
- Material balance within a job: task input = consumed materials + flow from preceding tasks. Task output = flow to following tasks + products made. Consumption, production and flows follow the given ratios.
- Between jobs: what one job passes on equals what the next job takes in.
- Stock: total consumption of each material stays within what the plan produces of it plus the starting stock.
- Costs: task cost = duration Γ resource cost per hour + consumed materials Γ their cost. Job and route costs add up. Cost per unit = route cost Γ· quantity.
Some constraints multiply two decisions together (for example, duration Γ resource choice). This makes the model non-convex, a harder class of problem than a plain linear model. A MIP solver solves it. OPTIMAL means the solver proved that the plan is optimal. There is no time limit.
Stage 2 input: POST /api/scheduling/scheduleFromJSONNODB
multipart/form-data with a part named file that holds the stage 2 input as JSON (at most 2 MB). The optional integer parameters dh, mna and sr go in the query string or as form fields (see Heuristic parameters).
The stage 2 input is the service's internal format. The easiest start is example/stage2_heuristic_input.json: keep its structure and change the values. Unknown keys are rejected.
| Key | Content |
|---|---|
planStartDateYear, planStartDateMonth, planStartDateDay (and planEndDate...) |
Planning window, as strings |
continueAssignmentsAfterPlanEndDate |
"false" in the service's pipeline: tasks may not run past the window |
workcenters.workcenter[] |
One workcenter with algorithm "MULTICRITERIA" and a workcenterresourcereference[].refid per resource |
resources.resource[] |
id, name, non-working periods under resourceavailability.nonworkingperiods.period[] (fromdate, todate), optional setupmatrixreference |
jobs.job[] |
id, name, arrivaldate, duedate, jobworkcenterreference.refid, jobtaskreference[].refid |
tasks.task[] |
id, name |
tasksuitableresources.tasksuitableresource[] |
taskreference.refid, resourcereference.refid, operationtimeperbatchinseconds, setupcode |
taskprecedenceconstraints.taskprecedenceconstraint[] |
preconditiontaskreference.refid finishes before postconditiontaskreference.refid starts |
setupmatrices.setupmatrix[] |
id and setup[] entries with fromcode, tocode, timeinseconds |
A resource's setupmatrixreference.refid must equal the id of a set-up matrix, or its set-up times are ignored.
Stage 2 output
{
"message": "Planning simulation executed successfully from uploaded JSON file.",
"dh": 2, "mna": 100, "sr": 10,
"assignmentCount": 5,
"schedule": {
"id": "planning output",
"assignments": {"assignment": [
{"task": {"id": "_31"}, "resource": {"id": "_4"},
"timeofdispatch": {"year": 2026, "month": 9, "day": 1, "hour": 8, "minutes": 0, "seconds": 0},
"durationinmilliseconds": 16333000}
]}
}
}
The values above only show the shape. Each assignment gives the task, its resource, its start time (timeofdispatch) and its duration. Task and resource IDs are exactly the ones in the input. Times are plain clock times in the same frame as the input dates; no time zone is attached. If assignmentCount is lower than the number of tasks, some tasks did not fit in the window.
Heuristic parameters (stage 2)
| Parameter | Name | What it controls | Default | Allowed |
|---|---|---|---|---|
dh |
Decision horizon | How many further decisions are simulated for each alternative | 2 | 1 to 5 |
mna |
Maximum number of alternatives | How many alternative assignments are generated at each decision point | 100 | 1 to 1000 |
sr |
Sampling rate | How many random continuations are simulated per alternative | 10 | 1 to 100 |
Higher values search more and take longer. Run time grows roughly in proportion to mna, and much faster with dh. The service's own pipeline always uses 2 / 100 / 10. The stage 2 endpoint lets you choose. Values outside the allowed range get HTTP 400.
License
The text of this page, the example data and run_example.py are licensed under CC BY 4.0 (Creative Commons Attribution 4.0 International).
The optimization service (the planning model, the scheduling heuristic and their hosting) is not included in this repository and is not covered by this license. It is a hosted service of LMS, University of Patras.
Acknowledgement
Funded by the European Union. This work is part of the EnerTEF project, which has received funding from the European Union's Horizon Europe research and innovation program under grant agreement No 101172887 (project page on CORDIS, the European Union's research and development information service). Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union or the European Research Executive Agency (REA). Neither the European Union nor REA can be held responsible for them.
Citation and contact
@misc{enertef_sustainable_supply_chain_optimizer_2026,
title = {Sustainable Supply Chain Optimizer},
author = {{LMS, Laboratory for Manufacturing Systems and Automation, University of Patras}},
year = {2026},
howpublished = {\url{https://huggingface.co/EnerTEF/Sustainable-Supply-Chain-Optimizer}},
note = {EnerTEF project}
}
Contact and live access: on request through the EnerTEF project or LMS, University of Patras (https://lms.mech.upatras.gr).