Open maurinhocba opened 3 years ago
Profe,
Estuve trabajando un poco con este tema. Hay algunas cosas que me quedaron con dudas. El primero es el más importante, lo demás es accesorio.
Se me ocurre que el eje x tenga las etiquetas de los DOFs (sea u o q), el eje y su valor y que el label asociado a esa 'curva' sea el instante de tiempo asociado. Así se podrían plotear más de 1 por gráfica, quedarían distintas curvas para distintos tiempos. No sé el tema de las escalas, si nos quedan valores muy distintos...
axes
de matplotlib 'sueltos', sino que estas funciones van a tener que generarlos usando subplots()
o alguna simular (add_subplot
). Dejo esto porque en un momento se me ocurrió la posibilidad de trabajar con los axes
de forma independiente y después agregarlos a las Figure
, pero bueno, parece que no es así. Siempre usé el módulo con subplots()
. No debería haber problemas.[https://stackoverflow.com/questions/6309472/matplotlib-can-i-create-axessubplot-objects-then-add-them-to-a-figure-instance] [https://matplotlib.org/stable/tutorials/intermediate/artists.html] [https://stackoverflow.com/questions/34162443/why-do-many-examples-use-fig-ax-plt-subplots-in-matplotlib-pyplot-python]
inds
y asociarle a cada uno su label=Nombre
, ya quedaría dentro del ax
. Ploteando todo junto como en el ejemplo creo que no quedaría. numpy.gradient
, no necesito definir otra función.Mientras tanto, sigo. Quedaría un archivo aparte, módulo 'plotter'. En estos días ya subo una primera versión al repo.
Update 29/06
El último commit de la branch dónde trabajo tiene avances respecto a las funciones para plotear.
en principio fig_ut
está andando. Función de nivel superior, internamente usa plt_ut
. En eg.py
hay un pequeño ejemplo de lo que comento a continuación (se ve feo porque diverge al final).
Propuse algunos cambios respecto a tu planteo orginal: Estoy trabajando con dicts
, me parecen más cómodos para pasar los argumentos armando todo estilo [[200000,[1,2],...]]. Directamente, generamos uno con keys
dadas por los nodos de interés y values
dadas por una lista de dofs que queremos per nodo: desired = {'200000':[1,2,5], '2000100':[5]}
. Internamente, algunas cosas se pasan a listas, pero para usar las funciones se me hace más fácil.
Otra ventaja (aparente): Creo que no va a ser necesario definir fig_ut_col
y fig_ut_one
, dado que los casos de interés pueden cubrirse con una lista de diccionarios usando fig_ut
:
dofLIST
es una lista de n dicts
con m_i
DOFs de interés por cada uno. Devuelve una figura compuesta por n subplots. En cada uno, plotea todos los dofs (sean del mismo nodo o no) que contienen los n-dicts integrantes de la lista.fig_ut_col
se emula pasando una lista con n dicts que tienen un único nodo y un único DOF. Esto según el comentario del planteo. Devuelve 1 figura con n subplots que tienen 1 curva cada uno.fig_ut_one
, se pasa un dict
(o [dict]
, el código lo acomoda sólo) que contiene n DOFs para un único nodo. Devuelve 1 figura con n curvas en 1 sistema de ejes.¿Está bien planteado? Creo que así está en el pseudocódigo / comentarios, salvo que haya agarrado el tema muy mal. Si no estás de acuerdo con esto, vuelvo a listas puras y a la definición independiente; pero me pareció interesante y más cómodo. Además, no hay que andar con cuidado de la adaptación interna de las funciones a menor nivel, por ejemplo si hay más de un DOF, si hay varios, cuántos nodos, etc....
Respecto a lo demás:
Respecto a la parte de 'customizar' los gráficos:
kwargs
si existe y de ahí acomoda las etiquetas y demás. Es cosa de agregarle un par de cositas y definir los defaults con lo que nos interesa. el tema de los diccionarios y fig_ut me parece muy bien veremos cómo vaya quedando lo demás cuando lo tengas hecho por si hay que arreglar algo todo excelente hasta acá
Update 17 07
Importante: Tengo que revisar bien el funcionamiento de las funciones ut_vt_PP (idem para la análoga de q). Entiendo que sería una figura con 3 axes: en el primero, u(t), segundo v(t), tercero PP(u(t)). Si alimento varios DOF (o modos en el caso de q), plotearía todo junto en cada uno de los axes. ¿Es correcto?
La alternativa es generar una 'matriz' de axes que plotee en filas los 3 tipos de gráficos (u,v,PP) y en columnas los DOFs deseados, de modo de utilizar 3 axe para cada DOF de interés (una columa per cada uno)
Comentarios commit:
Implementación de funciones para trabajar con los q
Opté por mantener la estructura ya existente (así queda todo más o menos homogéneo) y utilizar dicts para pasar los modos de interés. No cambia prácticamente nada. Lo propuesto era que los argumentos que indican los modos a plotear sean:
[1,3,4] > Ploteo modo 1,3 y 4 en 1 ax [[1,3],[2]] > Ploteo modo 1,3 en el primer ax, 2 en el segundo.
Me resultó más sencillo copiar lo ya en funcionamiento para los u: {'cualquier_nombre':[1,3,4]} > Plotea los modos 1,3,4 en 1 ax (no importa la key) [{'name':[1,3],{'otro_name':[2,3]}] > Plotea los modos 1,3 en el primer ax, modos 2,3 en el segundo
Es más o menos lo mismo... Pero internamente rescata prácticamente todas las estructuras.
IN PROGRESS: Acomodar un poco la documentación, el tema de la customización de labels, ejes y demás. Generar las funciones de índices de la reunión. Espero llegar a hacerlas mañana / el lunes antes de la reunión (si la hay)
23/07/21
Corrección de función para búsqueda de índices del vector tiempo, incorporación de atributos en objs stru
plot_timeVals
y plot_timeInds
para guardar los valores (más cercanos) e índices según el usuario indique. Por default, los ínidices son 0 y -2 (para plotear hasta el último de manera sencilla, se lleva a -1 internamente) y np.inf para los valores.
Las funciones a nivel fig
llaman la función sfti_time
(search for time indexes) en sim_db
y actualizan esto (ínidices y valores, así el usuario puede ver si 'cae' cerca)
23/07/21 Ahora el ploteo de la parte modal se hace con índices 'naturales'. Internamente se lleva a los de python (-1)
Incorporación de funciones de ploteo para un instante único de tiempo.
fig_uxuy
> plotea un DOF vs otro para todos los nodos disponibles en el struCase
, para un tiempo (o varios, uno per curva) especificado. Manteniendo la forma usual, se utiliza un dict
que contiene {'DOFs':[dof1,dof2],'t_vals':[t1,t2...]}
. Se tendrá una curva por cada ti
. El tiempo de ploteo se hace de forma aproximada con search_time
(y se indican ambos), por si no existe exactamente el deseado por el usuario. Si la función recibe una lista de dicts, genera un ax
por cada uno (forma usual)fig_us
> plotea un DOF para todos los nodos disponibles en el struCase
, para un tiempo especificado (o varios, idem anterior). Se utiliza un dict
que contiene comoKEY
el grado de libertad deseado (un número, relativo a cada nodo, internamente se busca la posición en la matriz general) y como values
una lista de tiempos [ti]
(uno per curva). En el eje x
se plotean las labels
de los nodos. Mismos comentarios que anterior para generar varios axes.fig_qs
> Idem anterior pero para todos los modos. Funciona de manera similar. Igualmente que lo usual para la parte modal, no se usan dicts sino una lista de tiempos de interés (una curva per tiempo) o una lista de listas si quiero varios axes.Comentario: ¿Necesitamos la análoga fig_qxqy
para comparar dos modos?
Nueva función para guardar plots: save_figure
Se agregó una función en plotter.py
que permite guardar las figuras en formato deseado (default png
, pero funciona con svg
también). La llamada puede hacerse manualmente o mediante kwargs
de las funciones fig
de alto nivel. Se puede customizar la carpeta (si no existe se crea automáticamente, útil para automatizar luego o separar por corridas), el código de archivo (un prefijo), el nombre de archivo, dpi y layout.
Las gráficas prueba quedan guardadas en el drive.
Para ir entrando en tema, quiero que usemos el enfoque que aparece resaltado en esta figura, copiada de esta parte de la página oficial de Matplolib.![image](https://user-images.githubusercontent.com/54688395/122068688-fe643500-cdca-11eb-8e8a-4c342cb0d401.png)
Me imagino un conjunto de funciones trabajando juntas.
Algunas más básicas como (seguro lo que sigue tiene un par de errores de sintaxis para corregir):
para graficar u(t) en un único par de ejes, y otra análoga, plt_qt, para graficar los q(t) en otro par de ejes (lo único que cambiaría sería que lee de struCase.q y no hace falta convertir índices, porque se entra con el número de modos a plotear y listo).
En un nivel superior, necesitamos funciones que trabajen sobre figuras, ordenando en ellas los ploteos que salen de las funiones anteriores:
fig_ut(struCase, dofLIST, **kwargs), esta pone en una misma figura varios axes como los que genera plt_ut Fijate que escribí dofLIST con más mayúsculas para distinguirlo del otro, porque este dofLIST es una lista con varias dofList, de manera que lo único que hace es contar los elementos de dofLIST, digamos n, generar una figura con n subplots apilados y llamar n veces a plt_ut para llenar ahí con datos; al así:
def fig_ut(struCase, dofLIST, **kwargs): ''' Arranges plots of u(t)
Hace falta lo mismo para los q. Estoy viendo que esto es un poquito enquilombado para hacer llamadas simples, en las que hay una sola dofList, o en las que hay varias dofList con un solo par (nodo,GL) adentro. Esta es bastante general.
fig_ut_one(struCase, dofList, **kwargs) sería otra versión simplificada, que pone un solo axes y todos los plots ahí dentro Estas últimas dos versiones deberían ser simplemente wrappers de fig_ut; algo así:
def fig_ut_col(struCase, dofList, **kwargs): ''' Wrapper for fig_ut
struCase: stru class object dofList: list (level 2) of lists (level 1 - pairs [node,DoF]) e.g.: dofList = [ [node1,3], [node1,1], [node3,1] ] node1 and node3 being integers (external labels of nodes included in struCase.nodes) the figure will have 3 axes arranged on a column top axes will have one curve (time evolution of the 3rd DoF of node node1) mid axes will have one curve (time evolution of the 1st DoF of node node1) bot axes will have one curve (time evolution of the 1st DoF of node node3) '''
for lst in dofList: lst = [ lst ]
fig = fig_ut(struCase, [dofList], **kwargs)
return(fig)
def fig_ut_one(struCase, dofList, **kwargs): ''' Wrapper for fig_ut
struCase: stru class object dofList: list (level 2) of lists (level 1 - pairs [node,DoF]) e.g.: dofList = [ [node1,3], [node1,1], [node3,1] ] node1 and node3 being integers (external labels of nodes included in struCase.nodes) the figure will have 1 axes having 3 curves, time evolutions of the 3rd DoF of node node1 the 1st DoF of node node1 the 1st DoF of node node3 '''
fig = fig_ut(struCase, [dofList], **kwargs)
return(fig)
Con eso es más que suficiente. Hay que tenerlo listo pronto.
Obviamente, si ves que conviene agregar otros datos de entrada (que pueden ir en kwargs) para poner títulos a gráficos y ejes, códigos de estilos de líneas o lo que sea, es bienvenido, siempre y cuando sea breve, porque necesitamos ponernos a analizar señales con urgencia.
Me preocupa que como está hecho todo, no nos va a servir de mucho para comparar resultados de distintas simulaciones, pero haremos unos wrappers luego, con otra clase más. Más adelante.