-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcap2.tex
678 lines (573 loc) · 38.5 KB
/
cap2.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
\chapter{Estrategia de resolución mediante acoplamiento de códigos}
\label{chap2}
\chapterquote
%~ {We build too many walls and not enough bridges.}
{Construimos demasiadas murallas y no suficientes puentes.}
{Sir Isaac Newton, 1643-1727}
\section{Paradigma maestro-esclavo}
\label{2:maestro-esclavo}
La estrategia general para la resolución de problemas acoplados mediante el Método de Descomposición Disjunta de Dominios es comentada a continuación.
Los problemas parciales pertenecientes a distintos subdominios se resuelven con diferentes códigos específicos.
Cada uno de estos códigos recibe valores de condiciones de borde y en base a ellos se encarga de calcular el valor de las incógnitas en las interfaces de acople mediante las ecuaciones \ref{ecuaciones-modelos}.
Existe un único programa que acopla los resultados.
Este programa se abstrae completamente de la física implicada en cada subsistema.
Simplemente recibe los valores para las incógnitas calculados por los diferentes programas y con ellos resuelve el sistema de ecuaciones de residuos \ref{sistema-simple}.
En base a estos residuos propone\footnote{
Esta propuesta reside en el método de resolución de ecuaciones no lineales seleccionado, ver sección \ref{1:metodos}.
} nuevos valores para las incógnitas en las interfaces de acople y los comunica nuevamente a los demás programas.
Este tipo de comunicación utilizado recibe el nombre de \textit{maestro-esclavo} \cite{maestro-esclavo},
en el cual existe un programa \textit{maestro} que tiene el control unidireccional sobre los demás programas, que actúan bajo el rol de \textit{esclavos}.
Además del envío y recepción de valores de variables, son funciones del código \textit{maestro} el envío de órdenes a sus \textit{esclavos} de comenzar el cálculo en un dado paso de evolución, reiniciar el cálculo o incluso abortar.
Cabe notar que cada código \textit{esclavo} podría ejecutarse en varios procesos, paralelizando sus cálculos,
ya sea mediante memoria compartida como mediante memoria distribuida.
Incluso podrían lanzarse diferentes procesos del código \textit{maestro} en la resolución de algún problema.
Debido a la complejidad en destinatarios de mensajes, cantidades y tipos de variables a compartir,
es necesario definir una estrategia clara de comunicación
que permita acoplar diversos códigos de manera genérica, segura y eficaz.
La estrategia definida es comentada en la siguiente sección.
\section{Modelos de comunicación}
\label{2:comunicacion}
La configuración \textit{maestro-esclavo} requiere la ejecución de múltiples programas independientes.
Al mismo tiempo, cada código podría estar corriendo en forma paralelizada\footnote{
En general, cada programa \textit{esclavo} es un programa que ya ha sido utilizado y validado para algún tipo de cálculo.
Si el código está paralelizado, en base a estudios de \textit{speedup} podría tenerse cierta experiencia en su modo de ejecución óptimo para alguna tarea dada.
Esta ejecución podría requerir múltiples nodos en un clúster, por ejemplo.
}.
Debido a esta complejidad es necesario planificar la estrategia de comunicación considerando la distribución del cálculo en múltiples procesadores,
con el objetivo de no perder generalidad en la herramienta desarrollada.
Los modelos de comunicación implementados son los siguientes:
\begin{itemize}
\item Paso de mensajes: este modelo es implementado para comunicar procesos de programas en los cuales es posible modificar sus códigos fuente.
\item Lectura y escritura de archivos de entrada y salida: este modelo es implementado para comunicar procesos de programas en los cuales no es posible modificar sus códigos fuente.
\end{itemize}
En ambos casos los programas esclavos se comunican solo con el programa \textit{maestro}.
Si bien estos modelos involucran comunicaciones remotas que son más lentas que las
comunicaciones locales, en general su latencia es despreciable frente al tiempo de cálculo de los procesos \textit{esclavos}.
\subsection*{Paso de mensajes}
\label{2:mpi}
En sistemas de memoria distribuida, el paso de mensajes es un método de
programación utilizado para realizar el intercambio de datos entre
procesos \cite{comunicacion}.
El paso de mensajes involucra la transferencia de datos desde un proceso
que envía a otro proceso que recibe.
El proceso que envía,
necesita conocer la localización, el tamaño y el tipo de los datos, así como el
proceso destino.
Estas funcionalidades se implementaron siguiendo el protocolo del estándard Interfaz de Paso de Mensajes (MPI, por sus siglas en inglés).
MPI es una especificación de paso de mensajes aceptada como estándar
por todos los fabricantes de computadores.
El objetivo principal de MPI es proporcionar un estándar para escribir
programas (lenguajes \texttt{C}, \texttt{C++}, \texttt{Fortran}) con paso de mensajes. De esta forma, se
pretende mejorar la portabilidad, el rendimiento, la funcionalidad y
la disponibilidad de las aplicaciones.
Se utilizaron dos implementaciones alternativas.
En la primera, cada código \textit{esclavo}, así como el código \textit{maestro}, es ejecutado de manera independiente,
y luego se establecen los comunicadores de acuerdo a las rutinas establecidas en el estándard MPI 2.0.
En estas rutinas el código \textit{maestro} publica una serie de puertos a los cuales cada código \textit{esclavo} debe conectarse.
%\footnote{Para que los programas puedan encontrar los puertos publicados, es necesario que todos ellos pertenezcan a un mismo servicio de comunicación generado por el \textit{ompi-server}.}
Una vez aceptadas las conexiones, los programas pueden intercambiar mensajes con él siguiendo una lógica preestablecida.
Cuando ya no es necesario que los programas continúen comunicándose, se cierran las conexiones.
En la otra implementación, todos los programas son ejecutados en simultáneo.
En este tipo de ejecuciones todos los procesos cuentan con un único comunicador original, \textit{MPI\_COMM\_WORLD}, y por ello es necesario crear nuevos grupos de procesos y de comunicadores.
%En este caso los comunicadores se establecen con rutinas establecidas en el estándard MPI 1.0.
Una vez establecidos los comunicadores, los programas ya pueden intercambiar mensajes con el código \textit{maestro} siguiendo la misma lógica que se establecerá para ambos modelos.
Si bien esta implementación no es posible para nuevas conexiones una vez que los programas han sido ejecutados,
son mucho más seguras, ya que no dependen del éxito de encontrar los puertos requeridos para las conexiones.
\subsection*{Lectura y escritura de archivos de entrada y salida}
\label{2:io}
La comunicación mediante lectura y escritura de archivos se implementó para demostrar la capacidad de acoplar códigos cuyos códigos fuente no son capaces de ser modificados.
La idea principal es ejecutar corridas simples del código \textit{esclavo} cada vez que se requierea su evaluación.
En problemas de evolución, estas corridas simples solo viven para realizar un cálculo entre dos pasos de evolución acoplados.
Las corridas son administradas por el código \textit{maestro}.
El código \textit{maestro} escribe en el archivo de entrada del programa \textit{esclavo} todos los parámetros necesarios para la ejecución del cálculo,
ordena su ejecución, espera a que este termine y luego realiza una búsqueda de los valores de las variables de interés en archivos de salida.
%~ La ejecución de programas \textit{esclavos} se implementó de dos formas alternativas.
%~ La primera forma es mediante el uso de la función \textit{system} de la librería de \textit{c}.
%~ Esta función deja al proceso que la ejecuta en pausa hasta que el programa \textit{esclavo} finaliza, cuando ella retorna algún mensaje de error o de éxito.
%~ La ventaja de esto es que no debe implementarse alguna función extra para conocer cuándo leer los archivos de salida del programa \textit{esclavo}.
%~ Sin embargo, no es posible disparar múltiples procesos de un programa mediante la función \textit{system} desde un proceso que actualemente utiliza \textit{MPI}.
%~ Ésta prohibición es necesaria para controlar el disparo de procesos.
%~ Para este tipo de ejecuciones, existe la función \textit{MPI\_Comm\_Spawn} de \textit{MPI}, que se implementó como forma alternativa de ejecución de programas \textit{esclavos}.
%~ Esta función permite especificar la cantidad de procesos de ejecución del programa a disparar.
%~ El problema es que la función devuelve el control al programa \textit{maestro} de forma instantánea, sin esperar a que el código disparado finalice,
%~ por lo que, en principio, debe implementarse alguna función extra para saber cuándo es posible leer el archivo de salida.
Es necesario notar que este modelo de comunicación no es tan eficiente como el de intercambio de mensajes,
ya que la lectura y escritura de archivos consume mayores recursos de tiempo, por lo que, siempre que fuera posible, es recomendable implementar el otro modelo.
Además, requiere la programación de rutinas extra específicas dedicadas a la escritura de archivos de entrada y lectura de archivos de salida de distintos códigos \textit{esclavos}.
\subsection*{Estructura de comunicación implementada}
\label{2:estructura}
Se desarrollaron funciones híbridas con la finalidad de cubrir todas las formas de comunicación descriptas en la sección previa.
En el caso de comunicación por intercambio de mensajes,
la estrategia definida es establecer comunicación siempre entre un único proceso del código \textit{maestro}, el proceso \textit{raíz},
y un único proceso de cada código \textit{esclavo} (sus propios procesos \textit{raíces}\footnote{
Es responsabilidad del código \textit{esclavo} la comunicación de los datos recibidos por el proceso \textit{raíz} a los demás procesos.
}).
En el caso de comunicación por manejo de archivos,
la estrategia definida paraleliza las responsabilidades entre todos los procesos lanzados del código \textit{maestro}.
El esquema \ref{esquema-comunicacion} resume la estrategia de comunicación.
\begin{figure}
\centering{}
\begin{tikzpicture}[
scale=0.9, every node/.style={scale=0.9},
block1/.style={
draw,
fill=white,
rectangle,
minimum width={width("Proceso $N_m$")+2pt},
minimum height={15pt},
font=\small},
block2/.style={
draw,
fill=white,
rectangle,
fill=blue!20,
text centered,
rounded corners,
minimum width={width("Esclavo N")+2pt},
minimum height={15pt},
font=\large},
connector/.style={
-o,
line width=0.3mm
},
arrow1/.style={
-{Latex[length=3mm, width=1mm]}
},
arrow2/.style={
{Latex[length=3mm, width=1mm]}-{Latex[length=3mm, width=1mm]}
}]
% Dummy
\node at (0,10em) (dummy) {}; % blank space
% Master
\node [block2] at (0,8em) (master) {\textbf{Maestro}};
\node [block1, below=1em of master] (masterp1) {...};
\node [block1, left=1em of masterp1] (masterp0) {Proceso $0_m$};
\node [block1, right=1em of masterp1] (masterpN) {Proceso $N_m$};
% Slave 1
\node [block2] at (-6.5,0em) (slave1) {\textbf{Esclavo 1}};
\node [block1, below=1em of slave1] (s1p0) {Proceso $0_1$};
\node [block1, below=1em of s1p0] (s1p1) {...};
\node [block1, below=1em of s1p1] (s1pN) {Proceso $N_1$};
% Slave 2
\node [block2] at (-3.5,-4em) (slave2) {\textbf{Esclavo 2}};
\node [block1, below=1em of slave2] (s2p0) {Proceso $0_2$};
\node [block1, below=1em of s2p0] (s2p1) {...};
\node [block1, below=1em of s2p1] (s2pN) {Proceso $N_2$};
% Slave 3
\node [block2] at (0,0em) (slave3) {\textbf{Esclavo 3}};
\node [block1, below=1em of slave3] (s3p0) {Proceso $0_3$};
\node [block1, below=1em of s3p0] (s3p1) {...};
\node [block1, below=1em of s3p1] (s3pN) {Proceso $N_3$};
% Slave 4
\node [block2] at (3.5,-4em) (slave4) {\textbf{Esclavo $i$}};
\node [block1, below=1em of slave4] (s4p0) {Proceso $0_i$};
\node [block1, below=1em of s4p0] (s4p1) {...};
\node [block1, below=1em of s4p1] (s4pN) {Proceso $N_i$};
% Slave N
\node [block2] at (6.5,0em) (slave5) {\textbf{Esclavo N}};
\node [block1, below=1em of slave5] (s5p0) {Proceso $0_N$};
% Processes
\draw[connector] (master.south) -- ($(masterp0.north)+(0,0.2em)$);
\draw[connector] (master.south) -- ($(masterp1.north)+(0,0em)$);
\draw[connector] (master.south) -- ($(masterpN.north)+(0,0.2em)$);
\draw[connector] (slave1.west) -- ++(-1em,0) |- (s1p0.west);
\draw[connector] (slave1.west) -- ++(-1em,0) |- (s1p1.west);
\draw[connector] (slave1.west) -- ++(-1em,0) |- (s1pN.west);
\draw[connector] (slave2.west) -- ++(-1em,0) |- (s2p0.west);
\draw[connector] (slave2.west) -- ++(-1em,0) |- (s2p1.west);
\draw[connector] (slave2.west) -- ++(-1em,0) |- (s2pN.west);
\draw[connector] (slave3.west) -- ++(-1em,0) |- (s3p0.west);
\draw[connector] (slave3.west) -- ++(-1em,0) |- (s3p1.west);
\draw[connector] (slave3.west) -- ++(-1em,0) |- (s3pN.west);
\draw[connector] (slave4.west) -- ++(-1em,0) |- (s4p0.west);
\draw[connector] (slave4.west) -- ++(-1em,0) |- (s4p1.west);
\draw[connector] (slave4.west) -- ++(-1em,0) |- (s4pN.west);
\draw[connector] (slave5.west) -- ++(-1em,0) |- (s5p0.west);
% Comunicators
\draw[arrow2] ($(masterp0.south)-(1em,0)$) to[out=-90, in=15, distance=2em] node [midway, sloped, anchor=center, above]{\textit{MPI}} (s1p0.east);
\draw[arrow2] ($(masterp0.south)+(0em,0)$) to[out=-90, in=35, distance=3em] node [midway, rotate=90, anchor=center, above]{\textit{MPI}} (s2p0.east);
\draw[arrow1] ($(masterp0.south)+(1em,0)$) to[out=-90, in=105, distance=2em] node [midway, sloped, anchor=center, above]{\textit{I/O}} (slave3.north);
\draw[arrow1] (masterp1.south) to[out=-90, in=105, distance=2em] node [midway, sloped, anchor=center, above]{\textit{I/O}} (slave4.north);
\draw[arrow1] (masterpN.south) to[out=-90, in=105, distance=2em] node [midway, sloped, anchor=center, above]{\textit{I/O}} (slave5.north);
\end{tikzpicture}
\caption[Esquema de comunicación entre programas implementado]{Esquema de comunicación entre los programas \textit{esclavos} y el programa \textit{maestro} implementado en el acoplamiento de códigos.
Los \textit{esclavos} se comunican solo con el \textit{maestro} y no intercambian datos entre sí.
Se utilizan dos modelos de comunicación diferentes.
En el primero, cada código \textit{esclavo} es comunicado con el código \textit{maestro} a por paso de mensajes (indicado como \textit{MPI}).
Como podrían correr en modo serial o paralelo, solo sus procesos \textit{raíces} establecen la comunicación con el proceso \textit{raíz} del programa \textit{maestro}.
En el segundo modelo de comunicación, los códigos \textit{esclavos} son directamente \textit{ejecutados} por el programa \textit{maestro} en uno o varios procesos.
En este modelo, la comunicación se establece solo mediante lectura y escritura de archivos (indicado como \textit{I/O}).}
\label{esquema-comunicacion}
\end{figure}
\section{Códigos maestros utilizados}
\label{2:maestros}
En este trabajo se utilizaron dos códigos \textit{maestros} que cumplen con la estrategia de acoplamiento descripta previamente.
El primer código \textit{maestro} utilizado es el código \textbf{Coupling} \cite{coup-0d3d} \cite{coup-black} \cite{coup-hyd}.
\textbf{Coupling} utiliza los modelos de comunicación por paso de mensaje entre programas ejecutados de manera independiente.
El código está diseñado de tal forma que los códigos acoplados resuelvan las ecuaciones \ref{ecuaciones-modelos}
para pares de incógnitas en las intefaces.
Cada par de incógnitas debe contener una variable que corresponda a una condición de borde de tipo \textit{Dirichlet} y otra variable que corresponda a una condición de borde de tipo \textit{Neumann}.
Con la idea de extender estas capacidades al acoplamiento de programas que pudieran ser ejecutados en simultáneo,
al acoplamiento de programas por lectura y escritura de archivos,
y al acoplamiento de programas cuyos cálculos no dependieran exclusivamente de condiciones de borde dinámicas,
sino también de otros parámetros del sistema que estuvieran acoplados\footnote{
Estas capacidades se extendieron con el objetivo de resolver problemas que involucraran distintos modelos físicos dentro del mismo dominio de cálculo (ver \ref{3:strategy-extended}).
},
se desarrolló un código más genérico de acoplamiento, el código \textbf{Newton} \cite{newton}.
En los Apéndices \ref{C:coupling} y \ref{C:newton} se describen las principales características de ambos códigos.
\section{Arquitectura de acoplamiento montada en código \textit{esclavo} comunicado por paso de mensajes}
\label{2:arquitectura-mpi}
En general, el código \textit{esclavo} es un programa de cálculo particular que no ha sido diseñado para mantenerse acoplado a otro código.
En esta sección se demuestra cómo mediante unas mínimas modificaciones en sus rutinas es posible implementar un acoplamiento eficiente por paso de mensajes.
Las acciones de acoplamiento se reúnen en cuatro instancias diferentes:
\begin{enumerate}
\item al principio del programa;
\item al principio de cada paso de evolución acoplado;
\item al finalizar cada paso de evolución acoplado;
\item al finalizar el programa.
\end{enumerate}
En cada una de estas instancias el programa \textit{esclavo} debe llamar a una función específica de acoplamiento.
El programador podría definir una nueva variable \textit{booleana} que a modo de bandera indique cuándo se está realizando un cálculo acoplado para realizar los llamados o evadirlos.
Los problemas que no involucran evolución de variables pueden ser tratados como problemas con un solo paso de evolución.
A continuación se describen las instancias de acoplamiento.
\subsection*{Acoplamiento en instancia 1: al principio del programa}
En esta instancia es necesario establecer la comunicación \textit{MPI} entre el proceso \textit{raíz} del código \textit{esclavo} y el código maestro.
Si ambos programas han sido ejecutados en forma independiente los pasos a realizar son los siguientes:
\begin{itemize}
\item búsqueda del puerto publicado por el el proceso \textit{raíz} del programa \textit{maestro};
\item conexión del proceso \textit{raíz} del programa esclavo a este puerto y creación del comunicador.
\end{itemize}
Si, en cambio, los programas han sido ejecutados en simultáneo, los pasos a realizar son los siguientes:
\begin{itemize}
\item creación de grupo global de procesos;
\item creación de subgrupo local de procesos;
\item creación de un comunicador dentro del subgrupo previo, necesario para el paso de mensajes dentro del programa;
\item creación de un grupo entre el proceso \textit{raíz} del programa esclavo y el proceso \textit{raíz} del programa \textit{maestro};
\item creación de un comunicador en el grupo previo, necesario para el paso de mensajes de acople.
\end{itemize}
Una vez implementada la comunicación, el código \textit{esclavo} puede recibir datos generales
(como parámetros de evolución iniciales, cantidad de pasos de evolución, cantidad de incógnitas en interfaces de acople, cantidad de interfaces de acople, etc.),
y chequear la consistencia con los datos propios del programa.
Si es necesario, los datos locales pueden ser cambiados notificando al usuario.
\subsection*{Acoplamiento en instancia 2: al principio de cada paso de evolución acoplado}
La estrategia de acoplamiento se define entre el parámetro de evolución inicial $t_{coup,0}$ y el parámetro final $t_{coup,N}$,
con $N+1$ pasos de acoplamiento cada $\Delta t_{coup}=\frac{t_{coup,N} - t_{coup,0}}{N}$.
La solución para $t_{coup,0}$ es la condición inicial del problema, y es dato para todos los programas.
Antes de resolver la solución para $t_{coup,1}$, se llama a las funciones de acoplamiento correspondientes a la instancia 2,
en las cuales se reciben los valores de condiciones de borde necesarios.
En base a estos valores, el programa \textit{esclavo} resuelve las ecuaciones implicadas en el subdominio correspondiente.
Este proceso debe repetirse previo al cálculo de cada instancia $i$ acoplada en $t_{coup,i}$.
Es posible que cada código \textit{esclavo} utilice subpasos de evolución $\Delta t_{local}$ locales
(como se explicó en el apartado \ref{1:evolucion}),
por lo cual requerirá valores extras para las condiciones de borde en estos pasos.
En estos casos, se implementa una estrategia de interpolación de valores entre la solución acoplada previa, y los valores recibidos para el siguiente paso acoplado.
\subsection*{Acoplamiento en instancia 3: al finalizar cada paso de evolución acoplado}
Una vez resuelto cada $\Delta t_{coup}$, el programa \textit{esclavo} envía al programa \textit{maestro} los valores de las incógnitas calculadas en las interfaces de acoplamiento.
Tras este envío, el programa \textit{esclavo} queda en espera de la órden para continuar.
Si el programa \textit{maestro} estuviera conectado a otros códigos \textit{esclavos},
en esta instancia también debe recepcionar los valores de las incógnitas calculados ellos.
Una vez que recibió todos los valores calculados para las incógnitas de acoplamiento,
resuelve las ecuaciones de residuos \ref{sistema-simple}.
Si el módulo del residuo cae por debajo de cierta tolerancia prefijada el código \textit{maestro} acepta los resultados y envía a sus \textit{esclavos} la órden de continuar con el cálculo.
En caso contrario, puede enviarles la órden de volver a calcular el mismo paso de acoplamiento, o incluso de abortar.
\pgfdeclarelayer{bg} % declare background layer
\pgfsetlayers{bg,main} % set the order of the layers (main is the standard laye
\begin{figure}
\centering{}
\begin{tikzpicture}[
block1/.style={
draw,
fill=white,
text centered,
rectangle,
minimum width={width("Proceso $N_m$")+2pt},
minimum height={17pt},
font=\small},
block2/.style={
draw,
rectangle,
fill=blue!20,
text centered,
rounded corners,
minimum width={width("Esclavo N")+2pt},
minimum height={15pt},
font=\large},
connector/.style={
-o,
line width=0.3mm
},
arrow1/.style={
-{Latex[length=3mm, width=1mm]}
},
arrow2/.style={
{Latex[length=3mm, width=1mm]}-{Latex[length=3mm, width=1mm]}
}
]
% Dummy
%\node at (0,10em) (dummy) {}; % blank space
% Códigos
\node [block2] at (0,25em) (maestro) {\textbf{Maestro}};
\node [block2, left=4.8em of maestro] (esclavo1) {\textbf{Esclavo $1$}};
\node [block2, right=4.8em of maestro] (esclavo2) {\textbf{Esclavo $2$}};
% Arranque Comunicadores
\node [block1, text width=2.5cm] at (0,22em) (comm0) {Creación de comunicadores};
\node [block1, text width=2.7cm, text centered, left=4.1em of comm0] (comm1) {Creación de comunicadores};
\node [block1, text width=2.7cm, text centered, right=4.1em of comm0] (comm2) {Creación de comunicadores};
\draw [arrow2] (comm0.west) -- (comm1.east);
\draw [arrow2] (comm0.east) -- (comm2.west);
% guess0
\node [block1, text width=2.5cm] at (0,16em) (guess0) {Cálculo de \scriptsize \{$x^{guess}(t_{coup,j+1})$, $y^{guess}(t_{coup,j+1})$\}};
% comm0 -- guess0
\draw [arrow1, dashed] (comm0) -- (guess0);
% ci
\node [anchor=east, text width=2cm, text centered] at (-7 em,18em) (ci1) {\scriptsize (condiciones iniciales)};
\node [anchor=west, text width=2cm, text centered] at ( 7 em,18em) (ci2) {\scriptsize (condiciones iniciales)};
% s1
\node [anchor=east] at (-11.5 em,17em) (tl10) {\footnotesize $t_{e_1,0}$};
\node at (-7.3em,17em) (dummy11) {};
\draw [dashed] ($(tl10)+(1em,-0.3em)$) -- ($(dummy11)-(0,0.3em)$);
\node [anchor=east] at (-11.5 em,15em) (tl11) {\footnotesize $t_{e_1,1}$};
\node at (-7.3em,15em) (dummy21) {};
\draw [dashed] ($(tl11)+(1em,-0.3em)$) -- ($(dummy21)-(0,0.3em)$);
\node [anchor=east] at (-11.5 em,13em) (tl12) {\footnotesize $t_{e_1,2}$};
\node at (-7.3em,13em) (dummy31) {};
\draw [dashed] ($(tl12)+(1em,-0.3em)$) -- ($(dummy31)-(0,0.3em)$);
\node [anchor=east] at (-11.5 em,11em) (tl1i) {\footnotesize $t_{e_1,i}$};
\node at (-7.3em,11em) (dummy41) {};
\draw [dashed] ($(tl1i)+(1em,-0.3em)$) -- ($(dummy41)-(0,0.3em)$);
\node [anchor=east] at (-11.5 em,9em) (tl1n) {\footnotesize $t_{e_1,N_1}$};
\node at (-7.3em,9em) (dummy51) {};
\draw [dashed] ($(tl1n)+(1.2em,-0.3em)$) -- ($(dummy51)+(0.2em,-0.3em)$);
% Delta t coup j
\node [anchor=east, text width=1cm, align=right] at (-15.75em,17em) (tc0) {\footnotesize $t_{coup,j}$};
\node [anchor=east, text width=1cm, align=right] at (-15.75em,9em) (tc1) {\footnotesize $t_{coup,j+1}$};
\node [text width=1cm, align=right] at (-18em,13em) (dtc1) {$\Delta t_{coup}$};
\draw[|-] ($(tc0.south)+(-1.75em,1em)$) to[out=-90, in=90] ($(dtc1.north)-(1.05em,0)$);
\draw[-|] ($(dtc1.south)-(1.05em,0)$) to[out=-90, in=90] ($(tc1.north)+(-1.75em,-1em)$);
% flechas s1
\draw[->] ($(tl10.east)+(1.8em,-0.7em)$) -- ($(tl10.east)+(1.8em,-2em)$);
\draw[->] ($(tl11.east)+(1.8em,-0.7em)$) -- ($(tl11.east)+(1.8em,-2em)$);
\draw[->] ($(tl12.east)+(1.8em,-0.7em)$) -- ($(tl12.east)+(1.8em,-2em)$);
\draw[->] ($(tl1i.east)+(1.8em,-0.7em)$) -- ($(tl1i.east)+(1.8em,-2em)$);
% s2
\node [anchor=west] at (11.5 em,17em) (tl20) {\footnotesize $t_{e_2,0}$};
\node at (7.3em,17em) (dummy12) {};
\draw [dashed] ($(dummy12)-(0,0.3em)$) -- ($(tl20.west)-(0em,0.3em)$);
\node [anchor=west] at (11.5 em,13em) (tl2i) {\footnotesize $t_{e_2,i}$};
\node at (7.3em,13em) (dummy22) {};
\draw [dashed] ($(dummy22)-(0,0.3em)$) -- ($(tl2i.west)-(0em,0.3em)$);
\node [anchor=west] at (11.5 em,9em) (tl2n) {\footnotesize $t_{e_2,N_2}$};
\node at (7.3em,9em) (dummy32) {};
\draw [dashed] ($(dummy32)-(0,0.3em)$) -- ($(tl2n.west)-(0em,0.3em)$);
% flechas s2
\draw[->] ($(dummy12.east)+(2em,-1em)$) -- ($(dummy12.east)+(2em,-3.5em)$);
\draw[->] ($(dummy22.east)+(2em,-1em)$) -- ($(dummy22.east)+(2em,-3.5em)$);
% send 1
\draw [arrow1] (guess0) to node [midway, above]{\footnotesize $x^{guess}$} ($(dummy11)-(0,1em)$);
\draw [arrow1] (guess0) to node [midway, above]{\footnotesize $y^{guess}$} ($(dummy12)-(0,1em)$);
% calc0
\node [block1, text width=2cm] at (0,8em) (calc0) {Cálculo de residuos};
% recv 1
\draw [arrow1] ($(dummy51)-(0,1em)$) to node [midway, above]{\footnotesize $y(x^{guess})$} (calc0);
\draw [arrow1] ($(dummy32)-(0,1em)$) to node [midway, above]{\footnotesize $x(y^{guess})$} (calc0);
% convergencia
\node [draw, ellipse, fill=white] at (0,4em) (convergencia) {\footnotesize Convergencia?};
\draw[arrow1] (calc0) -- (convergencia);
% si-no
\node [draw, diamond, fill=white] at(-2em, 1em) (si) {\footnotesize Sí};
\draw[arrow1] (convergencia) -- (si);
\node [draw, diamond, fill=white] at( 2em, 1em) (no) {\footnotesize No};
\draw[arrow1] (convergencia) -- (no);
% reiniciar
\node [draw,rectangle, fill=white] at( 2em, -2em) (reinicio) {\scriptsize Reiniciar};
\draw[arrow1] (no) -- (reinicio);
% continuar
\node [draw,rectangle, fill=white] at(-2em, -2em) (continuar) {\scriptsize Continuar};
\draw[arrow1] (si) -- (continuar);
% t->t+dt
\node [draw,rectangle, fill=white, text width=3cm, align=center] at (0,-4.5em) (actualizar) {\scriptsize $t_{coup,j} = t_{coup,j} + \Delta t$};
% t < tf
\node [draw,rectangle, fill=white, text width=2.5cm, align=center] at ($(actualizar.south)+(-3.45em,-1.5em)$) (minor1) {\scriptsize $t_{coup} < t_{coup,final}$};
% t > tf
\node [draw,rectangle, fill=white, text width=2.5cm, align=center] at ($(actualizar.south)+(+3.45em,-1.5em)$) (minor2) {\scriptsize $t_{coup} \geqslant t_{coup,final}$};
% continuar->guess
\draw[arrow1, dashed] (continuar) -- (actualizar);
\draw[arrow1, dashed] (actualizar) -- (minor1);
\draw[arrow1, dashed] (actualizar) -- (minor2);
%~ % s1
%~ \node [anchor=east] at (-11.5 em,-5em) (tl10) {\footnotesize $t_{e1,N}$};
%~ \node at (-7.3em,-5em) (dummy11) {};
%~ \draw [dashed] ($(tl10)+(1em,-0.3em)$) -- ($(dummy11)-(0,0.3em)$);
%~
%~ \node [anchor=east] at (-11.5 em,-7em) (tl11) {\footnotesize $t_{e1,N+1}$};
%~ \node at (-7.3em,-7em) (dummy21) {};
%~ \draw [dashed] ($(tl11)+(1.4em,-0.3em)$) -- ($(dummy21)-(0,0.3em)$);
%~
%~ \node [anchor=east] at (-11.5 em,-9em) (tl12) {\footnotesize $t_{e1,N+2}$};
%~ \node at (-7.3em,-9em) (dummy31) {};
%~ \draw [dashed] ($(tl12)+(1.4em,-0.3em)$) -- ($(dummy31)-(0,0.3em)$);
%~
%~ \node [anchor=east] at (-11.5 em,-11em) (tl1i) {\footnotesize $t_{e1,N+i}$};
%~ \node at (-7.3em,-11em) (dummy41) {};
%~ \draw [dashed] ($(tl1i)+(1.4em,-0.3em)$) -- ($(dummy41)-(0,0.3em)$);
%~
%~ \node [anchor=east] at (-11.5 em,-13em) (tl1n) {\footnotesize $t_{e1,N+N}$};
%~ \node at (-7.3em,-13em) (dummy51) {};
%~ \draw [dashed] ($(tl1n)+(1.6em,-0.3em)$) -- ($(dummy51)+(0.2em,-0.3em)$);
%~
%~ % Delta t coup 2
%~ \node [anchor=east] at (-15.25em,-5em) (tc0) {\footnotesize $t_{coup,1}$};
%~ \node [anchor=east] at (-15.25em,-13em) (tc1) {\footnotesize $t_{coup,2}$};
%~ \node at(-17.5em,-9em) (dtc1) {$\Delta t_{coup}$};
%~ \draw[|-] ($(tc0.south)+(-1.75em,1em)$) to[out=-90, in=90] ($(dtc1.north)-(1em,0)$);
%~ \draw[-|] ($(dtc1.south)-(1em,0)$) to[out=-90, in=90] ($(tc1.north)+(-1.75em,-1em)$);
%~
%~ % flechas s1
%~ \draw[->] ($(tl10.east)+(1.8em,-0.7em)$) -- ($(tl10.east)+(1.8em,-2em)$);
%~ \draw[->] ($(tl11.east)+(1.8em,-0.7em)$) -- ($(tl11.east)+(1.8em,-2em)$);
%~ \draw[->] ($(tl12.east)+(1.8em,-0.7em)$) -- ($(tl12.east)+(1.8em,-2em)$);
%~ \draw[->] ($(tl1i.east)+(1.8em,-0.7em)$) -- ($(tl1i.east)+(1.8em,-2em)$);
%~
%~ % s2
%~ \node [anchor=west] at (11.5 em,-5em) (tl20) {\footnotesize $t_{e2,N}$};
%~ \node at (7.3em,-5em) (dummy12) {};
%~ \draw [dashed] ($(dummy12)-(0,0.3em)$) -- ($(tl20.west)-(0em,0.3em)$);
%~
%~ \node [anchor=west] at (11.5 em,-9em) (tl2i) {\footnotesize $t_{e2,N+i}$};
%~ \node at (7.3em,-9em) (dummy22) {};
%~ \draw [dashed] ($(dummy22)-(0,0.3em)$) -- ($(tl2i.west)-(0em,0.3em)$);
%~
%~ \node [anchor=west] at (11.5 em,-13em) (tl2n) {\footnotesize $t_{e2,N+N}$};
%~ \node at (7.3em,-13em) (dummy32) {};
%~ \draw [dashed] ($(dummy32)-(0,0.3em)$) -- ($(tl2n.west)-(0em,0.3em)$);
%~
%~ % flechas s2
%~ \draw[->] ($(dummy12.east)+(2em,-1em)$) -- ($(dummy12.east)+(2em,-3.5em)$);
%~ \draw[->] ($(dummy22.east)+(2em,-1em)$) -- ($(dummy22.east)+(2em,-3.5em)$);
%~
%~ % send 1
%~ \draw [arrow1] (guess1) to node [midway, above]{\footnotesize $x^{guess}$} ($(dummy11)-(0,1em)$);
%~ \draw [arrow1] (guess1) to node [midway, above]{\footnotesize $y^{guess}$} ($(dummy12)-(0,1em)$);
%~
%~ % calc1
%~ \node [block1, text width=2cm] at (0,-14em) (calc1) {cálculo de residuos};
%~
%~ % recv 1
%~ \draw [arrow1] ($(dummy51)-(0,1em)$) to node [midway, above]{\footnotesize $y(x^{guess})$} (calc1);
%~ \draw [arrow1] ($(dummy32)-(0,1em)$) to node [midway, above]{\footnotesize $x(y^{guess})$} (calc1);
%~
%~ % Final node
%~ \node at (0,-17em) (final) {...};
%~ \draw [arrow1] (calc1) -- (final);
% Continue order 1
%~ \node at($(continuar.south) - (0.7em,0)$) (c11) {};
%~ \node at($(c11) - (0em,0.3em)$) (c12) {};
%~ \node at($(c12) - (7em,0em)$) (c13) {};
%~ \node at($(c13) - (0em,2em)$) (c14) {};
%~ \draw [dashed, arrow1] (c11.center) -- (c12.center) -- (c13.center) -- (c14.center);
%~ % Continue order 2
%~ \node at($(continuar.south) + (0.7em,0)$) (c21) {};
%~ \node at($(c21) - (0em,0.3em)$) (c22) {};
%~ \node at($(c22) + (11em,0em)$) (c23) {};
%~ \node at($(c23) - (0em,2em)$) (c24) {};
%~ \draw [arrow1,dashed] (c21.center) -- (c22.center) -- (c23.center) -- (c24.center);
% Reinitial connection
%\draw[arrow1, dashed] (reinicio.east) to[out=0, in=-10, distance=4cm] (guess0.south);
%~ \node at($(reinicio.north) + (0.7em,0)$) (r1) {};
%~ \node at($(r1) + (0em,0.6em)$) (r2) {};
%~ \node at($(r2) + (4em,0em)$) (r3) {};
%~ \node at($(r3) + (0em,12em)$) (r4) {};
%~ \node at($(r4) - (6.5em,0em)$) (r5) {};
%~ \node at($(r5) + (0em,3em)$) (r6) {};
%~ \draw[arrow1,dashed] (r1.center) -- (r2.center) -- (r3.center) -- (r4.center) -- (r5.center) -- (r6.center);
%~ % Reinit order 1
%~ \node at($(reinicio.north) - (0.7em,0)$) (r11) {};
%~ \node at($(r11) + (0em,0.3em)$) (r12) {};
%~ \node at($(r12) - (16em,0em)$) (r13) {};
%~ \node at($(r13) + (0em,19.5em)$) (r14) {};
%~ \node at($(r14) + (2.5em,0em)$) (r15) {};
%~ \draw[arrow1,dashed] (r11.center) -- (r12.center) -- (r13.center) -- (r14.center) -- (r15.center);
% Reinit order
\node at($(reinicio.east) + (11em,0em)$) (r23) {};
\node at($(r23) + (0em,22.0em)$) (r24) {};
\node at($(r24) - (13.5em,0em)$) (r25) {};
\node at($(r25) - (0em,2.em)$) (r26) {};
\draw[arrow1,dashed] (reinicio.east) -- (r23.center) -- (r24.center) -- (r25.center) -- (r26.center);
% Continue order
\node at($(minor1.west) - (8em,0em)$) (c23) {};
\node at($(c23) + (0em,26.7em)$) (c24) {};
\node at($(c24) + (13.5em,0em)$) (c25) {};
\node at($(c25) - (0em,2.em)$) (c26) {};
\draw[arrow1,dashed] (minor1.west) -- (c23.center) -- (c24.center) -- (c25.center) -- (c26.center);
% Liberacion Comunicadores
\node [block1, text width=2.5cm] at ($(actualizar.south) - (0em,5em)$) (comm02) {Liberación de comunicadores};
\node [block1, text width=2.7cm, text centered, left=4.1em of comm02] (comm12) {Liberación de comunicadores};
\node [block1, text width=2.7cm, text centered, right=4.1em of comm02] (comm22) {Liberación de comunicadores};
\draw [arrow2] (comm02.west) -- (comm12.east);
\draw [arrow2] (comm02.east) -- (comm22.west);
% Minor2 -- liberacion
\draw[arrow1,dashed] (minor2) -- (comm02);
% Cuadros
\begin{pgfonlayer}{bg} % select the background layer
% Esclavo 1
\draw [red, rounded corners, thick, opacity=0.5, fill=red!10]
(esclavo1.west)
-- ($(esclavo1.west) - (1.5em,0)$) -- ($(esclavo1.west) - (1.5em,38em)$)
-- ($(esclavo1.east) + (0.5em,-38em)$) -- ($(esclavo1.east) + (0.5em,0em)$)
-- (esclavo1.east);
% Esclavo 2
\draw [red, rounded corners, thick, opacity=0.5, fill=red!10]
(esclavo2.west)
-- ($(esclavo2.west) - (0.5em,0)$) -- ($(esclavo2.west) - (0.5em,38em)$)
-- ($(esclavo2.east) + (1.5em,-38em)$) -- ($(esclavo2.east) + (1.5em,0em)$)
-- (esclavo2.east);
% Maestro
\draw [black!60!green, rounded corners, thick, opacity=0.5, fill=black!60!green!10]
(maestro.west)
-- ($(esclavo1.east) + (0.7em,0)$) -- ($(esclavo1.east) + (0.7em,-38em)$)
-- ($(esclavo2.west) + (-0.7em,-38em)$) -- ($(esclavo2.west) - (0.7em,0em)$)
-- (maestro.east);
\end{pgfonlayer}
\end{tikzpicture}
\caption[Esquema de acoplamiento entre programas implementado]{Esquema de acoplamento entre el programa \textit{maestro} y los programas \textit{esclavos}.
En el ejemplo, cada código \textit{esclavo} resuelve ecuaciones diferenciales en distintos subsitemas.
Estos subsistemas están acoplados entre sí en alguna interfaz en las que las variables \{$x,y$\} son incógnitas.
Los cálculos se acoplan cada $\Delta t_{coup}$, pero cada programa utiliza subpasos de cálculos locales.
Una vez generados los comunicadores,
el código \textbf{Esclavo 1} recibe como \textit{guess} para el tiempo $t_{coup,1}$ la variable $x^{guess}$.
El código \textbf{Esclavo 2} recibe alternativamente como \textit{guess} para el tiempo $t_{coup,1}$ la variable $y^{guess}$.
Al finalizar $\Delta t_{coup}$ ambos programas devuelven al programa \textbf{Maestro} las variables conjugadas calculadas.
\textbf{Maestro} computa los residuos entre los valores \textit{guess} previamente propuestos y los valores recibidos.
Si el residuo no supera cierta tolerancia prefijada, envía la órden de reinicio a cada programa \textit{esclavo},
para volver a calcular el mismo paso de acoplamiento, tras lo cual enviará nuevos valores \textit{guess} propuestos.
En caso contrario, cuando los resultados convergen, envía la órden de continuación, y ambos \textit{esclavos} prosiguen con el cálculo para el siguiente paso de evolución.
Al finalizar el cálculo se liberan los comunicadores.
}
\label{esquema-evolucion}
\end{figure}
\subsection*{Acoplamiento en instancia 4: al finalizar el programa}
Antes de finalizar el programa, es necesario cerrar las conexiones, liberar los grupos y los comunicadores establecidos.
El programa \textit{esclavo} debe recibir una order para ejecutar estas acciones.
%\vspace{\baselineskip}
%\vspace{1cm}
\bigskip
La Figura \ref{esquema-evolucion} esquematiza las instancias 1, 2, 3 y 4 en la resolución de un problema sencillo
en que se acoplan dos programas \textit{esclavos} al programa \textit{maestro}.
En este ejemplo, las variables $\{x,y\}$ son las incógnitas de acoplamiento.
La estrategia definida consiste en imponer al primer subdominio un valor \textit{guess} para $x$ y al segundo subdominio un valor \textit{guess} para $y$.
En primer lugar se establecen los comunicadores entre el programa \textit{maestro} y los programas \textit{esclavos}.
\textbf{Maestro} es el programa que se ocupa de proponer los valores \textit{guess} y de enviarlos a los \textit{esclavos}.
El programa \textbf{Esclavo 1} resuelve cada paso de evolución acoplado $\Delta t_{coup}$ en función del valor $x_{guess}$ recibido, y envía a \textbf{Maestro} el valor $y$ calculado a partir de él.
El programa \textbf{Esclavo 2} calcula $x$ en función de $y_{guess}$ y también envía el resultado a \textbf{Maestro}.
Notar que cada código $i$ puede utilizar una discretización de $N_i$ subpasos de evolución locales para resolver cada $\Delta t_{coup}$.
Luego de que \textbf{Maestro} recibe los valores calculados,
resuelve las ecuaciones de residuos y decide si los resultados están convergidos o si es necesario continuar con las iteraciones dentro del paso de evolución.
Una vez asegurada la convergencia, se actualiza el parámetro de evolución y se procede a resolver el paso siguiente.
Cuando el parámetro de evolución alcanza el valor final $t_f$, se termina el cálculo y se procede a la liberación de comunicadores.
En problemas que implican la resolución de casos estacionarios, es decir, sin evolución de variables, el esquema de abordaje es idéntico.
En la Figura \ref{esquema-evolucion} podría pensarse que se resuelve un único paso temporal ficticio, sin la necesidad de imponer condiciones iniciales.
Cuando la comunicación entre programas se realiza mediante manejo de archivos, se respeta la misma estructura de acoplamiento,
exceptuando las instancias relacionaadas a la creación y liberación de comunicadores, que son reemplazadas por instancias de manejo de archivos.
El código \textit{maestro} escribe los valores \textit{guess} para las variables de acoplamiento en el archivo de entrada del código \textit{esclavo}, y luego ordena su ejecución.
El código \textit{esclavo} solo vive para resolver $\Delta t_{coup}$, es decir, finaliza una vez que escribe los valores calculados en algún archivo de salida,
que luego será leído por el código \textit{maestro}.