Añadir frameworks necesarios para programar OpenGL en XCode 4.x.

XCode es uno de los mejores IDE’s que he probado, no está a la altura de Eclipse en cuando a plugins y esas cosas porque ya sabemos como es la gente de la manzana con sus aplicaciones en lo referente a terceros pero… aún así creo que hay pocos entornos más amigables para programar.

Tiene sus cosas, no obstante, pero se le pilla el truco rápido. Algo que me ha costado un poco es añadir los frameworks necesarios para trabajar con OpenGL. Esto se puede extrapolar a compiladores, opciones de compilación, etc…

En primer lugar tenemos es útil conocer el comando de compilación para lo que estamos haciendo, en mi caso para compilar algo en OpenGL la orden es:

g++ test.o -o test -framework OpenGL -framework GLUT

Necesitamos pues trabajar con un compilador que soporte C++ y añadir los frameworks OpenGL y GLUT.

  1. Creamos nuestro proyecto, pinchamos sobre él en el explorador.
  2. Seleccionamos el target.
  3. Vamos a la pestaña build phases.
  4. Añadimos los dos frameworks.

Captura de pantalla 2013-05-23 a la(s) 11.47.34

 

Una cosa más, posiblemente al compilar os salte un error si habéis incluido en vuestro código la sentencia “exit(0)” -muy común para cerrar ventanas y menús- el cual solucionaremos añadiendo la librería <cstdlib> antes de <GLUT/glut.h>. Existen otras formas pero esta es la más sencilla.

Espero que os sirva.

Publicado en básico | Deja un comentario

Algunas notas sobre ajuste de curvas y sus consecuencias.

Me ha parecido interesante escribir sobre esto con el fin de llamar la atención sobre el uso de herramientas que nos permitan ver gráficamente los datos. Yo vi las coordenadas por encima y las di por buenas, las vi en gnuplot y también. Hasta que no he hecho zoom no han salido a la luz los problemas. Las consecuencias podían ser nefastas.

Mirando de cerca el código que emplee para alterar la forma de los perfiles anteriormente he descubierto algún error. Detalles matemáticos, pero con importancia.
A muchos pueden sonarnos iguales los términos “b-spline” y “spline”. Los términos pueden ser muy parecidos, pero el concepto matemático es totalmente distinto. Las curvas de tipo spline son más parecidas a los polinomios de interpolación a los que estamos acostumbrados que a las curvas que aparecen en los programas de CAD, por ejemplo. Las splines se definen matemáticamente bajo la condición de que el punto de interpolación ha de formar parte de la curva, tiene que pertenecer a ella. Los splines cúbicos se adaptan muy bien a las curvas con un grado bajo y evitando las oscilaciones de los polinomios de grado alto -se “descontrolan” al pasar de grado 5-6 por norma. A priori pueden parecer nuestros amigos pero…
El resultado de emplear una spline para enlazar los puntos de control.
El resultado de emplear una spline para enlazar los puntos de control.
Por el contrario las curvas de tipo b-spline no necesariamente han de contener a los puntos de interpolación, solamente han de contener obligatoriamente a los puntos inicial y final y debido a ello son más suaves (ojo! dependiendo del grado, pero ahora no es momento de entrar ahí).
Aún nos quedan un par de detalles, puesto que los perfiles han de tener una cuerda fija, en mi caso de la unidad, es imprescindible que los puntos (0,0) y (1,0) que definen el borde de ataque y el de salida respectivamente queden contenidos en las curvas. Es este hecho el que hace que siempre que se aproxime un perfil (por la vía fácil) se haga mediante dos b-splines en lugar de una.
El ajuste de curvas mediante b-splines puede requerir un poco de picardía por nuestra parte, debemos colocar más puntos de control allí donde sea necesario mantener una buena fidelidad con el modelo, como el caso del borde de ataque. El resultado puede ser algo así, para un caso rápido:
Ajuste de los puntos de control mediante b-splines.
Ajuste de los puntos de control mediante b-splines.
Aún así, los resultados no son óptimos. El ajuste de curvas mediante b-splines no es tan simple cuando lo queremos hacer con pocos puntos de control. Miremos el resultado; en azul la forma óptima, el contorno interior es el que ajusta con 14 puntos de control:
Comparativa entre los métodos de ajuste.Si queremos que el resultado -con pocos puntos de control- sea óptimo hay que hacer un ajuste inverso de la curva, en lugar de tomar puntos de control sin más hay que contestar a la pregunta ¿dónde han de situarse los puntos de control para que la b-spline ajuste perfectamente?. No es complicado, jugad un poco.
Antes de hacer la comparativa entre mallas estructuradas y no estructuradas quería jugar un poco con gmsh, desgraciadamente los exámenes no dejan mucho tiempo libre. Aunque siempre hay algún hueco.
Publicado en Aerodinamica, básico, cfd | Etiquetado , | Deja un comentario

Mallado no estructurado de perfiles con gmsh

El trago de gmsh no ha sabido tan amargo como esperaba. Con poco trabajo se puede escribir un script que automatice todo el proceso una vez se ha entendido la sintaxis del programa.

Hace mallas muy rápido, el precio a pagar es la complejidad, como en la mayoría de software científico. Aún así me ha resultado bastante más amigable que la herramienta nativa de OpenFOAM.

Mallado no estructurado de un perfil con gmsh

 

Es un lujo, menos tiempo y esfuerzo para hacer lo mismo que star-ccm+ por ejemplo.

Con un poco de rodaje creo que le cogeré el punto. La semana que viene escribiré con algunos datos en la mano comprando la mallas; estructuradas vs. no-estructuradas. Tiempos, residuales y errores.

También hablaré más del tema y colgaré los scripts de Python para hacerlo.

Se aceptan sugerencias.

Publicado en Aerodinamica, OpenFOAM, python | Etiquetado , , , , , | Deja un comentario

Code: read numeric values with C++. From file to array.

Here is a small C++ piece of code for those who need to read some numeric values from files.
My file format is bellow; scientific notation, commented lines.. etc.
# Time	Cd	Cl	Cm
1	-660.486	1.0285	-0.239618
2	-326.356	-1.86631	0.047646
3	166.242	-1.14998	0.0979555
4	235.177	0.835717	-0.0900095
25	-5.1296	0.303168	-0.191563
75	-5.11033	0.0031427	-2.12211e-05
76	-5.09629	0.00546416	-0.00169277
77	-5.09693	0.00482644	-0.0037029
Here is a generic code witch works this my format, probably needs some small modifications for you.

/*
 * =====================================================================================
 *
 *       Filename:  readData.cpp
 *
 *    Description:  Programa para almacenar valores numericos tomados de un fichero
 *                  Soporta notacion cientifica (e-01) y lineas comentadas.
 *
 *        Version:  1.0
 *        Created:  10/01/13 10:57:22
 *       Revision:  0
 *       Compiler:  g++
 *
 *         Author:  Samuel Rodriguez Bernabeu (srodrb), srodrb@gmail.com
 *   Organization:  University of Leon (Spain)
 *
 * =====================================================================================
 */

#include <iostream>
#include <fstream>
#include <string>
#include <math.h>  // necesaria para pow
#include <cstdlib> // para atof en c++
using namespace std;

int main(int argc, const char *argv[])
{
//============================== Function arguments
string commentSymbol = "#";
const char* filename = "forceCoeffs/0/forceCoeffs.dat";
//============================== end of function arguments

string STRING;
string value;
string exp; //contiene el numero
int expValue = 0;
int numberOfLines = 0;
string previousLine="";
int numberOfValues = 1;

ifstream infile;
infile.open (filename);
if(!infile){printf("ERROR: cannot open %s\n", filename );}

while(!infile.eof())
{
    getline(infile,STRING);

    if ((STRING != previousLine) && (STRING[0] != commentSymbol[0]))
    {
        numberOfLines++;

        if(numberOfLines == 1)
        {
            for (int i = 0; i < STRING.size()-1; i++) {
                if( isspace(STRING.at(i)) )
                {
                    numberOfValues++;
                }
            }
        }

        previousLine=STRING;
    }
}

numberOfLines--;
infile.close();

float values[numberOfLines][numberOfValues];
int column = 0;
int row = 0;

infile.open (filename);
if(!infile){printf("ERROR: cannot open %s\n", filename );}

while(!infile.eof())
{
    getline(infile,STRING);
    if (STRING != previousLine)
    {
        if( STRING[0] != commentSymbol[0] )
        {
            for (int i = 0; i < STRING.size(); i++) {

                if (!isspace(STRING.at(i))){

                    if(STRING.at(i) == 'e')
                    {
                        i++;
                        while( !isspace(STRING.at(i))) {
                            exp.append(STRING,i,1);
                            i++;
                            expValue = atoi(exp.c_str());
                            if(i == STRING.size()){goto endLoop;}
                        }
                    }

                    else{
                        value.append(STRING,i,1);
                        if(i==STRING.size()-1){goto endLoop;}
                    }
                }

                else{
                    endLoop:

                    values[row-1][column] =
                                     (double)strtod(value.c_str(),NULL)*
                                     (pow(10,expValue));
                    expValue = 0;
                    column++;
                    value.clear();
                    exp.clear();

                    }
            }
        }
        column = 0;
        row++;
    }
}

row = row-1; // non out-of-bounds condition
return 0;
}
Here is the output showing the array values:

1  -660.486  1.0285  -0.239618
2  -326.356  -1.86631  0.047646
3  166.242  -1.14998  0.0979555
4  235.177  0.835717  -0.0900095
25  -5.1296  0.303168  -0.191563
75  -5.11033  0.0031427  -2.12211e-05
76  -5.09629  0.00546416  -0.00169277
77  -5.09693  0.00482644  -0.0037029

Publicado en C/C++, OpenFOAM | Etiquetado , , | Deja un comentario

Leer valores numéricos de un fichero y almacenarlos en un array. C++.

Es un secreto a voces que hay lenguajes de programación más aptos a la hora de realizar ciertas tareas. Lo que nos ocupa hoy nos llevaría 3 o 4 líneas con Numpy (Python), en cambio con C++ veréis que es algo más largo.
En esta situación uno puede verse tentado a emplear algún truco para poder usar sus funciones favoritas en otros lenguajes, sin embargo dejarnos llevar por ello puede depararnos más complicaciones y quebraderos de cabeza que los que podemos tener si nos paramos a escribir un código en consonancia con el resto de nuestro proyecto. Pensad que si se escribe una vez, ya no deberá volver a escribirse.
Pensad también en otro problema de usar “trucos”; la portabilidad. Las librerías estándar no fallan, no necesitan opciones de compilación adicionales, etc.
Desconozco muchas cosas de este lenguaje y seguramente mi código será torpe y redundante, pero funciona y lo hace con las librerías estándar, que es lo que nos interesa a los que hacemos números (;
El problema es el siguiente, tenemos un fichero con datos que queremos guardar en un array y su formato es el siguiente:
# Time	Cd	Cl	Cm
1	-660.486	1.0285	-0.239618
2	-326.356	-1.86631	0.047646
3	166.242	-1.14998	0.0979555
4	235.177	0.835717	-0.0900095
25	-5.1296	0.303168	-0.191563
75	-5.11033	0.0031427	-2.12211e-05
76	-5.09629	0.00546416	-0.00169277
77	-5.09693	0.00482644	-0.0037029
Es el formato de salida de las funciones para calcular fuerzas sobre una superficie en OpenFOAM.
Como vemos tenemos notación científica y comentarios, hay que lidiar con ello.
El siguiente código hace tres cosas; cuenta las líneas no comentadas y las columnas de datos que hay en fichero y con ello dimensiona el array de datos, lee los datos como cadenas y los transforma a coma flotante, almacena los datos en el array.

/*
 * =====================================================================================
 *
 *       Filename:  readData.cpp
 *
 *    Description:  Programa para almacenar valores numericos tomados de un fichero
 *                  Soporta notacion cientifica (e-01) y lineas comentadas.
 *
 *        Version:  1.0
 *        Created:  10/01/13 10:57:22
 *       Revision:  0
 *       Compiler:  g++
 *
 *         Author:  Samuel Rodriguez Bernabeu (srodrb), srodrb@gmail.com
 *   Organization:  University of Leon (Spain)
 *
 * =====================================================================================
 */

#include <iostream>
#include <fstream>
#include <string>
#include <math.h>  // necesaria para pow
#include <cstdlib> // para atof en c++
using namespace std;

int main(int argc, const char *argv[])
{
//============================== Variables modificables por el usuario
string commentSymbol = "#";
const char* filename = "forceCoeffs/0/forceCoeffs.dat";
//============================== fin de las variables modificables por el usuario

string STRING;
string value;
string exp; //contiene el numero
int expValue = 0;
int numberOfLines = 0;
string previousLine="";
int numberOfValues = 1;

ifstream infile;
infile.open (filename);
if(!infile){printf("ERROR: cannot open %s\n", filename );}

// Contaremos las lineas que hay en el fichero, para poder reservar memoria adecuadamente
while(!infile.eof())
{
    getline(infile,STRING);

    if ((STRING != previousLine) && (STRING[0] != commentSymbol[0]))
    {
        numberOfLines++; // Contador del numero de lineas no comentadas

        if(numberOfLines == 1)
        {
            // Este bucle nos permite contar el numero de columnas que tiene el fichero.
            for (int i = 0; i < STRING.size()-1; i++) {
                // Mi formato solo tiene un espacio, de lo contrario modifica esta parte.
                if( isspace(STRING.at(i)) )
                {
                    numberOfValues++;
                }
            }
        }

        previousLine=STRING;
    }
}

numberOfLines--; // restamos uno porque no nos damos cuenta de que la linea esta repetida
                 // hasta despues de inicial el bucle de nuevo
infile.close();

float values[numberOfLines][numberOfValues];
int column = 0;
int row = 0;

infile.open (filename);
if(!infile){printf("ERROR: cannot open %s\n", filename );}

// Volvemos a abrir el fichero para empezar a leer desde cero.
while(!infile.eof())
{
    getline(infile,STRING);
    if (STRING != previousLine)
    {
        if( STRING[0] != commentSymbol[0] ) // filtramos las lineas comentadas
        {
            for (int i = 0; i < STRING.size(); i++) {

                if (!isspace(STRING.at(i))){

                    if(STRING.at(i) == 'e')
                    {
                        i++;
                        while( !isspace(STRING.at(i))) {
                            exp.append(STRING,i,1);
                            i++;
                            expValue = atoi(exp.c_str());
                            if(i == STRING.size()){goto endLoop;}
                        }
                    }

                    else{
                        value.append(STRING,i,1);
                        if(i==STRING.size()-1){goto endLoop;}
                    }
                }

                else{
                    endLoop:

                    values[row-1][column] =
                                     (double)strtod(value.c_str(),NULL)*
                                     (pow(10,expValue));
                    expValue = 0;
                    column++;
                    value.clear();
                    exp.clear();

                    }
            }
        }
        column = 0;
        row++;
    }
}

row = row-1; // Restamos 1 para no salirnos de la indexacion
             // el mismo problema de antes.
return 0;
}
Si mostramos el array tendremos la salida del programa:

1  -660.486  1.0285  -0.239618
2  -326.356  -1.86631  0.047646
3  166.242  -1.14998  0.0979555
4  235.177  0.835717  -0.0900095
25  -5.1296  0.303168  -0.191563
75  -5.11033  0.0031427  -2.12211e-05
76  -5.09629  0.00546416  -0.00169277
77  -5.09693  0.00482644  -0.0037029

Espero que le ahorre a alguien un rato escribiendo cosas que seguramente estarán en alguna librería que desconozco.
Si alguien usa el código y encuentra algún fallo no dude en escribirme. Por supuesto se aceptan mejoras, sugerencias, ect…
Publicado en C/C++, OpenFOAM, Sin categoría | Etiquetado , , | Deja un comentario

La importancia de no partir desde cero. Inicializar los campos fluidos con datos previos, un ejemplo interesante.

En las siguientes líneas se muestran un ejemplo del impacto sobre la solución del empleo de campos fluidos previamente calculados mediante solvers simples como punto de partida de solvers más completos. Los casos que se comparan son idénticos; mismos solvers, mallas, algoritmos de resolución, tolerancias… etc.
Soy consciente de que la malla usada no puede emplearse con la intención de obtener resultados de calidad, sin embargo no dispongo de recursos para emplear mallas de mayor resolución.
Tal vez recordarás con orgullo la primera vez que conseguiste que aparecieran en tu pantalla los colorines degradados fruto de tu primera simulación CFD. Seguramente pulsar botones y desplegar opciones, poner algún valor que te sonaba y seleccionar muchas otras cosas de las que no habías oído hablar nunca te condujeron al resultado.
Y es que esto del CFD siempre es confuso y algo frustrante desde el principio. Tal vez en aquella simulación llegaste sin querer, trasteando las opciones, a ver una gráfica fea que decía algo así como “Residuales”. Con el tiempo conseguir que las líneas de esa gráfica convergiesen de algún modo sería tu único y primordial objetivo, los colores quedarían en un segundo plano.
Ahora, aunque sabes que la convergencia no lo es todo -aunque sí parte- del proceso que te acerca a una solución realista del problema sigues persiguiendo esa convergencia de las variables.
En primer lugar tenemos que pensar en el significado que tiene “convergencia” para nuestro solver. Un solver, como lo entendemos en CFD, no es más que un modo de resolver unas determinadas ecuaciones de forma iterativa. Los procesos iterativos siempre tienen un criterio de parada que lleva a la finalización del algoritmo. En nuestro caso, nuestros solvers son algoritmos de resolución de sistemas de ecuaciones diferenciales, o más simple, de sistemas de ecuaciones lineales.
Estos sistemas se abordan de distinta forma según el algoritmo con el fin de hacer el proceso eficiente, pero en esencia su objetivo es el mismo que el algoritmo de Gauss-Seidel (el más conocido). El criterio de parada que se impone en estos algoritmos es que el valor de cada una de las variables en un determinada iteración (no necesariamente de ha de ser de todas) difiera “poco” del valor de esa misma variable en la siguiente iteración. La diferencia entre los valores de las variables en una iteración y la siguiente son lo que conocemos como ‘residuales’. Podemos hacer más cercana esa idea pensando en una “estabilidad” de la solución.
Seguramente ese “poco” del párrafo anterior te ha dejado algunos cabos sueltos. Bien, esa diferencia queda bien definida dentro de cada solver, veamos como se plantea en OpenFOAM mirando el fichero fvSolution:
solvers
{
   p
   {
     solver GAMG;
     tolerance 1e-06;
     relTol 0.1;
     smoother GaussSeidel;
     nPreSweeps 0;
     nPostSweeps 2;
     cacheAgglomeration true;
     nCellsInCoarsestLevel 10;
     agglomerator faceAreaPair;
     mergeLevels 1;
   }

   U
   {
     solver smoothSolver;
     smoother GaussSeidel;
     nSweeps 2;
     tolerance 1e-08;
     relTol 0.1;
   }

   nuTilda
   {
     solver smoothSolver;
     smoother GaussSeidel;
     nSweeps 2;
     tolerance 1e-08;
     relTol 0.1;
   }
}

SIMPLE
   {
     nNonOrthogonalCorrectors 0;
     pRefCell 0;
     pRefValue 0;

   residualControl
   {
     p 1e-5;
     U 1e-5;
     nuTilda 1e-5;
   }
}
Como podemos ver el criterio de parada para las tres variables en este caso es el mismo, 1e-5. OpenFOAM nos da acceso a esto para modificarlo libremente (=
Otro criterio interesante que podemos tocar en este punto son los factores de relajación. Éstos valores varían dependiendo del algoritmo que empleemos pero en esencia nos indican la variación máxima que permitimos a nuestras variables entre una iteración y la siguiente:
relaxationFactors
{
 fields
 {
 p 0.3;
 }
 equations
 {
 U 0.7;
 nuTilda 0.7;
 }
}
Aquí tenemos un ejemplo que he realizado con la misma malla y el mismo solver que empleé en el post anterior.
OpenFOAM nos permite emplear las condiciones finales (o las que deseemos) como punto de partida en otro solver. Sobra decir que los casos han de ser geométricamente iguales*.
Vamos a ver los resultados de los residuales:
Únicamente empleando simpleFoam.

residuales para simplefoam

simpleFoam + potentialFoam (solver para flujo potencial del cual extraemos una aproximación del campo de presiones y velocidades).residuales simplefoam + potentialfoam

 

Como podéis ver los resultados hablan por sí mismos. Iniciar los solvers con los valores de los campos fluidos de otros solvers más simples es un recurso básico pero potente que nos ayuda a alcanzar la convergencia en nuestras simulaciones. Esta es una regla recurrente que podemos aplicar múltiples veces siempre que los solvers crezcan en complejidad, por ejemplo:
potencial -> incompresible estacionario -> incompresible transitorio.
Sin embargo, como decía hace unas líneas, lograr la convergencia no significa todo, más bien nada. Vamos a pararnos a analizar los datos un minuto, podéis refrescar la memoria en el post anterior sobre cómo se han extraído los datos.
Los datos de presión que extraemos del campo son los siguientes, recordad que ambas soluciones habían alcanzado la convergencia*:
simpleFoam

presiones solo simplefoam

simpleFoam a partir de potentialFoam.presiones simpleFoam + potentialFoam
No estamos acostumbrados a ver los datos en este formato cuando se trata de analizar perfiles, así que vamos a hacer un poco de magia con Gnuplot y vamos a hacer que nos muestre el coeficiente de presiones directamente, os dejo de paso el script:

set title 'NACA 0012 alfa 0. \n simpleFoam.'
set yrange [] reverse
set ylabel 'Pressure coeff. Cp'
set xlabel 'chord x/c'
set grid
plot "sets/812/upper_surf_p.xy" u 1:($4/(0.5*30*30)) w lp pi 10 t 'Upper surface.',\
 "sets/812/lower_surf_p.xy" u 1:($4/(0.5*30*30)) w lp pi 10 t 'Lower surface.'

Volvamos a echar otro vistazo:
simpleFoam

coeficiente presiones simpleFoam

simpleFoam + potentialFoamcoeficiente de presiones simplefoam + potencial
Ahora son más familiares… pero, si ambas convergen para las mismas ecuaciones y la misma malla, ¿cómo puede ser que los resultados sean distintos?. La magia del CFD… Sin embargo nos asalta la duda, ¿cuál de los dos es cierto más aproximado? Vamos a por papers. Que empleemos el perfil Naca 0012 no es fortuito, seguramente sea el más documentado de los perfiles y no tardamos en encontrar información de calidad sobre él, como en este enlace de la NASA:
Datos publicados por la NASA en la web citada anteriormente.

Datos publicados por la NASA en la web citada anteriormente.

No cabe duda de que los datos conseguidos por el solver iniciado a partir de los datos del flujo potencial son los más acerdados.
Seguro que después de ver esto más de uno dedica un minuto al postprocesado de los datos..
*No necesariamente es cierto, OpenFOAM permite emplear por ejemplo datos de un patch extraído de otro caso y muchas cosas más, sin embargo en este artículo nos ocupa sólo el caso de inicializar dominios fluidos completos.
*simpleFoam no había alcanzado realmente la convergencia, pero la gráfica de residuales no cambia de tendencia, al menos hasta las 5000 iteraciones donde he llegado.
Publicado en Aerodinamica, cfd, OpenFOAM | Etiquetado , , | Deja un comentario

Regalo de Reyes, el mes de VIM =).

Tanto follón he dado con el dichoso editor que al final…

Regalo de reyes

Muchas gracias a @GemaZs por escribir la carta por mí, y por ese día de Reyes =)

Prometo hacer una review sobre el libro cuando termine de leerlo, voy a por él.

Publicado en Personal, Vim | Etiquetado , | Deja un comentario