Estudio de Simulación del Comportamiento de un Sistema Híbrido de Fabricación y Ensamblado de tipo Jobshop
Abstract
En este documento se pretende estudiar el modelado y simulación de un entorno industrial, cuyo ritmo de fabricación se rige por una serie de Workload Policies, utilizando un software público y de código abierto como es el lenguaje de programación de Python y la librería Simpy de simulación de eventos discretos. Comparando los resultados de los modelos mas relevantes del paper original con modelos similares, aplicando otra regla de ordenación de colas, se estudiarán si el modelo es mejorable.
Full text
Proyecto Fin de Carrera Ingeniería de Telecomunicación Formato de Publicación de la Escuela Técnica Superior de Ingeniería Autor: F. Javier Payán Somet Tutor: Juan José Murillo Fuentes Dep. Teoría de la Señal y Comunicaciones Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2013 Trabajo Fin de Grado Grado en Ingeniería Aeroespacial Estudio de Simulación del Comportamiento de un Sistema Híbrido de Fabricación y Ensamblado de tipo Jobshop Autor: Ángel Quiles Oñate Tutor: Marcos Calle Suárez Dpto. Organización Industrial y Gestión de Empresas I Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2022
Trabajo Fin de Grado Grado en Ingeniería Aeroespacial Estudio de Simulación del Comportamiento de un Sistema Híbrido de Fabricación y Ensamblado de tipo Jobshop Autor: Ángel Quiles Oñate Tutor: Marcos Calle Suárez Profesor Titular Dpto. Organización Industrial y Gestión de Empresas I Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2022
Trabajo Fin de Grado: Estudio de Simulación del Comportamiento de un Sistema Híbrido de Fabricación y Ensamblado de tipo Jobshop Autor: Ángel Quiles Oñate Tutor: Marcos Calle Suárez El tribunal nombrado para juzgar el trabajo arriba indicado, compuesto por los siguientes profesores: Presidente: Vocal/es: Secretario: acuerdan otorgarle la calificación de: El Secretario del Tribunal Fecha:
Agradecimientos A mis padres, que me mostraron el camino. A mi hermano, que siempre está. A mis abuelos, que siempre estarán. A mis amigos, sin ellos no sería nada. I
Resumen En este documento se pretende estudiar el modelado y simulación de un entorno industrial, cuyo ritmo de fabricación se rige por una serie de Workload Policies, utilizando un software público y de código abierto como es el lenguaje de programación de Python y la librería Simpy de simulación de eventos discretos. Comparando los resultados de los modelos mas relevantes del paper original con modelos similares, aplicando otra regla de ordenación de colas, se estudiarán si el modelo es mejorable. III
XNotación ThrWIP Máximo número de trabajos en proceso de forma simultánea permitido en la planta (Threshold WIP) TkInstante de llegada del pedido k tmanu f acture Tiempo de procesado de una operación ToutkTiempo de salida del pedido k tsim Horizonte de simulación Ttk Throughput time, tiempo de procesado, Ttk=Toutk−Tk U Utilización, fracción del tiempo productivo entre el tiempo total de simulación U[a,b] Distribución uniforme con límite inferior a y superior b WIP Trabajo en proceso, del inglés Work In Progress
1 Objetivo En este capitulo se presentarán los motivos que han llevado a la realización de este trabajo, así como su justificación y el alcance que pretende tener. 1.1 Presentación En el último siglo, los sistemas de producción sufrieron un gran desarrollo y crecimiento, tanto en volumen como en eficiencia. Este salto se debe principalmente al surgimientos de nuevas técnicas de control y gestión de la producción, junto con el auge de la simulación y las herramientas de predicción de las demandas. Sin embargo, en los pequeños talleres, en su mayoría artesanos, estas herramientas de predicción son excesivamente costosas o ineficientes para el volumen de producción con el que trabajan. Es por eso que para este tipo de sistemas de producción, las reglas de despacho tradicionales resultan una alternativa mucho mas útil. Simplemente siguiendo una estrategia EDD (Fecha de Entrega mas Temprana, del inglés Earliest Due Date) los resultados podrían mejorar considerablemente en determinados entornos, frente al clásico FIFO (Primero en Llegar-Primero en Salir, del inglés First In-First Out) empleado, por ejemplo, en numerosos talleres del automóvil locales. El siguiente paso, sería una regla de control del ritmo de trabajo, dinámica pero suficientemente sencilla para poder ser aplicada en el día a día de un pequeño job-shop o taller. Como plantean Renna et al [1], con unas políticas de control del carga de trabajo (Workload Control Policies) se podría mejorar los resultados de un pequeño taller, al reducir los niveles de piezas almacenadas mientras se mantiene un nivel de producción suficientemente bueno. Como declaran en las conclusiones de su estudio, esta estrategia de control «permite mejorar la utilización media y, por tanto, reducir el consumo de energía [...], además, la reducción del nivel de WIP reduce el coste de almacenamiento de los objetos», y por otro lado «el modelo propuesto utiliza una cantidad de información limitad y se caracteriza por una baja carga computacional, lo que corrobora a usabilidad del modelo propuesto en casos industriales», lo que refuerza la idea planteada anteriormente. 1.2 Objeto de trabajo Este trabajo pretende continuar con el concepto planteado por Renna et al [1], recreando su modelo y explorando su estrategia con otro criterio de ordenación de colas. Para ello, se recurrirá a la simulación con un lenguaje de programación libre como es Python y al paquete de simulación de eventos discretos Simpy. Ya que Python se trata de uno de los lenguajes mas usados en el entorno científico, el proyecto servirá también como demostración de las posibilidades de la simulación de eventos discretos en un software de uso libre. 1
2Capítulo 1. Objetivo Figura 1.1 Representación del layout del taller [Fuente: Renna et al.]. Entonces, se tomará como punto de partida el modelo expuesto en Renna et al, usando los dos resultados mas favorables de Workload Control policies y ampliando el estudio existente mediante el análisis del comportamiento de este entorno productivo bajo la implementación de otro criterio de ordenación ampliamente utilizado, dado su buen comportamiento. Una vez recreado el modelo en Python se realizarán las simulaciones pertinentes y el tratamiento de los datos obtenidos para su comparación, con el fin de extraer conclusiones y posibles recomendaciones.
2 Sistemas de Producción Para poder comprender el resto del proyecto, es necesario entender, al menos de forma superficial, los sistemas productivos y cómo funcionan. Este capítulo se centrará en explicar las bases de estos sistemas, comenzando por una introducción a la teoría y siguiendo con ejemplos de sistemas existentes, incluyendo un estudio de como encaja el modelo estudiado entre ellos. 2.1 Introducción La producción es, tal y como explica James L. Riggs en su libro Sistemas de Producción: Planeación, Análisis y Control [2], el acto intencional de producir algo útil, así como la generación tanto de bienes, como de servicios. Esta definición tiene el valor de que no limita la forma en la que se produce, podríamos hablar de un coche, o del contenido de un libro. En este proyecto, se tomará un sentido mas industrial, de este modo, otro concepto del que se hablará mucho es el de sistema de producción, el cual puede ser definido como el proceso organizado mediante el cual los elementos son transformados en productos útiles. De esta forma, para una empresa que empieza un proyecto, elegir un sistema de producción es una decisión crucial, pues no solo conlleva elegir un proceso de manufactura para el bien deseado, sino que incluye la gestión, organización de todos los recursos de materia, maquinaria, etc. que se engloban dentro de la producción. En definitiva, el sistema contempla tanto los procesos como las actividades necesarias para realizar la transformación de los elementos iniciales a los productos o servicios deseados. Al comienzo, los sistemas de producción existentes eran mucho mas simples que los actuales y por tanto la necesidad de realizar un estudio de mejora era prácticamente inexistente. Pero con el paso del tiempo, los procesos se volvieron complejos, teniendo utilizando componentes que provienen de otro sistema y proporcionando productos a otros sistemas que lo utilizarán como puntos de partida, creándose un enrevesado sistema de sistemas. Figura 2.1 Retrato de Friedrick W. Taylor [Fuente: www.biografiasyvidas.com [3]]. 3
4Capítulo 2. Sistemas de Producción Figura 2.2 Retrato de Henry L. Gantt [Fuente: www.biografiasyvidas.com [4]]. La revolución industrial trajo el desarrollo de la industria, la producción se aceleró, la busqueda de la reducción de costes por la fuerza bruta llevó, en primer lugar, a centrar las mejoras en el objeto pero sin tener en cuenta las condiciones de los trabajadores, aún así aparecieron conceptos como la distribucion en planta por departamentos y registros mejorados de costes. Ya en el siglo XX, un nuevo enfoque, con tendencia "científica", entró de lleno en la gestión y administración de la producción, de la mano de nombres como Frederick W. Taylor padre de la corriente de pensamiento llamada administración científica oTaylorismo y Henry L. Gantt, creador del diagrama homónimo que permite planificar y visualizar los distintos procesos de un proyecto. Esta tendencia de pensamiento, prefería contemplar la máquina y el operario como una unidad, donde uno de los factores en la efectividad era el salario de la persona encargada. Mas adelante, se comenzaría a tener en cuenta la fatigua, la monotonía o las reacciones emocionales como factores que afectaban a la productividad, lo que convertiría al conjunto trabajador-máquina en algo poco intuitivo, agravándose la situación ante la llegada de sistemas de producción cada vez mas complejos. Pese a esto, se tendió a la mejora de las condiciones laborales y la automatización, a la producción en serie (conocida a partir de su implantación en las fábricas de Ford). A finales del último siglo, la crisis económica de los 80 incentivó a los empresarios americanos a importar filosofías de producción asiáticas, que habían implantado empresas como Toyota. Actualmente, existen estrategias y filosofias de administración de la producción que buscan eliminar poco a poco cualquier gasto innecesario asociado a la actividad productiva, el Lean Manufacturing, que se inspira y aúna diversos principios orientales. La automatización de los procesos parece el camino, y aunque exista el miedo al desempleo que pueda surgir por la sustitución de la mano de obra, lo cierto es que las condiciones para los trabajadores han ido mejorado, eliminando tareas peligrosas y monótonas. 2.2 Sistemas de Producción Básicos Existen dos estrategias básicas para la organización de la producción, el Make to Stock (MTS) y el Make to Order (MTO), presentan enfoques totalmente opuestos. En la actualidad se han derivado numerosas filosofías que intentan aunar lo mejor de ambos sistemas, como el Assemble to Order (ATO) o el Make to Forecast (MTF). También existen algunas estrategias mas extremas como el Engineer to Order (ETO). 2.2.1 Make to Stock (MTS) El sistema Make to Stock (en castellano, Fabricar para Almacenar), es utilizado cuando sólo existe una sola variedad de producto y la demanda no presenta grandes variaciones a corto plazo. La idea
2.2 Sistemas de Producción Básicos 5 Figura 2.3 Una linea de producción de ruedas en 2014 en Jianxing, China [Fuente: www.nytimes.com]. detrás de este sistema es que la producción sea continua, fabricándose el producto sin que el cliente haya realizado un pedido, y almacenándose para cuando se necesite. Este sistema se incluye en los conocidos como sistemas Push. Según son definidos en Diseño y Gestión de Sistemas Productivos [5], estos sistemas son en los que la demanda "empuja" la producción con el objetivo de cumplir los plazos. El principio es simple, se produce sin parar para cuando sea necesario. El sistema se comporta como un flujo constante de material, para ello, los productos deben ser lo mas simples posibles, sin variaciones. Para que los costes por inventario no se hagan inasumibles, es necesario que la demanda sea relativamente estable, al menos a largo plazo, ya que el sistema es inflexible y muy complicado de reajustar. Por tanto, el dimensionamiento inicial de la producción es clave en este sistema, un sistema mas grande generará costes de almacenaje excesivos, y uno pequeño no suplirá la demanda de los clientes a tiempo. La ventaja de este sistema se da para el cliente, ya que puede disponer del producto terminado de forma instantánea a realizar el pedido. Reincidiendo en el dimensionamiento del sistema, si el sistema es pequeño y no se alcanza la demanda, se pierde esta ventaja de la rapidez de entrega. En resumen, podemos agrupar las ventajas de este tipo de sistema como las siguientes: • Los tiempos de producción y de espera por parte del cliente son mínimos. El primero, por la simplicidad requerida al producto final y el segundo, gracias al almacenaje de los productos acabados. • Altamente automatizable. El que sea un producto simple y que se fabrique en altas cantidades, hace que su la automatización sea la mejor opcion, lo que reduce costes y tiempos. • Baja intervención humana. Directamente relacionado con el punto anterior, una alta automatización implica una necesidad menor de intervención humana, además de una formación mas reducida de la mano de obra. Y los principales problemas que presenta: • Inversión inicial muy alta. Es necesario la compra del sistema de automatización de la producción, la maquinaria y los almacenes.
6Capítulo 2. Sistemas de Producción Figura 2.4 Diagrama comparativo entre los sistemas push ypull [Fuente: Elaboración Propia]. • Sistema muy inflexible. El alto grado de automatización impide cambios y/o personalización de los productos sin una reconfiguración del sistema completo. Incrementar o reducir el ritmo de producción es algo menos costoso. • Puesta a punto compleja. El diseño y puesta a punto del sistema es cara y compleja por culpa de la automatización, lo que no hace rentable reconfigurarlo a menudo. Los sistemas MTS siguen siendo usados en la actualidad, los encontramos en productos que pueden ser almacenados largas cantidades de tiempo (no perecederos) y que poseen una demanda estable, además de ser productos poco personalizables. Este es el caso de tornillos, rodamientos, neumáticos y pilas, productos que se encuentran limitados por unos estándares de diseño y que son necesarios constantemente, pero también se usa en el caso de refrescos, la harina o incluso la leche. Para el escenario en el que vamos a trabajar, el de un taller (job-shop) no es la estrategia mas acertada, debido al volumen de producción y las diferencias entre ordenes, como se verá mas adelante. 2.2.2 Make to Order (MTO) Los sistemas Make to Order también conocidos como Build to Order, en castellano Fabricar bajo Pedido, consisten en esperar a que un cliente haga un encargo para comenzar a fabricar el producto. Para ello, la empresa ha debido con anterioridad ofrecer sus productos y posibilidades al mercado, y es una vez que se firme un contrato, cuando se prepara la producción del encargo. Frente al MTS, el MTO es un sistema pull, en el que es la demanda la que "tira" de la producción, de igual forma de la que dentro de la fabrica de estos sistemas, una estación de trabajo "tira" de la producción de la estación aguas arriba cuando lo necesitan, creándose, como explican Onieva et al [5], una demanda interna. Se genera entonces un flujo de información que viaja aguas arriba, desde el cliente, hasta los proveedores, pasando antes por las etapas productivas; al contrario que los sistemas MTS, donde la información viajaba aguas abajo (la información bajaba en forma de deadline por la línea de producción), como se ve en la figura 2.4. En este caso, al ser el cliente el que establece el inicio de la producción, existe la posibilidad de personalizar el producto final a las necesidades del cliente (en el MTS el producto se encontraba ya listo cuando se establecía el pedido). Esta posibilidad da lugar a que productos mas complejos sean producidos, pero tienen que estar definidos al menos en parte. El mejor ejemplo se suele encontrar
2.2 Sistemas de Producción Básicos 7 Figura 2.5 Final Assembly Line del A400M en San Pablo, Sevilla [Fuente: fly-news.es]. en la industria aeronáutica, donde un cliente, ya sea una aerolínea o un ejército, encarga un avión configurando los equipos embarcados, motorizaciones, etc, según sus necesidades y preferencias (diferenciar de los sistemas ETO, que se verán mas adelante). A diferencia de los sistemas Make to Stock, salvando pequeños inventarios intermedios, no hacen falta almacenar el producto final, ya que una vez terminado se entrega al cliente directamente. Si en un sistema de este tipo, se optimizan los tiempos de forma que los componentes lleguen a la vez a las estaciones de trabajo, sin ningún tipo de retraso, los inventarios intermedios son innecesarios, se habla entonces de un sistema just-in-time (justo a tiempo en castellano). Sin embargo, el cliente tendrá que esperar a que el producto se termine, ya que se empieza a producir cuando lo establece el pedido. A modo de resumen, presenta las siguientes ventajas: •Productos de muy alta calidad y personalizados. •Producción equiparada a la demanda existente, pues se fabrica bajo pedido. •Mayor flexibilidad, comparada con sistemas MTS. Pero también presenta algunas desventajas: •Mayores costes de producción, sobre todo comparando con MTS. • La formación necesaria para los trabajadores es mayor, pues los productos son mas complejos y es necesario especialistas. •Tiempos de espera mas largos para el cliente. 2.2.3 Otros sistemas Con el desarrollo de la industria, también se desarrollaron los sistemas productivos, apareciendo desarrollos de los dos comentados anteriormente. A continuación, se presenta una lista de algunos, pero no se profundizará mas ya que se escapa de alcance de este proyecto. •Engineer to Order, ETO – En este caso, el cliente plantea un problema y sus limitaciones, y la empresa contratada se encarga de cercar el problema y diseñar el producto solución, a demás de fabricarlo. El producto es, por tanto, lo mas personalizado posible.
8Capítulo 2. Sistemas de Producción – El tiempo de espera del cliente es el mayor de todos, pues incluye la fase de diseño, fabricación e incluso de certificación. •Assemble to Order, ATO – Punto medio entre MTO y MTS, en donde se fabrican y almacenan los componentes del producto final con una filosofía similar a la MTS, y cuando se establece un pedido, se montan según los requerimientos del cliente. – El tiempo de espera se ve reducido con respecto al MTO, puesto que solo se incluye el de montaje, las piezas están ya listas. – Para que exista personalización, las piezas deben tener algún tipo de modularidad. Un ejemplo de este sistema pueden ser coches, donde se monten los extras según la demanda del cliente. •Make to Forecast, MTF – Híbrido de los sistemas MTS y MTO, donde se fabrica según se prevé que los clientes vayan a presentar un pedido. – Los productos finales deben ser tan simples como en el MTS, pero la ventaja es que si funciona bien, los costes por inventario se reducen drásticamente, manteniendo la velocidad de entrega al cliente. – El principal problema de este sistema es la previsión de las variaciones de la demanda. Hace falta en primer lugar un modelo de predicción con un porcentaje de acierto alto, y después, un sistema productivo capaz de absorber las variaciones del ritmo de producción, lo que implica que habrá momentos en los que la planta se encuentre sobredimensionada y ociosa, para no saturar cuando se prevé un pico de demanda. La realidad es mucho mas compleja, y existen muchos mas sistemas de los comentados en este documento, se podría tener un taller que fabrique componentes con una filosofía MTF, pero que los monte según el cliente lo pida, hibridándolo con un ATO, por ejemplo. De cualquier modo, es mas que suficiente para continuar con el proyecto. Tabla 2.1 Comparativa de las características esenciales de los distintos sistemas de producción básicos.. Estrategia Personalización Limitación Velocidad de Entrega Coste Personalización Complejidad de gestión Reto ETO Plena, única Ilimitada Muy lenta Muy Alto Alta Diseño y fabricación MTO Amplia Gamas de fabricación Lenta Alto Moderada Organización MTF Moderada Oferta Moderada Moderado Muy alta Adivinar demanda ATO Poca Módulos de montaje Rápida Poco Poca Planificación de inventarios MTS Ninguna Variedad almacenada Inmediata Ninguno Muy baja Demanda estable 2.3 Sistemas de Control Los sistemas de control, se pueden definir como el conjunto de procesos y actividades centradas en vigilar y controlar el progreso de los pedidos por las distintas estaciones, liberar de forma progresiva los pedidos a la fábrica y recabar información del estado de los pedidos y la planta en sí; como explica Mikell P. Groover en su libro [7]. Groover divide el control en tres fases, la liberación de los pedidos, la planificación de los pedidos y el progreso de los pedidos, pero en este proyecto haremos una distinción entre control en la entrada y el control a pie de planta, donde el primero incorpora a la liberación y el segundo incorpora las dos últimas fases de Groover.
2.3 Sistemas de Control 9 Figura 2.6 Gráfico tiempo de entrega frente a grado de personalización cualitativo de las regiones donde es mas útil aplicar un sistema de los comentados [Fuente: International Journal of Operations & Production Management [6]]. 2.3.1 Regulación en la Entrada Cuando un pedido entra en producción, este llega con la información estrictamente necesaria para ser procesada en la planta. Esta información dependerá del tipo de planta y producto en cuestión, pero puede incluir la hoja de ruta del producto por el taller (que operaciones deben ser realizadas, en qué estación y en que orden), los requisitos de materiales, la lista de piezas necesarias y diversas etiquetas sobre transporte y gestión. Una vez que el pedido ha entrado en la planta, se cuenta para los datos, se tiene que gestionar su prioridad en el taller y sus estadísticas cuentan para las estadísticas globales del taller, por ello, puede resultar interesante controlar cuando se "libera" un pedido, con el objetivo de no sobrecargar a los trabajadores y las máquinas. Workload Control Existen diversos criterios y mecanismos para regular la entrada. En el artículo estudiado, se utiliza un criterio llamado Workload Control Policy (Política de Control de Carga de Trabajo), que elige cuando liberar el pedido en función de la carga de trabajo de la planta. En este caso en concreto, evalúa la carga como el número de pedidos en proceso al mismo tiempo en la planta, el número de pedido Work-In-Process (WIP). Para ello, los autores establecen un umbral máximo de pedidos que se pueden estar trabajando al mismo tiempo, este criterio se debe evaluar antes de liberar cualquier encargo al taller. Se puede escribir como: WIP ≤ThrWIP (2.1) Siendo WIP el número de pedidos en proceso, y ThrWIP el umbral (threshold) o número máximo de pedidos en proceso permitido. Siguiendo este criterio, los encargos de clientes que lleguen cuando esta condición no se cumpla, deberán esperar a cuando se cumplan. Los pedidos se almacenan mientras esperan a ser procesados en una lista, el preshop, que puede ser ordenada según la prioridad elegida; por lo que podríamos tomar al sistema como una máquina y al preshop como su cola. Estos criterios pueden incurrir en retrasos si la condición es muy estricta, por lo que habría que jugar con el umbral. Este umbral puede ser arbitrario, o venir dado por el límite de capacidad de la planta, en todo caso, es útil limitar que un pedido entre en producción si sabe que sus componentes van a estar esperando mucho tiempo, lo que implica costes de inventario.
16 Capítulo 3. Simulación Figura 3.2 Gráfico sacado de una simulación continua de un modelo depredador-presa [Fuente: McHaney, R. [8]]. Estas simulaciones funcionan muy bien, pero presentan el problema de encontrar las ecuaciones que rigen el problema, ya que para determinados problemas, como se verá mas adelante, las variables no son continuas, como es el caso de la llegada de pedidos a una fabrica. Además, los ordenadores no son capaces de trabajar con variables continuas, necesitarían una memoria infinita, es por ello que para este tipo de simulaciones se tiende a discretizar el problema, tomar intervalos de integración, etc., mientras mas pequeños mejor. Simulación de Eventos Discretos Las simulaciones de eventos discretos se caracterizan por "discretizar" el tiempo en bloques, durante los que no ocurre nada importante, ya que el sistema solo cambia de estado en los eventos instantáneos que ocurren entre cada step (paso) de tiempo. Todas las variables salvo el tiempo permanece constante entre cada evento. Esto resulta ser una solución de compromiso en muchos casos, pues hay variables con no pueden cambiar instantáneamente de estado, pero resulta muy útil para modelar entradas y salidas de elementos en un depósito o una cola, lo que resulta clave a la hora de simular la dinámica de sistemas productivos. Ya que en un proceso productivo las operaciones se realizan siguiendo unas instrucciones paso a paso, lo que es fácilmente trasladado a los pasos de tiempo discretos de este tipo de simulación. En un sistema productivo es fácil encontrarse con un tiempo de montaje, un tiempo de fabricación, un tiempo de empaquetado, de transporte, tiempo de espera a la llegada de recursos o a la disponibilidad de una herramienta o máquina. En cuanto a los eventos, son instantáneos, cambian las variables de estado del sistema y pueden surgir a raíz de otros eventos, o generarlos. Un ejemplo sencillo sería el proceso que sigue una pieza desde su llegada a una maquina, su procesado y el envío de esta hasta que llega a la siguiente estación. Como se puede ver en la figura 3.3 el proceso para una línea es sencillo, pero cuando hay dos en paralelo, el reloj de simulación debe pararse en cada evento; durante el tiempo que la simulación procesa el evento de una línea, el estado de la otra se mantiene constante. Existen dos enfoques a como pasar el tiempo del reloj de simulación en este tipo de simulaciones, por un lado existen softwares que establecen un tiempo de progreso fijo, en los que el tiempo es discretizado en pequeños pasos y en la simulación se avanza uno a uno parándose en cada uno y comprobando si existe algún evento activo en esa porción de tiempo. Por otro lado, existen softwares de simulación que poseen una lista de los eventos presentes en la simulación junto con el instante de tiempo en el que se llevan a cabo, antes de avanzar el tiempo, el software revisa en la lista cual es el siguiente evento, y mueve el reloj hasta ese punto; cabe recordar que los eventos pueden generar otros eventos, que se irán añadiendo a la lista progresivamente. La estrategia de saltar al siguiente evento, llamada en inglés next-event advance, puede ser, generalmente mas rápida; ya que no necesita simular los intervalos entre un evento y otro.
3.2 Modelo en Python 17 Figura 3.3 Diagrama donde se puede ver como se entrelazan las pausas en una simulación de eventos discretos con dos líneas paralelas, las paradas corresponderían con una simulación nextevent advance [Fuente: Elaboración propia]. 3.2 Modelo en Python En este proyecto se ha elegido Python para como plataforma para realizar la simulación, junto con el complento SimPy que ofrece numerosas herramientas para simulación de eventos discretos. Ya que el objetivo del trabajo es el desarrollo no es el desarrollo del código de Python en sí, si no la simulación, en esta sección se presenta una descripción sobre como modelar algunos proceso relevantes, que se explorarán adaptados al caso estudiados en el siguiente capítulo. 3.2.1 Python ySimPy En primer lugar, Python es un lenguaje de programación considerado de alto nivel y multiparadigma, usado ampliamente en distintas industrias, que incluyen desde aplicaciones científicas, simulación, hasta el desarrollo de aplicaciones o videojuegos. El motivo de la elección de este lenguaje de programación, además de por su popularidad y su relativa sencillez de aprendizaje, reside en que el software posee una licencia de código abierto, lo que permite que cualquiera pueda replicar este proyecto sin necesidad de comprar una licencia, como pasaría con MATLAB y su extensión de simulación Simulink u otros sofwares específicos de simulación de eventos discretos. En Python, se incluyen funciones que ya aparecen en multitud de lenguajes, a continuación se presenta un resumen de funcionalidades básicas y relevantes para este proyecto: • Variables. Las variables guardan información, ya sea texto, un entero, un número decimal, un valor lógico o un conjunto de los anteriores. Existen distintos tipos de variable según el tipo de dato que guarde, los que se usan en este proyecto son: –Entero, un número entero, se define como A = 34 . – Float, numero en formato de coma flotante de doble precisión, permite almacenar decimales, se definen exactamente igual que los enteros pero deben tener presente algún decimal: b = 3.00 . – Booleano, solo puede tomar valor verdadero o falso, se definen con las palabras reservadas correspondientes: Var_verdadero = True; Var_falso = False . – Strings, cadenas de caracteres, se definen de de la siguiente forma: C=’texto’ . Se usan para guardar texto como una serie de caracteres, sumar dos cadenas es equivalente a encadenarlas.
18 Capítulo 3. Simulación – Lista, agrupa varios objetos de forma ordenada, que pueden ser de distintos tipos. Se define como lista = [3.45, ’hola’,False] . Los elementos se indexan con un índice que indica su posición, comenzando con el 0, para acceder al objeto en la última posición se puede usar el índice -1. Los elementos pueden se a su vez otras listas, lo que permite crear una especie de matrices con listas anidadas. – Diccionarios, almacena elementos por pares, para acceder al valor del objeto se le especifica al diccionario su clave asociada, dic = {’clave1’:’valor1’,’clave2 ’:[1, 2.1, ’hola’], ’clave3’:True} . Como se ve se pueden introducir listas dentro de estos diccionarios, incluso otro diccionario, lo que lo convierte en una herramienta útil para almacenar cualidades o características de un objeto, o en el caso de este proyecto los tiempos y variables de estado de un pedido. • Sentencias condicionales. Son instrucciones que especifican que parte del código debe ejecutarse cuando o mientras se cumplan ciertas condiciones. – Sentencia if. La sentencia if condition: code establece que el código que se escriba a continuación solo se puede ejecutar si se cumple una condición especificada. Se complementa con las sentencias elif condition2: code 2 , que permite establecer otra sección ejecutable en el caso que no se cumpla la primera condición sólo si se cumple otra condición expresada, se pueden escribir tantos elif como sea necesario; y else: code3 , que permite que se ejecute otro fragmento de código si no se cumplen ninguna de las condiciones anteriores. Se pueden anidar condiciones, introduciendo sentencias if dentro de otras: – Bucle for. Estos bucles permiten ejecutar un mismo bloque de código varias veces, recorriendo un objeto iterable (una lista o una cadena por ejemplo) y asignándole a una variable un valor del iterable cada vez, recorriéndolo de principio a fin. Por ejemplo: >>> primos = [1, 2, 3, 5, 7, 11, 13] >>> for num in primos: # En cada iteración num tomará un valor de primos ... print(num), 1 2 3 5 7 11 13 – Bucle while. Este tipo de bucle ejecuta un bloque de código sin parar, mientras se mantenga una condición especificada. Se puede crear un bucle infinito si la condición es verdadera siempre, o escribiendo True en la posición de la condición. Ejemplo en el que se escriben los números primos menores que 10: >>> primos = [1, 2, 3, 5, 7, 11, 13], num_id = 0 # Se empieza con el primer elemento de la lista primos >>> while primos[num_id] <= 10: ... print(primos[num_id]), ... num_id += 1 # Actualización del índice
3.2 Modelo en Python 19 1 2 3 5 7 • Funciones. Las funciones son bloques de código con un nombre asignado y se ejecutan solo cuando son llamadas en el código, a las que entran unos inputs y devuelve unos resultados. Se definen como def nombre_funcion(input1,input2,...): , dentro de la función se pueden leer las variables definidas fuera de ella, pero trabajará con variables a nivel local. Para poder devolver algún valor, necesita que al final aparezca esta línea return output . Un ejemplo que devuelve la suma de dos variables: >>> def suma(x,y): ... return x+y ... >>> suma(3,4) 7 >>> suma(10,2.4) 12.4 • Métodos. Los métodos son funciones internas asociadas a objeto. Permiten acceder a funciones propias u atributos no visibles a simple vista. Para acceder a ellos se escribe el nombre del objeto, seguido con un punto, el nombre del método y entre paréntesis los inputs que necesite el método, si es que es necesario. Uno muy usado es método append para listas, que permite añadir un elemento al final de la lista. >>> saludos = [’hola’,’adios’,’que tal’] >>> saludos.append(’buenas’) >>> print(saludos) [’hola’,’adios’,’que tal’,’buenas’] Una gran ventaja de Python es que, a pesar de que de base carece de muchas herramientas para aplicaciones mas específicas, la comunidad que posee detrás ha ido desarrollando módulos o paquetes complementarios para cada tareas. La sencillez de instalación y su variedad hacen que se puede usar para casi cualquier propósito, para su instalación, basta con que el paquete de datos se encuentre en la ruta de Python y escribir al principio del código import paquete as paq , donde paq es el nombre (normalmente abreviado) con el que se llamará al paquete en el código. En este trabajo se usarán varios, resumidos en tabla 3.1, pero el que permite la simulación de eventos discretos en Python es SimPy. Esta extensión, establece un marco de trabajo para que Python pueda comprender como se relacionan los procesos, como avanza el tiempo y manejar las interrupciones. Con SimPy se definen los procesos como una función más en el código para modelar un cliente, una máquina, una cola o lo que se elija , pero se añade antes de correr la simulación un comando nuevo que le dice a Python como tratar esa función. Para una simulación con SimPy, primero se deben definir los procesos involucrados mediante funciones, con sus entradas y salidas. En segundo lugar, crear un entorno de simulación de SimPy donde correr la simulación. A este entorno se le asignan los procesos, llamando a las funciones y asignando un nombre al proceso; esto permite utilizar una sola función para modelar varios procesos similares y despues crearlos en el entorno de simulación llamando a la misma función con varios
20 Capítulo 3. Simulación Tabla 3.1 Relación de módulos de Python usados. Nombre Código Descripción SimPy import simpy Eventos discretos. Random import random Permite generar números aleatorios con numerosas distribuciones. NumPy import numpy as np Incorpora unos arrays de datos en forma de matrices y vectores muy útiles además de herramientas para trabajar con ellos. PyPlot import matplotlib.pyplot as plt Añade herramientas de representación de gráficos. Pandas import pandas as pd Facilita la exportación de datos en forma- to .dot y Excel. Time import time Permite usar contadores de tiempo para indicar el paso del tiempo en el código. parámetros y nombres distintos. En este paso se definen también los eventos. Por ultimo se da la orden de simular el entorno creado. El ejemplo que presenta la documentación propia de SimPy [10], dos relojes simultáneos con ritmos distintos, es bastante esclarecedor: Código 3.1 Ejemplo de código para simulación en Simpy, dos relojes asíncronos, de [10]. >>> import simpy >>> >>> def clock(env, name, tick): ... while True: ... print(name, env.now) ... yield env.timeout(tick) ... >>> env = simpy.Environment() >>> env.process(clock(env, ’fast’, 0.5)) <Process(clock) object at 0x...> >>> env.process(clock(env, ’slow’, 1)) <Process(clock) object at 0x...> >>> env.run(until=2) fast 0 slow 0 fast 0.5 slow 1 fast 1.0 fast 1.5 Nótese como el ejemplo utiliza un bucle sin fin con un while True: para que los relojes corran indefinidamente, sin esto el proceso definido en la función correría una sola vez; en estos casos, es importante darle al comando de inicio de simulación el parámetro del tiempo de la simulación, ya que sin él correría indefinidamente. Además, cabe destacar el uso de los métodos de Python para, primero definir el entorno de simulación, y segundo, añadirle características a este entorno. Por último, es sumamente importante que cada proceso (definido como una función en Python) tenga una duración, para eso se le incorpora una pausa con el comando yield env.timeout(duration) , incluyendo la duracion de este proceso dentro.
3.2 Modelo en Python 21 3.2.2 Procesos básicos A continuación se presentan, en rasgos generales, como se van a modelar y codificar los distintos procesos involucrados en un sistema productivo. Se han identificado 4 procesos que pueden ser modelados de forma independiente que sean relevantes en este proyecto, el proceso de llegada y creación de pedidos nuevos, el proceso por el que se liberan los pedidos de forma ordenada desde el preshop a la planta, el proceso por el que las máquinas individuales manejan su cola y realizan las operaciones requeridas para dejarlas en el buffer de componentes; y por último el proceso de ensamblaje, que toma las piezas de los buffers y las monta según un pedido y lo entrega al cliente. Llegada de pedidos En primer lugar, es necesario modelar la forma en la que llegan los pedidos. Suponiendo que un pedido llega nada mas comenzar la simulación, el software desconoce qué características tiene este pedido, cuantas piezas, de qué tipo, qué fecha de entrega hay que cumplir, etc. El primer paso es generar los datos siguiendo las distribuciones aleatorias correspondientes, es el bloque hexagonal en la figura 3.4. Figura 3.4 Esquema lógico del proceso de llegada de pedidos [Fuente: Elaboración propia]. Una vez generadas los atributos relevantes para la simulación de este pedido, este debe pasar a una lista de preshop, donde se registren todos los pedidos que han llegado, pero que aún no han iniciado su fabricación. Los pedidos se modelarán en el código como un diccionario que almacene cada uno de los atributos, esto permite añadir o sobrescribir atributos conforme el pedido avanza en la planta. La lista de preshop, a ojos de Python, se tratará como una lista que guarda de forma ordenada los diccionarios de los pedidos. En este momento, se pueden actualizar las estadísticas que se estén midiendo sobre la planta, en relación a las llegadas. Además, se lanza un aviso (en programación se suele llamar flag a la variable que indica este tipo de eventos) a los demás procesos, de que se ha actualizado la lista de preshop; se verá mas adelante como hacen uso los distintos procesos de estas alertas. Lo único que le falta al proceso es volver a empezar, una vez haya pasado un tiempo. Este tiempo se tendrá que modela con una distribución de probabilidad lo mas parecida a la realidad posible. A continuación se presenta un código que modelaría este proceso a rasgos generales.
22 Capítulo 3. Simulación Código 3.2 Código para un proceso genérico de llegada de pedidos. def process_newOrder(env): # New order arrives, its data is generated and passed to the preshop global preshop, flagarrival global level_queue, t_level_queue while True: Tarrival = 4 yield env.timeout(random.expovariate(1 / Tarrival)) # Time between new orders # Generation of parameters of the order Q_comp = random.randint(2, 6) # Number of different types of components required components = [1, 2, 3, 4, 5, 6] Qjk = np.zeros(6) # List with the quantity of the component j for order k for tin range(Q_comp): ty = random.choice(components) components.remove(ty) Qjk[ty - 1] = random.randint(1, 6) # Number of component of type j DDk = env.now + sum(Qjk) * random.randint(3, 5) # Deadline orderk = {’Qjk’: Qjk, ’DDk’: DDk, ’T0’: env.now} # Dictionary of the order # Introduce the order in the preshop preshop.append(orderk) # The preshop is a list filled with orders waiting to be released # Queue performance Measurements level_queue.append(len(preshop) - 1) # Before addition t_level_queue.append(env.now) level_queue.append(len(preshop)) # After addition t_level_queue.append(env.now) flagarrival.succeed() # Flag event new order has arrived flagarrival = env.event() # Reset flag Proceso de liberación a planta El siguiente paso es manejar la preshop e ir liberando los pedidos poco a poco a la planta. Lo primero, como se aprecia en la figura 3.5 es comprobar que la lista de pedidos pendientes no se encuentre vacía, lo que produciría errores si se intenta ejecutar el código que sigue. En el caso de que se encuentre sin ningún pedido, se tendría que esperar a la alerta de que llegue un nuevo pedido (el aviso que se modelo anteriormente). Cuando ya se sabe que existen pedidos que liberar,
3.2 Modelo en Python 23 es necesario calcular los parámetros de la planta relevantes y compararlos con los umbrales de Workload Control. Figura 3.5 Esquema lógico del proceso de liberación de pedidos [Fuente: Elaboración propia]. El proceso vuelve a evaluar su situación, si se cumplen los límites establecidos, sigue adelante; en el caso contrario, debe esperar a que el buffer o la lista de pedidos WIP se desatasquen un poco. El proceso normal de liberación continúa ordenando la Preshop siguiendo la norma de despacho elegida, de este modo, se asegura elegir el pedido que se encuentre en lo mas alto de esa lista ordenada. Con la lista ordenada y el pedido de mayor prioridad elegido, se pasa a generar tantas operaciones como componentes haya en el pedido, teniendo en cuenta el tipo y cantidad. Estas operaciones se modelan de forma similar los pedidos, en forma de diccionario con sus parámetros, la diferencia es que estas operaciones son las que se guardaran en las colas de las máquinas y los buffers, lo pedidos se quedan en un plano superior a la planta, pues no representan objetos en sí. Con esta filosofía, es necesario eliminar el pedido elegido del preshop y pasarlo a la lista de Work In Process junto con el resto de pedidos activos en la planta, las operaciones pasan a repartirse por las distintas máquinas disponibles, de forma indiferente , pues se esta considerando que cualquier máquina puede realizar cualquier tipo de operación. El último paso necesario, es avisar a las máquinas que se han actualizado sus colas de espera. Un codigo de ejemplo se presenta a continuación: Código 3.3 Código para un proceso genérico de liberación de pedidos. def process_release(env): global preshop, WIP global flagqueue, flagbuffer, flagassembled while True: if len(preshop) > 0: A=len(WIP) B=max(max(Thrb-b),0) if A < Thr_wip and B: # First & Second workload policy respectively queuetorelease = [] # Generation of a list of operations to release to the machines
24 Capítulo 3. Simulación # Firstly, organise the Preshop preshop = sorted(preshop, key=lambda d: d[’DDk’]) order = preshop[0] # Selected order (higher priority order) Qjk = order[’Qjk’] DDk = order[’DDk’] for component in range(len(Qjk)): if Qjk[component] != 0: for jj in range(int(Qjk[component])): queuetorelease.append({’DDk’: DDk, ’Comp’: component + 1}) # An operation is assigned as a dict with properties # Moving orders from preshop to Work-In-Progress list WIP.append(preshop[0]) del preshop[0] # Release of the generated operations for operation in queuetorelease: mach = sorted(queue_mach, key=lambda d: len(queue_mach [d]))[ 0] # Extract which machine has a shorter queue queue_mach[mach].append(operation) # Operation is introduced in shorter queue flagqueue.succeed() # Queue update event flag flagqueue = env.event() # Event Reset else: yield flagbuffer or flagassembled # Wait for either the WIP reduction or the buffers update else: yield flagarrival Proceso de fabricación en una máquina Este proceso solo es necesario definirlo una vez, y después llamarlo tantas veces como máquinas existan en la planta. Desde el punto de vista de una máquina individual i , lo primero que necesita para comenzar a producir las operaciones, es saber si hay alguna operación pendiente en su cola. En caso negativo, debe esperar a la que se actualizen las colas (flag queue). Cuando haya alguna operación, es necesario ordenar la cola según el criterio elegido previamente, se escoge la operación con mayor prioridad y se saca de la cola. En este momento, se comienza procesar la pieza, poniendo el proceso en pausa el tiempo correspondiente (generado aleatoriamente generalmente). Tras terminar el procesado de la operación, se actualizan los atributos de la operación, se le añade la marca de tiempo en el que termina su procesado. Las operaciones terminadas se van mandando la lista de buffer correspondiente al tipo de componente, lanzando el aviso correspondiente de que se ha actualizado el buffer. Véase el código de ejemplo, donde se puede apreciar que los buffer se han modelado como listas que van almacenando los diccionarios de las operaciones, estos buffers no se ordenarán, pues cada tipo de componente es igual, independientemente del pedido para el que se haya fabricado.
3.2 Modelo en Python 25 Figura 3.6 Esquema lógico del proceso de procesamiento de operaciones en una máquina genérica [Fuente: Elaboración propia]. Código 3.4 Código para un proceso genérico de procesamiento en una máquina. def process_manufacture(env, mach): # "mach" acts as an identifier of the machine global queue_mach, buffers, WIP global flagqueue, flagbuffer while True: if len(queue_mach[’machine ’ +str(mach)]) > 0: # First the machine queue is ordered queue_mach[’machine ’ +str(mach)] = sorted(queue_mach[’ machine ’ +str(mach)], key=lambda d: d[’DDk’]) inprocess = queue_mach[’machine ’ +str(mach)].pop(0) # The first operation in queue is picked # Manufacturing start inprocess[’T_mach’] = env.now yield env.timeout(random.expovariate(1)) # Manufacturing time # Send order to buffer comp = inprocess[’Comp’] inprocess[’T_buff’] = env.now buffers[’Buffer ’ +str(comp)].append(inprocess) flagbuffer.succeed() # Buffer update flag flagbuffer = env.event() # Flag reset else: yield flagqueue # Wait for the machine queue to update Proceso de montaje El proceso final comienza comprobando que existen pedidos pendiente por ser montados, esto es equivalente a evaluar si algún pedido en la lista de Work In Process, en caso contrario sería necesario
32 Capítulo 4. Modelo de Simulación Donde bj indica el nivel del buffer del componente j . En el caso que sea mayor que cero se cumple que aún hay hueco en algún buffer. • Estrategia 2, umbrales adaptables. Una variación con respecto al caso anterior, se mantiene la primera condición de liberación de 4.1. Sin embargo, la segunda se modifica, y se incluye que el umbral de los buffers sea adaptable a cada tipo de componente conforme avanza la simulación. La expresión de control es la misma, pero con el nuevo umbral que se calcula en función de la media y la varianza del número de componentes de ese tipo que han ido llegando. f(bj) = m´ axm´ ax jThrbj−bu f f erj; 0>0,Thrbj=6(4.3) ThrbJ=averagej+K∗variancej(4.4) Donde K es un parámetro de ajuste, que ajusta la probabilidad de poder almacenar todos los componentes de un pedido. Un valor alto de K aumenta la probabilidad de almacenar todos los componentes de un pedido, pero aumentaría los costes por inventario. 4.1.3 Escenarios de simulación En el artículo original, para estudiar las distintas estrategias, se plantean cuatro escenarios para ver como reacciona cada una de ellas bajo distintas condiciones de demanda y configuración de pedidos. Estos cuatro escenarios se van a mantener en este estudio, son los siguientes: • Escenario base. Es el caso mas sencillo, los pedidos llegan con la distribución exponencial antes vista y con media µllegada =4 . El número de componentes distintos mantiene la distribución uniforme U[2,6] y el número de operaciones por componente una uniforme U[1,6] . El tiempo de ensamblaje sigue una distribución exponencial de media uno y la fecha de entrega se calcula mediante la expresión antes vista. • Demanda variable. En este escenario se introduce una serie de fluctuaciones en la demanda de pedidos, parámetro λ (y por tanto la media) de la distribución exponencial deja de ser constante, y va variando cada 200 unidades de tiempo, siguiendo la siguiente tabla: Tabla 4.1 Fluctuación de la demanda. Tiempo Media, µllegada Parámetro λ 0 4 0.25 200 6 2/3 400 2 0.5 600 5 0.2 800 4 0.25 • Componentes desiguales. Ahora, varía la distribución del número de operaciones de un tipo concreto dependiendo del tipo de componentes que sea, la distribución no es la misma para cada componente, pero se mantiene la distribución que da el número distintos de componentes. Para cada componente la probabilidad sigue las siguientes distribuciones: • Combinación de escenarios 2 y 3. Este último caso es el mas complejo de los cuatro, pues combina la demanda variable del segundo caso y las distribuciones de operaciones específicas para cada tipo de componente.
4.1 Contexto inicial 33 Tabla 4.2 Demanda de operaciones específica de cada componente. Componente Distribución 1U[1,5] 2U[1,3] 3U[4,6] 4U[8,10] 5U[1,10] 6U[7,10] En los casos con demanda variable, basta con introducir una serie de "bucles if" que definan el valor del tiempo medio de espera dependiendo del tramo de la simulación. Para implementar la distinta probabilidad del numero de operaciones por componente, es mas complejo, pero es posible crear una lista que en cada elemento albergue los dos extremos de la distribución uniforme de cada componente. De este modo, la definición de un pedido nuevo, junto con la espera a su llegada y la definición del número de operaciones para componentes vendría dado por el siguiente código: Código 4.1 Codificación de la definición de un pedido y el tiempo de espera genérica para cualquiera de los escenarios estudiados. Qprob = [[1, 5], [1, 3], [4, 6], [8, 10], [1, 10], [7, 10]] # Uniform distribution limits for each component Tarrival = 4 # ’Type_Demand == ’Constant’ if Type_Demand == ’Variable’: instant = env.now if 200 <= instant < 400: Tarrival = 6 elif instant < 600: Tarrival = 2 elif instant < 800: Tarrival = 5 yield env.timeout(random.expovariate(1 / Tarrival)) # Time between new orders Q_comp = random.randint(2, 6) # Number of different types of components required components = [1, 2, 3, 4, 5, 6] Qjk = np.zeros(6) # List with the quantity of the component j for order k for tin range(Q_comp): ty = random.choice(components) components.remove(ty) if Type_Comp == ’Different’: Qjk[ty - 1] = random.randint(Qprob[ty - 1][0], Qprob[ty - 1][1]) # Number of component of type j else:# Type_Comp == ’Equal’ Qjk[ty - 1] = random.randint(1, 6) # Number of component of type j
34 Capítulo 4. Modelo de Simulación DDk = env.now + sum(Qjk) * random.randint(3, 5) # Deadline orderk = {’Qjk’: Qjk, ’DDk’: DDk, ’T0’: env.now} 4.2 Diseño Experimental El siguiente apartado define el experimento que se va a realizar en este estudio, se establecerán los parámetros fijados de simulación y que criterio se ha elegido para comparar con el planteado en el estudio original. 4.2.1 Ratio crítico En el trabajo de Renna et al. [1], se comparan varias estrategias de gestión de carga de trabajo. Las conclusiones del estudio determinan que ambas estrategias de Workload Control mejoraban en cuanto a sostenibilidad al tener menor nivel de utilización y reducían los inventarios de ensamblaje, con la contraprestación de ser algo menos eficientes en cuanto al ritmo de producción. En todo momento en su estudio se impone la ordenación de las colas internas mediante la regla de Earliest Due Date (fecha de entrega mas cercana), en este proyecto se comparará su modelo frente al mismo con ordenación de todas las colas bajo el criterio de del Ratio Crítico. Esta comparación entre EDD y Ratio Crítico, para un modelo de jobshop con estas características, es novedosa, no se ha realizado antes; y se puede entender como una ampliación del artículo original. El interés surge de intentar encontrar una mejora al modelo existente, variando una pequeña parte del mismo. Como se vio anteriormente, el criterio del Ratio Crítico compara el tiempo restante hasta la entrega con el tiempo estimado de fabricación. El cálculo es el siguiente, como se vio en la sección 2.3, incluyendo las variables propias de este modelo: CRk=DDk−t ˆ tprocessk =DDk−t ∑jQk j∗¯ tmanu f acture +¯ tassembly (4.5) El tiempo estimado ˆ tprocessk se puede calcular a partir de las medias de las distribuciones de fabricación de cada componente y ensamblaje ( ¯ tmanu f acture y ¯ tassembly ), lo que finalmente dependería del número total de componentes, es decir la suma de Qk j. En el caso de las colas de las máquinas, para ordenar las operaciones, el tiempo estimado es la media de tiempo de fabricación de un componente. En los buffers no hace falta ordenar las operaciones. Lo que se pretende al introducir esta regla de despacho es intentar darle prioridad a los pedidos y operaciones que llevan mas prisa con respecto al tiempo de fabricación asociado, y no solo porque su fecha de entrega esté cercana. 4.2.2 Características del estudio En el estudio se replicarán los 4 escenarios planteados en el artículo original, tanto para la estrategia de umbrales constantes como la de umbrales adaptables. Primero se tomará como criterio de ordenación de colas el EDD y se repetirá para el Ratio Crítico, teniendo en total 16 configuraciones distintas que simular (4 escenarios por 2 estrategias por 2 reglas de despacho). Cada una de estas configuraciones se simulará 1000 veces a lo largo de un horizonte de simulación tsim =1000 unidades de tiempo. Las variables que se medirán, son las propuestas por Renna et al.[1], explicadas la sección 2.4, los resultados y conclusiones se presentan en el siguiente capítulo. El trabajo original deja algunos parámetros sin aclarar, para este estudio se han tomado valores considerados razonables, entre los que se encuentran:
4.2 Diseño Experimental 35 • Número de simulaciones, nmax . Es el número de simulaciones por caso, esto es escenario, estrategia y regla de despacho, distinto. Se tomará igual mil iteraciones por caso, para asegurar un mínimo de convergencia de resultados. • Horizonte de simulación , tsim . Las unidades de tiempo que durará cada simulación individual, igual a mil unidades de tiempo, tal y como se establece en el artículo estudiado. • Umbral de WIP, ThrWIP Se toma de forma arbitraria igual a 4, ya que si la frecuencia de llegada es de un pedido cada 4 unidades de tiempo y el numero de operaciones por pedido medio es 14 unidades de tiempo 2 , contando el tiempo medio de fabricación de cada operación mas el tiempo de ensamblaje, sale una media de tiempo de procesado de 15 unidades de tiempo. Dividiendo la frecuencia de llegada entre el tiempo de procesado medio, da que de media, habrá 3.75 pedidos en proceso al mismo tiempo en la planta, con un umbral de 4 la planta es capaz de trabajar bien en un funcionamiento normal. • Parámetro de ajuste, K . En el documento estudiado, se hacen simulaciones para tres valores de este parámetro K=1,1.5,3 ; siendo el mejor resultado para K=3 ya que, como se explica en el propio estudio, se llega a cubrir el almacenaje de los componentes en el 99.73% de los casos, suponiendo una distribución normal. Se tomará este valor en la simulación de este estudio. Tabla 4.3 Parámetros de simulación. Parámetro Símbolo Valor Número de simulaciones nmax 1 000 Horizonte de simulación tsim 1 000 u.d. Umbral de WIP T hrW IP 4 Parámetro de ajuste de umbral K3 2 Teniendo como distribución de distintos componentes U[2,6] y la de numero de componentes de cada tipo U[1,6] , teniendo en cuenta que son distribuciones independientes, la media de componentes totales es el producto de las medias, es decir, 14 unidades de tiempo
5 Resultados A continuación, se van a comentar los resultados de las variables interés descritas en el apartado de Mediciones Interesantes (2.4). Se presentan los resultados para cada configuración del experimento. Se presentarán gráficos de barras para cada uno de los cuatro escenarios estudiados, para cada uno de ellos se mostrarán los resultados de cada estrategia ( Trhb constante frente a Thrb adaptable). Para cada estrategia se representará el nivel medio de la variable estudiada, representando en azul los resultados obtenidos usando la regla EDD como criterio de ordenación de colas y en naranja los correspondientes al ratio crítico. 5.1 Pedidos completados En la figura 5.1 se puede observar como se obtienen resultados similares para para cada escenario independientemente del criterio y estrategia. En primer lugar, se observa que el criterio de ordenación del ratio crítico no funciona tan bien como el EDD desde el punto de vista de la producción bruta. La diferencia máxima entre los dos criterios se produce en el escenario 4, el único caso donde el Ratio Crítico supone una ventaja, en concreto, usar el ratio crítico bajo estas condiciones supone completar un 1,79% mas de media (un 1,83% si sólo se tiene en cuenta la estrategia 1) que usando EDD. La diferencia mínima se encuentra en el segundo caso, donde las diferencias no son tan grandes, siendo el criterio del ratio un 0,20% peor de media ( 0.11% peor para el APP1 y un 0.28% en el APP2). En términos generales de producción, elegir el ratio crítico supone producir 0,19% menos si se escoge la estrategia de umbrales constantes, y un 0,17% menos con la estrategia de umbrales variables; que si se usasen las mismas estrategias con el criterio de fecha de entrega mas temprana. 5.2 Retraso A diferencia del caso anterior, observando los resultados brutos del retraso medio en la figura 5.2, es fácil darse cuenta que en los tres primeros casos, el retraso es negativo, lo que indica que las entregas se realizan antes de tiempo. Es en estos casos donde el criterio EDD parece superior al del Ratio Crítico, siendo la diferencia mayor en el caso de demanda variable (caso 2). En el caso base la diferencia es mínima (menos de un 2,30% con respecto al EDD para ambas estrategias). Sin embargo, en el caso 4, el retraso medio se vuelve positivo, es decir los pedidos comienzan a llegar tarde. Pero es mas interesante notar que es aquí donde el ratio crítico ha funcionado mejor en términos de retrasos, pues presenta un retraso medio menor que las simulaciones con EDD. De hecho es el caso donde la diferencia absoluta entre ambos criterios es mayor, las simulaciones en las que se ha usado el ratio crítico muestran un retraso de entorno al 12% menor que en las EDD, 37
38 Capítulo 5. Resultados Figura 5.1 Comparativa de los pedidos medios completados en cada configuración [Fuente: Elaboración propia]. Figura 5.2 Comparativa del retraso medio de los pedidos en cada configuración [Fuente: Elaboración propia]. siendo una pizca mejor aquellas que además presentaban la estrategia 2 ( 11,74% frente al 11,69% de la estrategia 1). Esto podría deberse a que el sistema se satura en términos de capacidad productiva en esta
5.3 Tiempo de procesado 39 configuración, la mezcla de alta demanda y que con los componentes diferenciados se tiene un número medio de componentes por pedido mas alto, consigue que el sistema no sea capaz de producir y terminar los encargos a tiempo. Encontrar la verdadera razón a este comportamiento se escapa al alcance de este proyecto y se deja pendiente de estudio su exploración. 5.3 Tiempo de procesado Figura 5.3 Comparativa del tiempo medio de procesado en cada configuración [Fuente: Elaboración propia]. Al igual que con el retraso medio, con el tiempo de procesado medio el criterio EDD es superior al del Ratio Crítico en todos los escenarios salvo en el último (ver figura 5.3). Se aprecia también que ambas estrategias son similares en términos de resultados, independientemente del criterio elegido, dicho esto, en los tres primeros casos, en promedio usar el ratio critico supone tiempos de procesado entorno a un 16% mas largos que usando EDD, sin embargo, en el último escenario es un 5,59% mejor que el EDD. Volviendo a relacionarlo con el retraso, se ve como en el caso 4 el tiempo de procesado aumenta mucho, lo que puede ser la causa de que el retraso se vuelva positivo en este escenario. El aumento del tiempo de procesado parece estar directamente ligado a la complejidad del escenario, pues en el caso 2 y 3 presenta niveles similares, pero es en la combinación de ambos donde mas aumenta este tiempo.
40 Capítulo 5. Resultados Figura 5.4 Comparativa de los pedidos en cola medios en cada configuración [Fuente: Elaboración propia]. 5.4 Pedidos en cola Para esta medida, existe una correlación similar al tiempo de procesado con la complejidad del escenario. Tener la escala misma escala en la figura 5.4 no ayuda a la visualización de los primeros escenarios, por ello se presenta la siguiente tabla de resultados, que también puede verse en el Apéndice B. Tabla 5.1 Resultados numéricos del nivel medio de los pedidos en cola, para las distintas configuraciones. Estrategia Criterio Escenario 1 2 3 4 APP1 EDD 0,17 5,48 2,91 42,14 CR 0,30 6,27 4,07 40,37 APP2 EDD 0,17 5,60 2,86 41,67 CR 0,29 6,36 3,99 39,88 Como se ve las colas medias se hacen mas y mas largas cada vez. Se puede comprobar que para el primer escenario no se suelen acumular mas de 3 pedidos y que la cola vuelve a caer a cero con frecuencia. Sin embargo, para los casos dos y tres, las colas comienzan a crecer, apareciendo "montañas" de pedidos en cola pero que vuelven a bajar a niveles mas bajos. En el último escenario se da un caso extremo, y es que la cola no para de crecer, indefinidamente, salvo en contadas ocasiones; esto sucede porque el sistema no es capaz de producir al ritmo de la demanda, es por
5.5 Pedidos en proceso 41 esto por lo que el retraso se vuelve positivo en este caso. Comentando los resultados, se puede ver como ocurre algo similar al las variables comentadas con anterioridad; en todos los casos salvo el último el criterio EDD es algo superior (menores colas) que el Ratio Crítico. En concreto, la mayor diferencia se da en el primer caso, llegando a colas un 75,25% mas largas de media en usando el CR frente al EDD (se da para el caso del escenario 1 con estrategia APP1). Para el último escenario, ambas estrategias dan resultados similares, pero aquellas en la que se utiliza el criterio de la razón crítica suponen unas colas un 4,25% menores de media entre la estrategia 1 y 2. 5.5 Pedidos en proceso Figura 5.5 Comparativa de los niveles de WIP medios en cada configuración [Fuente: Elaboración propia]. Se presentan unos resultados muy similares para cualquier caso y configuración del taller, las diferencias son mínimas entre las distintas combinaciones de criterios y estrategias. Se puede ver claramente como nunca se sobre pasa el límite impuesto por el criterio de Workload Control de ThrWIP =4, que ocurriera esto significaría que algo no funciona en el modelo. Es interesante destacar que los mayores niveles de WIP se dan en el caso 4, independientemente del criterio y la estrategia, estando todos muy cercanos entre ellos (todos se aproximan a la centésima a 2,25 ). Los escenarios 2 y 3 son muy similares, entorno a 2,5 y 3 pedidos en proceso de media, respectivamente; mientras que en el primer caso, el numero medio de pedidos en proceso es significativamente menor que en el resto, no llega a 2 pedidos de media.
48 Apéndice A. Código general Mean_Delay_APP2_EDD = np.zeros(4) Mean_Queue_APP2_EDD = np.zeros(4) Mean_Throughput_APP2_EDD = np.zeros(4) Mean_WIP_APP2_EDD = np.zeros(4) Mean_Utilization_APP2_EDD = np.zeros(4) Mean_Buffer_APP2_EDD = np.zeros(4) Dev_Buffer_APP2_EDD = np.zeros(4) Mean_Completed_APP2_EDD = np.zeros(4) Mean_Delay_APP1_CR = np.zeros(4) Mean_Queue_APP1_CR = np.zeros(4) Mean_Throughput_APP1_CR = np.zeros(4) Mean_WIP_APP1_CR = np.zeros(4) Mean_Utilization_APP1_CR = np.zeros(4) Mean_Buffer_APP1_CR = np.zeros(4) Dev_Buffer_APP1_CR = np.zeros(4) Mean_Completed_APP1_CR = np.zeros(4) Mean_Delay_APP2_CR = np.zeros(4) Mean_Queue_APP2_CR = np.zeros(4) Mean_Throughput_APP2_CR = np.zeros(4) Mean_WIP_APP2_CR = np.zeros(4) Mean_Utilization_APP2_CR = np.zeros(4) Mean_Buffer_APP2_CR = np.zeros(4) Dev_Buffer_APP2_CR = np.zeros(4) Mean_Completed_APP2_CR = np.zeros(4) # Process definitions # GENERAL def process_newOrder(env): # New order arrives, its data is generated and passed to the preshop global orders, preshop, flagarrival global level_queue, t_level_queue while True: Tarrival = 4 # ’Type_Demand == ’Constant’ if Type_Demand == ’Variable’: instant = env.now if 200 <= instant < 400: Tarrival = 6 elif instant < 600: Tarrival = 2 elif instant < 800: Tarrival = 5 yield env.timeout(random.expovariate(1 / Tarrival)) # Time between new orders
49 Q_comp = random.randint(2, 6) # Number of different types of components required components = [1, 2, 3, 4, 5, 6] Qjk = np.zeros(6) # List with the quantity of the component j for order k for tin range(Q_comp): ty = random.choice(components) components.remove(ty) if Type_Comp == ’Different’: Qjk[ty - 1] = random.randint(Qprob[ty - 1][0], Qprob[ty - 1][1]) # Number of component of type j else:# Type_Comp == ’Equal’ Qjk[ty - 1] = random.randint(1, 6) # Number of component of type j DDk = env.now + sum(Qjk) * random.randint(3, 5) # Deadline orderk = {’Qjk’: Qjk, ’DDk’: DDk, ’T0’: env.now} # Save the new order orders.append(orderk) # Introduce the order in the preshop preshop.append(orderk) # The preshop is a list filled with orders waiting to be released # Queue performance Measurements level_queue.append(len(preshop) - 1) # Before addition t_level_queue.append(env.now) level_queue.append(len(preshop)) # After addition t_level_queue.append(env.now) flagarrival.succeed() # Flag event new order has arrived flagarrival = env.event() # Reset flag def process_manufacture(env, mach): global queue_mach, buffers, t_level_buffer, level_buffer, WIP, t_utilization global flagqueue, flagbuffer while True: if len(queue_mach[’machine ’ +str(mach)]) > 0: # First the machine queue is ordered queue_mach[’machine ’ +str(mach)] = sorted(queue_mach[’ machine ’ +str(mach)], key=lambda d: d[’DDk’]) inprocess = queue_mach[’machine ’ +str(mach)].pop(0) # The first operation in queue is picked # Manufacturing start
50 Apéndice A. Código general inprocess[’T_mach’] = env.now tstart = env.now # For the utilization calculation yield env.timeout(random.expovariate(1)) # Manufacturing time # Performance measurements t_utilization[mach - 1] += env.now - tstart # Send order to buffer comp = inprocess[’Comp’] inprocess[’T_buff’] = env.now buffers[’Buffer ’ +str(comp)].append(inprocess) # Performance measurements t_level_buffer.append(env.now) instant_level = 0 for jin range(len(buffers)): instant_level += len(buffers[’Buffer ’ +str(j + 1)]) / len(buffers) # Mean between all the buffers level_buffer.append(instant_level) flagbuffer.succeed() # Buffer update flag flagbuffer = env.event() # Flag reset else: # Wait for the machine queue to update yield flagqueue def process_assembly(env): global buffers, WIP, finished, t_level_buffer, level_buffer global flagbuffer, flagassembled, flagqueue, flagbuffout while True: if len(WIP) > 0: # Firstly, organise the WIP if sortmode == ’EDD’:# If sorting EDD sort the queue by Earliest Due Date WIP = sorted(WIP, key=lambda d: d[’DDk’]) elif sortmode == ’CritRat’:# If sorting with Critical Ratio mode instant = env.now WIP = sorted(WIP, key=lambda d: (d[’DDk’] - instant) / ( sum(d[’Qjk’]) * 1 + 1)) inprocess = WIP[0] ready = True for jin range(len(buffers)): # Check if buffer j has enough components for the order k (bj > Qjk) if len(buffers[’Buffer ’ +str(j + 1)]) < inprocess[’Qjk’ ][j]: ready = False
51 if ready: inprocess[’T_ass’] = env.now # The moment the order enters assembly operations = [] for jin range(len(buffers)): # Extract the operations needed from buffer j operationj = buffers[’Buffer ’ +str(j + 1)][:int( inprocess[’Qjk’][j])] del buffers[’Buffer ’ +str(j + 1)][:int(inprocess[’ Qjk’][j])] # This operations are added to a list with the used operations operations += operationj flagbuffout.succeed() # Extraction from buffer completed flagbuffout = env.event() # Reset flag # Performance measurements t_level_buffer.append(env.now) instant_level = 0 for jin range(len(buffers)): instant_level += len(buffers[’Buffer ’ +str(j + 1)]) /len(buffers) # Mean of all the buffers level_buffer.append(instant_level) # Add the used operations to the finished order inprocess[’operations’] = operations yield env.timeout(random.expovariate(1)) # Assembly time inprocess[’T_out’] = env.now # Performance measurements (before step) level_wip.append(len(WIP)) t_level_wip.append(env.now) # Write the order in the finished list finished.append(inprocess) del WIP[0] flagassembled.succeed() # Finished order event flag flagassembled = env.event() # Reset flag # Performance measurements (after step) level_wip.append(len(WIP)) t_level_wip.append(env.now) else: # Wait for buffer update yield flagbuffer else: yield flagqueue # Wait for new order in the WIP
52 Apéndice A. Código general # APPROACH 1 def process_releaseAPP1(env): global preshop, WIP global flagqueue, flagbuffout, flagassembled while True: if len(preshop) > 0: b = np.zeros(6) # Temporary list with the lenghts of the buffers for jin range(6): b[j] = len(buffers[’Buffer ’ +str(j + 1)]) if len(WIP) < Thr_wip and max(max(Thrb - b), 0): # First & Second workload policy respectively queuetorelease = [] # Generation of a list of operations to release to the machines # Firstly, organise the Preshop if sortmode == ’EDD’:# If sorting EDD sort the queue by Earliest Due Date preshop = sorted(preshop, key=lambda d: d[’DDk’]) elif sortmode == ’CritRat’:# If sorting with Critical Ratio mode instant = env.now preshop = sorted(preshop, key=lambda d: (d[’DDk’] - instant) / (sum(d[’Qjk’]) * 1 + 1)) order = preshop[0] Qjk = order[’Qjk’] DDk = order[’DDk’] for component in range(len(Qjk)): if Qjk[component] != 0: for jj in range(int(Qjk[component])): queuetorelease.append({’DDk’: DDk, ’Comp’: component + 1}) # An operation is # assigned as a dict with properties # Performance Measurements before step level_queue.append(len(preshop)) t_level_queue.append(env.now) level_wip.append(len(WIP)) t_level_wip.append(env.now) # Moving orders from preshop to Work-In-Progress list WIP.append(preshop[0]) del preshop[0] # Performance Measurements after step level_queue.append(len(preshop)) t_level_queue.append(env.now)
53 level_wip.append(len(WIP)) t_level_wip.append(env.now) # Release of the generated operations for operation in queuetorelease: mach = sorted(queue_mach, key=lambda d: len(queue_mach [d]))[ 0] # Extract which machine has a shorter queue queue_mach[mach].append(operation) # Operation is introduced in shorter queue flagqueue.succeed() # Queue update event flag flagqueue = env.event() # Event Reset else: yield flagbuffout or flagassembled # Wait for either the WIP reduction or the buffers update else: yield flagarrival # APPROACH 2 def process_releaseAPP2(env): global preshop, WIP global flagqueue, flagbuffout, flagassembled while True: if len(preshop) > 0: b = np.zeros(6) # Temporary list with the lengths of the buffers for jin range(6): b[j] = len(buffers[’Buffer ’ +str(j + 1)]) Q = np.zeros([6, len(orders)]) # Array with the number of components needed for each order for kin range(len(orders)): for jin range(6): Q[j, k] = orders[k][’Qjk’][j] aveq = np.array(list(map(lambda x: np.average(Q[x, :]), range (6)))) # Average number of components of # each type needed varq = np.array(list(map(lambda x: np.var(Q[x, :]), range(6)) )) # Variance of each of the components Thrbj = aveq + K * varq # Updatable buffer threshold for each component if len(WIP) < Thr_wip and max(max(Thrbj - b), 0): # First & Second workload policy respectively # Generation of a list of operations to release to the machines queuetorelease = [] # Firstly, organise the Preshop
54 Apéndice A. Código general if sortmode == ’EDD’:# If sorting EDD sort the queue by Earliest Due Date preshop = sorted(preshop, key=lambda d: d[’DDk’]) elif sortmode == ’CritRat’:# If sorting with Critical Ratio mode instant = env.now preshop = sorted(preshop, key=lambda d: (d[’DDk’] - instant) / (sum(d[’Qjk’]) * 1 + 1)) order = preshop[0] Qjk = order[’Qjk’] DDk = order[’DDk’] for component in range(len(Qjk)): if Qjk[component] != 0: for jj in range(int(Qjk[component])): queuetorelease.append({’DDk’: DDk, ’Comp’: component + 1}) # An operation is # assigned as a dict with properties # Performance Measurements before step level_queue.append(len(preshop)) t_level_queue.append(env.now) level_wip.append(len(WIP)) t_level_wip.append(env.now) # Moving orders from preshop to Work-In-Progress list WIP.append(preshop[0]) del preshop[0] # Performance Measurements after step level_queue.append(len(preshop)) t_level_queue.append(env.now) level_wip.append(len(WIP)) t_level_wip.append(env.now) # Release of the generated operations for operation in queuetorelease: mach = sorted(queue_mach, key=lambda d: len(queue_mach [d]))[0] # Extract which machine has a # shorter queue queue_mach[mach].append(operation) # Operation is introduced in shorter queue flagqueue.succeed() # Queue update event flag flagqueue = env.event() # Event Reset else: yield flagbuffout or flagassembled # Wait for either the WIP reduction or the buffers update else: yield flagarrival
55 print(’--- Processes defined at %s seconds ---’ % (time.time() - start_time)) print(’===== SIMULATION START =====’) # Define and Run each of the cases case_counter = 0 # Index of the case being studied for case in Case_config: Type_Demand, Type_Comp = case # Performance measuerements # Approach 1 Delay_APP1_EDD = np.zeros(nmaxiter) # Mean delay of each run Level_Queue_APP1_EDD = np.zeros(nmaxiter) # Mean number of orders in preshop Throughput_APP1_EDD = np.zeros(nmaxiter) # Mean order throughput time (Tout-T0) for each run Level_WIP_APP1_EDD = np.zeros(nmaxiter) # Mean number of orders being worked on at the same time for each run Utilization_APP1_EDD = np.zeros(nmaxiter) # Mean fraction of the time a machine is working Level_Buffer_APP1_EDD = np.zeros(nmaxiter) # Mean of the mean buffer level for each buffer Completed_APP1_EDD = np.zeros(nmaxiter) # Number of orders completed each run Delay_APP1_CR = np.zeros(nmaxiter) # Mean delay of each run Level_Queue_APP1_CR = np.zeros(nmaxiter) # Mean number of orders in preshop Throughput_APP1_CR = np.zeros(nmaxiter) # Mean order throughput time (Tout-T0) for each run Level_WIP_APP1_CR = np.zeros(nmaxiter) # Mean number of orders being worked on at the same time for each run Utilization_APP1_CR = np.zeros(nmaxiter) # Mean fraction of the time a machine is working Level_Buffer_APP1_CR = np.zeros(nmaxiter) # Mean of the mean buffer level for each buffer Completed_APP1_CR = np.zeros(nmaxiter) # Number of orders completed each run # Approach 2 (K = 3) Delay_APP2_EDD = np.zeros(nmaxiter) # Mean delay of each run Level_Queue_APP2_EDD = np.zeros(nmaxiter) # Mean number of orders in preshop Throughput_APP2_EDD = np.zeros(nmaxiter) # Mean order throughput time (Tout-T0) for each run Level_WIP_APP2_EDD = np.zeros(nmaxiter) # Mean number of orders being worked on at the same time for each run
56 Apéndice A. Código general Utilization_APP2_EDD = np.zeros(nmaxiter) # Mean fraction of the time a machine is working Level_Buffer_APP2_EDD = np.zeros(nmaxiter) # Mean of the mean buffer level for each buffer Completed_APP2_EDD = np.zeros(nmaxiter) # Number of orders completed each run Delay_APP2_CR = np.zeros(nmaxiter) # Mean delay of each run Level_Queue_APP2_CR = np.zeros(nmaxiter) # Mean number of orders in preshop Throughput_APP2_CR = np.zeros(nmaxiter) # Mean order throughput time (Tout-T0) for each run Level_WIP_APP2_CR = np.zeros(nmaxiter) # Mean number of orders being worked on at the same time for each run Utilization_APP2_CR = np.zeros(nmaxiter) # Mean fraction of the time a machine is working Level_Buffer_APP2_CR = np.zeros(nmaxiter) # Mean of the mean buffer level for each buffer Completed_APP2_CR = np.zeros(nmaxiter) # Number of orders completed each run # Consecutive simulations # APP1: EDD sortmode = ’EDD’ for niter in range(nmaxiter): # The process is simulated nmaxiter times orders = [] # List with every order and its data preshop = [] # List with the orders queuing for enter production (not operations) WIP = [] # List with the orders currently being worked on finished = [] # List with the finished orders # Medidores level_arrivals = [0] level_dismissed = [0] t_level_orders = [0] ## Performance measurements # Queue level_queue = [0] t_level_queue = [0] # WIP level_wip = [0] t_level_wip = [0] # Utilization t_utilization = np.zeros(6) # Buffers t_level_buffer = [0] # List with the sample times of the buffers levels
57 level_buffer = [0] # List with the samples of the average buffer level (sum of all 6 levels divided by 6) # Simpy Enviroment enviroment = simpy.Environment() arrival = enviroment.process(process_newOrder(enviroment)) release = enviroment.process(process_releaseAPP1(enviroment)) # Definition of the machines and their queues queue_mach = {} for iin range(6): queue_mach[’machine ’ +str(i+1)]=[]# Queues are defines as a dictionary with lists of dictionaries machine1 = enviroment.process(process_manufacture(enviroment, 1)) machine2 = enviroment.process(process_manufacture(enviroment, 2)) machine3 = enviroment.process(process_manufacture(enviroment, 3)) machine4 = enviroment.process(process_manufacture(enviroment, 4)) machine5 = enviroment.process(process_manufacture(enviroment, 5)) machine6 = enviroment.process(process_manufacture(enviroment, 6)) # Buffers definition buffers = {} for iin range(6): buffers[’Buffer ’ +str(i + 1)] = [] # Buffers are defined as a dictionary of lists # Definicion assembly assembly = enviroment.process(process_assembly(enviroment)) # Definition of update flags flagarrival = enviroment.event() # Flag that warns a new order has arrived flagbuffer = enviroment.event() # Flag that warns a new component has entered the buffer flagbuffout = enviroment.event() # Flag that warns components have left the buffers flagqueue = enviroment.event() # Flag that warns a new component has entered the machines queues flagassembled = enviroment.event() # Flag that warns an order has been finished enviroment.run(until=tsim)
64 Apéndice A. Código general ## Performance measurements # Queue level_queue = [0] t_level_queue = [0] # WIP level_wip = [0] t_level_wip = [0] # Utilization t_utilization = np.zeros(6) # Buffers t_level_buffer = [0] # List with the sample times of the buffers levels level_buffer = [0] # List with the samples of the average buffer level (sum of all 6 levels divided by 6) # Simpy Enviroment enviroment = simpy.Environment() arrival = enviroment.process(process_newOrder(enviroment)) release = enviroment.process(process_releaseAPP2(enviroment)) # Definition of the machines and their queues queue_mach = {} for iin range(6): queue_mach[’machine ’ +str(i+1)]=[]# Queues are defines as a dictionary with lists of dictionaries machine1 = enviroment.process(process_manufacture(enviroment, 1)) machine2 = enviroment.process(process_manufacture(enviroment, 2)) machine3 = enviroment.process(process_manufacture(enviroment, 3)) machine4 = enviroment.process(process_manufacture(enviroment, 4)) machine5 = enviroment.process(process_manufacture(enviroment, 5)) machine6 = enviroment.process(process_manufacture(enviroment, 6)) # Buffers definition buffers = {} for iin range(6): buffers[’Buffer ’ +str(i + 1)] = [] # Buffers are defined as a dictionary of lists # Definicion assembly assembly = enviroment.process(process_assembly(enviroment)) # Definition of update flags
65 flagarrival = enviroment.event() # Flag that warns a new order has arrived flagbuffer = enviroment.event() # Flag that warns a new component has entered the buffer flagbuffout = enviroment.event() # Flag that warns components have left the buffers flagqueue = enviroment.event() # Flag that warns a new component has entered the machines queues flagassembled = enviroment.event() # Flag that warns an order has been finished enviroment.run(until=tsim) # Run performance results delayk = [] Tprocessk = [] Completed_APP2_CR[niter] = len(finished) for order in finished: delayk.append(order[’T_out’] - order[’DDk’]) # Delay Tprocessk.append(order[’T_out’] - order[’T0’]) # Process time Delay_APP2_CR[niter] = sum(delayk) / len(delayk) Throughput_APP2_CR[niter] = sum(Tprocessk) / len(Tprocessk) # Queue Level_Queue_APP2_CR[niter] = np.average(level_queue[1:], weights =np.diff(t_level_queue)) # WIP Level_WIP_APP2_CR[niter] = np.average(level_wip[1:], weights=np. diff(t_level_wip)) # Utilization Utilization_APP2_CR[niter] = sum(t_utilization) / 6 / tsim # Total utilization/ number of machines/ sim time # Buffer Level_Buffer_APP2_CR[niter] = np.average(level_buffer[1:], weights=np.diff(t_level_buffer)) # Final Results Mean_Delay_APP2_CR[case_counter] = sum(Delay_APP2_CR * Completed_APP2_CR) / sum(Completed_APP2_CR) Mean_Queue_APP2_CR[case_counter] = sum(Level_Queue_APP2_CR) / nmaxiter Mean_Throughput_APP2_CR[case_counter] = sum(Throughput_APP2_CR * Completed_APP2_CR) / sum(Completed_APP2_CR) Mean_WIP_APP2_CR[case_counter] = sum(Level_WIP_APP2_CR) / nmaxiter Mean_Utilization_APP2_CR[case_counter] = sum(Utilization_APP2_CR) / nmaxiter Mean_Buffer_APP2_CR[case_counter] = sum(Level_Buffer_APP2_CR) / nmaxiter Dev_Buffer_APP2_CR[case_counter] = np.std(Level_Buffer_APP2_CR)
66 Apéndice A. Código general Mean_Completed_APP2_CR[case_counter] = sum(Completed_APP2_CR) / nmaxiter print(’--- FINISHED: CASE ’, case, ’, APP2, Critical Ratio, at %s seconds ---’ % (time.time() - start_time)) # Update Case counter case_counter += 1 # Global Results graphs print(’--- FINISHED SIMULATIONS at %s seconds ---’ % (time.time() - start_time)) print(’===== START PLOTTING =====’) Titles = [’Mean Delay’,’Mean Queue Level’,’Mean Throughput Time’,’ Mean WIP Level’, ’Mean Utilization Level’,’Mean Buffer Level’,’Buffers Std. Dev.’,’Mean Completed Orders’] SubTitles = [’1: Basic’,’2: Variable Demand’, ’3: Variable Components’,’4: Mix 2 & 3’] y_lim = [[-70, 55], [0, 90], [0, 150], [0, 4.5], [0, 1], [0, 8], [0, 1], [0, 350]] Means_APP1_EDD = [Mean_Delay_APP1_EDD, Mean_Queue_APP1_EDD, Mean_Throughput_APP1_EDD, Mean_WIP_APP1_EDD, Mean_Utilization_APP1_EDD, Mean_Buffer_APP1_EDD, Dev_Buffer_APP1_EDD, Mean_Completed_APP1_EDD] Means_APP2_EDD = [Mean_Delay_APP2_EDD, Mean_Queue_APP2_EDD, Mean_Throughput_APP2_EDD, Mean_WIP_APP2_EDD, Mean_Utilization_APP2_EDD, Mean_Buffer_APP2_EDD, Dev_Buffer_APP2_EDD, Mean_Completed_APP2_EDD] Means_APP1_CR = [Mean_Delay_APP1_CR, Mean_Queue_APP1_CR, Mean_Throughput_APP1_CR, Mean_WIP_APP1_CR, Mean_Utilization_APP1_CR, Mean_Buffer_APP1_CR, Dev_Buffer_APP1_CR, Mean_Completed_APP1_CR] Means_APP2_CR = [Mean_Delay_APP2_CR, Mean_Queue_APP2_CR, Mean_Throughput_APP2_CR, Mean_WIP_APP2_CR, Mean_Utilization_APP2_CR, Mean_Buffer_APP2_CR, Dev_Buffer_APP2_CR, Mean_Completed_APP2_CR] Approaches = [’APP1’,’APP2 (K=3)’] Criteria = [’EDD’,’CR’] pos = np.arange(len(Approaches)) bar_width = 0.35 for fig_id in range(len(Titles)): fig = plt.figure(Titles[fig_id]) for subfig_id in range(len(SubTitles)):
67 plt.subplot(2, 2, subfig_id + 1) plt.bar(pos, [Means_APP1_EDD[fig_id][subfig_id], Means_APP2_EDD[ fig_id][subfig_id]], bar_width, edgecolor=’black’) plt.bar(pos + bar_width, [Means_APP1_CR[fig_id][subfig_id], Means_APP2_CR[fig_id][subfig_id]], bar_width, edgecolor=’black’) plt.xticks(pos+bar_width/2, Approaches) plt.xlabel(’Approach’, fontsize=’x-large’) plt.legend(Criteria, loc=0, fontsize=’x-large’) plt.grid(True, axis=’y’) plt.title(SubTitles[subfig_id], fontsize=’x-large’) plt.ylim(y_lim[fig_id]) plt.subplots_adjust(hspace=0.3) fig.set_size_inches(12, 15) plt.savefig(Titles[fig_id] + ’.png’, dpi=100) print(’--- FINISHED PLOTTING AT %s seconds ---’ % (time.time() - start_time)) print(’===== START DATA SAVING =====’) # Save the data to a file Columns = [’Case’,’Approach’,’Sorting Criteria’,’Demand Type’,’ Components type’] + Titles Cases = [1, 2, 3, 4] Data = [] # Where is going to be stored for case_id in range(len(Cases)): a = [Cases[case_id], Approaches[0], Criteria[0], Case_config[case_id ][0], Case_config[case_id][1]] b = [Cases[case_id], Approaches[1], Criteria[0], Case_config[case_id ][0], Case_config[case_id][1]] c = [Cases[case_id], Approaches[0], Criteria[1], Case_config[case_id ][0], Case_config[case_id][1]] d = [Cases[case_id], Approaches[1], Criteria[1], Case_config[case_id ][0], Case_config[case_id][1]] for column_id in range(len(Titles)): a.append(Means_APP1_EDD[column_id][case_id]) b.append(Means_APP2_EDD[column_id][case_id]) c.append(Means_APP1_CR[column_id][case_id]) d.append(Means_APP2_CR[column_id][case_id]) Data.append(a) Data.append(b) Data.append(c) Data.append(d) Data = pd.DataFrame(Data, columns=Columns) Data.to_csv(’Global_Data.csv’, index=False)
68 Apéndice A. Código general print(’--- FINISHED: DATA SAVING at %s seconds ---’ % (time.time() - start_time)) plt.show()
Apéndice B Tablas de Resultados Tabla B.1 Tabla de Resultados I. Case Approach Sorting Criteria Demand Type Components type Mean Delay 1 APP1 EDD Constant Equal -48,031 1 APP2 (K=3) EDD Constant Equal -48,097 1 APP1 CR Constant Equal -46,939 1 APP2 (K=3) CR Constant Equal -47,026 2 APP1 EDD Variable Equal -30,871 2 APP2 (K=3) EDD Variable Equal -30,569 2 APP1 CR Variable Equal -28,104 2 APP2 (K=3) CR Variable Equal -27,875 3 APP1 EDD Constant Different -64,704 3 APP2 (K=3) EDD Constant Different -64,904 3 APP1 CR Constant Different -59,371 3 APP2 (K=3) CR Constant Different -59,643 4 APP1 EDD Variable Different 55,820 4 APP2 (K=3) EDD Variable Different 54,859 4 APP1 CR Variable Different 49,296 4 APP2 (K=3) CR Variable Different 48,419 69
70 Apéndice B. Tablas de Resultados Tabla B.2 Tabla de Resultados II. Case Approach Sorting Criteria Mean Queue Level Mean Throughput Time Mean WIP Level 1 APP1 EDD 0,172 7,801 1,784 1 APP2 (K=3) EDD 0,169 7,795 1,780 1 APP1 CR 0,302 8,916 1,927 1 APP2 (K=3) CR 0,292 8,846 1,914 2 APP1 EDD 5,481 25,043 2,580 2 APP2 (K=3) EDD 5,596 25,391 2,586 2 APP1 CR 6,269 27,752 2,654 2 APP2 (K=3) CR 6,361 28,042 2,651 3 APP1 EDD 2,908 22,352 2,782 3 APP2 (K=3) EDD 2,858 22,195 2,780 3 APP1 CR 4,075 27,780 2,930 3 APP2 (K=3) CR 3,990 27,537 2,922 4 APP1 EDD 42,139 142,502 3,249 4 APP2 (K=3) EDD 41,667 141,599 3,252 4 APP1 CR 40,366 134,266 3,256 4 APP2 (K=3) CR 39,882 133,385 3,257
71 Tabla B.3 Tabla de Resultados III. Case Approach Sorting Criteria Mean Utilization Level Mean Buffer Level Buffers Std. Dev. Mean Completed Orders 1 APP1 EDD 0,582 2,719 0,265 248,676 1 APP2 (K=3) EDD 0,580 2,712 0,254 247,848 1 APP1 CR 0,580 2,747 0,262 247,782 1 APP2 (K=3) CR 0,578 2,736 0,258 247,271 2 APP1 EDD 0,750 3,383 0,216 320,401 2 APP2 (K=3) EDD 0,751 3,387 0,211 320,792 2 APP1 CR 0,748 3,548 0,257 320,043 2 APP2 (K=3) CR 0,749 3,541 0,247 319,896 3 APP1 EDD 0,891 5,171 0,329 243,414 3 APP2 (K=3) EDD 0,890 5,185 0,324 242,874 3 APP1 CR 0,887 5,654 0,332 241,839 3 APP2 (K=3) CR 0,885 5,650 0,346 241,405 4 APP1 EDD 0,962 4,801 0,157 264,152 4 APP2 (K=3) EDD 0,963 4,794 0,155 264,274 4 APP1 CR 0,963 6,196 0,273 268,976 4 APP2 (K=3) CR 0,963 6,193 0,281 268,904
Índice de Figuras 1.1 Representación del layout del taller [Fuente: Renna et al.] 2 2.1 Retrato de Friedrick W. Taylor [Fuente: www.biografiasyvidas.com [3]] 3 2.2 Retrato de Henry L. Gantt [Fuente: www.biografiasyvidas.com [4]] 4 2.3 Una linea de producción de ruedas en 2014 en Jianxing, China [Fuente: www.nytimes.com] 5 2.4 Diagrama comparativo entre los sistemas push ypull [Fuente: Elaboración Propia] 6 2.5 Final Assembly Line del A400M en San Pablo, Sevilla [Fuente: fly-news.es] 7 2.6 Gráfico tiempo de entrega frente a grado de personalización cualitativo de las regiones donde es mas útil aplicar un sistema de los comentados [Fuente: International Journal of Operations & Production Management [6]] 9 3.1 Experiencia del calculo de pi, mediante Montecarlo, se ve la tendencia hacia el valor [Fuente: engaging-data.com] 15 3.2 Gráfico sacado de una simulación continua de un modelo depredador-presa [Fuente: McHaney, R. [8]] 16 3.3 Diagrama donde se puede ver como se entrelazan las pausas en una simulación de eventos discretos con dos líneas paralelas, las paradas corresponderían con una simulación next-event advance [Fuente: Elaboración propia] 17 3.4 Esquema lógico del proceso de llegada de pedidos [Fuente: Elaboración propia] 21 3.5 Esquema lógico del proceso de liberación de pedidos [Fuente: Elaboración propia] 23 3.6 Esquema lógico del proceso de procesamiento de operaciones en una máquina genérica [Fuente: Elaboración propia] 25 3.7 Esquema lógico del proceso de montaje de pedidos [Fuente: Elaboración propia] 26 4.1 Esquemático de todas las entidades que intervienen en el proceso [Fuente: Elaboración propia] 30 5.1 Comparativa de los pedidos medios completados en cada configuración [Fuente: Elaboración propia] 38 5.2 Comparativa del retraso medio de los pedidos en cada configuración [Fuente: Elaboración propia] 38 5.3 Comparativa del tiempo medio de procesado en cada configuración [Fuente: Elaboración propia] 39 5.4 Comparativa de los pedidos en cola medios en cada configuración [Fuente: Elaboración propia] 40 5.5 Comparativa de los niveles de WIP medios en cada configuración [Fuente: Elaboración propia] 41 5.6 Comparativa de la utilización media en cada configuración [Fuente: Elaboración propia] 42 5.7 Comparativa de los niveles medios de los búferes en cada configuración [Fuente: Elaboración propia] 43 73