Desarrollar una función de parsing de ficheros PDB (que denominaremos GetPDB) que permita representar adecuadamente los datos estructurales relevantes de una proteína en nuestro entorno de programación. Dicha función deberá ser capaz de leer los datos desde un fichero pdb y almacenarlos en una estructura matricial (cuyo tipo denominaremos TPDB) integrada por registros (de tipo TAtomPDB) que contendrán campos relativos a la identificación de cada átomo, residuo y subunidad a la que pertenece, coordenadas cristalográficas (a la que asignaremos un tipo TPunto), etc. La función se empleará en las sucesivas aplicaciones para interaccionar con los ficheros de estructura.
La función CargarProteina es una de las funciones
de la librería biotools y es capaz de
leer los datos de un fichero *.pdb
y almacenarlos en una estructura matricial del tipo TPDB (explicado en la actividad 2), que permite a los sucesivos
programas creados manejar esta información. Esta función solo requiere un único
argumento de tipo TStrings. Por otra
parte, la salida de esta función (y, por tanto, la función) es de tipo TPDB. Así, el descriptor de la función
es el siguiente:
function CrearTPDB(lineas: TStrings): TPDB;
En la cláusula var se declaran las siguientes
variables:
p: TPDB;
j, n, nr, nc:
integer;
linea: string;
La variable p es de tipo TPDB y es donde se almacenarán los datos de la proteína. Las
variables j, n, nr y nc son de tipo entero y son variables
contadoras. La variable j es la
variable contadora de un bucle for,
mientras que las otras variables son más especiales y se explicarán más adelante
en esta actividad. Por último, la variable linea
se utiliza para ir guardando una a una todas las líneas contenidas en el TStrings lineas.
A continuación,
comienza el bloque principal de la función CargarProteina.
En primer lugar, aparecen 4 líneas de código con las que se fija el tamaño de
distintos arrays que habían sido
creados dinámicamente al crear los distintos tipos de records descritos en la actividad 2.
setlength(p.atom,
lineas.Count);
setlength(p.indice, lineas.Count);
setlength(p.res, lineas.Count);
setlength(p.cad, lineas.Count);
- La matriz p.atom se trata de un array de datos de tipo TAtomPDB.
- La matriz p.indice es un array de números enteros.
- La matriz p.res consiste en un array con datos de tipo TResiduoPDB.
- La matriz p.cad se trata de un array de datos de tipo TCadenaPDB.
Como la longitud de
cada uno de estos campos del TPDB es
distinta para cada proteína, el tamaño de cada una de estas matrices se fija en
exceso, es decir, se crean más elementos de los que van a rellenarse. Esto se
hace fijando el número de elementos al número de líneas que posee el TStrings lineas. Una vez que ya se han rellenado todos estos campos del record TPDB se ajusta su tamaño de nuevo
al número de elementos que tengan en concreto para esa proteína.
Posteriormente se establece
en 0 el valor de las variables n, nr y nc:
n:= 0; nr:= 0; nc:= 0;
Una vez que se han
hecho todos estos preparativos se rellenan los distintos campos del TPDB p.
Cada línea del código se explica mediante comentarios.
p.header:=
copy(lineas[0],63,66); //Este campo contiene la ID de 4
dígitos del fichero *.pdb.
for j:= 1 to
lineas.Count-1 do //Este bucle permite recorrer
todas las líneas del TStrings de
entrada de la función.
begin
linea:= lineas[j]; //Cada línea del TString se
guarda en la variable linea de tipo string.
if (copy(linea,1,6) =
'ATOM ') and ((linea[17] = ' ') or (linea[17]
= 'A')) then //Con esta condición se seleccionan solo aquellas
líneas del fichero *.pdb que
contienen información sobre los átomos de la proteína, excluyendo aquellas que
contienen átomos sobre los que se han propuesto otras coordenadas alternativas,
lo cual va ser ignorado por los programas de esta asignatura.
begin
n:= n+1; //El contador n permite
acceder a cada una de las posiciones del array
atom, con datos de tipo TAtomPDB.
p.atom[n].NumAtom:= strtoint(trim(copy(linea,7,5)));
//Este campo contiene el serial
number de cada átomo, es decir, el
número con el que se identifica cada átomo en el fichero *.pdb. No tiene por qué ser un número
consecutivo (ver p.indice).
p.atom[n].ID:=
trim(copy(linea,13,4)); //Este campo contiene la ID de
cada átomo.
p.atom[n].NumRes:=
strtoint(trim(copy(linea,23,4))); //Este campo contiene el número de
residuo al que pertenece cada átomo.
p.atom[n].AltRes:=
linea[17]; //Este campo indica si se trata de coordenadas
alternativas del átomo o no.
p.atom[n].Residuo:=
trim(copy(linea,18,3)); //Este campo indica el residuo al
que pertenece cada átomo en código de 3 letras.
p.atom[n].Subunidad:= linea[22]; //Este campo hace referencia a la
subunidad a la que pertenece cada átomo.
p.atom[n].coor.X:=
strtofloat(trim(copy(linea,31,8))); //Este campo contiene la
coordenada X de cada átomo.
p.atom[n].coor.Y:=
strtofloat(trim(copy(linea,39,8))); //Este campo contiene la
coordenada Y de cada átomo.
p.atom[n].coor.Z:=
strtofloat(trim(copy(linea,47,8))); //Este campo contiene la
coordenada Z de cada átomo.
p.indice[n]:= n; //Este campo permite asignar un número entero consecutivo a cada átomo
de la proteína, ya que el serial number que aparece en el fichero *.pdb puede no ser consecutivo (ver p.atom.NumAtom).
if p.atom[n].ID =
'N' then //La línea del primer átomo de cada residuo que aparece
en el fichero *.pdb siempre
coincide con el la línea del átomo de nitrógeno que forma parte del esqueleto
covalente de la proteína. Por tanto, este condicional permite rellenar los
campos del TPDB relacionados con
residuos y no con átomos.
begin
nr:= nr+1; //El contador nr permite
acceder a cada una de las posiciones del array
res, con datos de tipo TResiduoPDB.
p.res[nr].NumRes:=
p.atom[n].NumRes; //Este campo permite asignar a
cada residuo un número entero consecutivo.
p.res[nr].ID:=
p.atom[n].Residuo; //Este campo permite asignar a
cada residuo su ID en código de 3 letras.
p.res[nr].Subunidad:=
p.atom[n].Subunidad; //Este campo indica la subunidad a la que pertenece cada residuo.
p.res[nr].N:= n; //Este campo contiene el serial
number de cada átomo de nitrógeno del esqueleto covalente de la proteína.
end;
if p.atom[n].ID =
'CA' then p.res[nr].CA:= n; //Este campo contiene el serial number de cada carbono α del esqueleto covalente de la proteína.
if p.atom[n].ID =
'C' then p.res[nr].C:= n; //Este campo contiene el serial number de cada carbono
carbonílico del esqueleto covalente de la proteína.
if p.atom[n].ID =
'O' then p.res[nr].O:= n; //Este campo contiene el serial number de cada átomo de oxígeno
del esqueleto covalente de la proteína.
if pos(p.atom[n].subunidad,
p.cadenas) = 0 then //Este condicional se cumplirá
cuando la subunidad a la que pertenezca un átomo determinado no esté presente
en el campo p.cadenas. Esto se
traduce en que ha aparecido una nueva subunidad y se rellenarán los campos
necesarios relacionados con esta nueva subunidad.
begin
p.cadenas:=
p.cadenas + p.atom[n].Subunidad; //Esto permite elongar el campo p.cadenas (de tipo string) con la ID de la nueva subunidad.
nc:= nc + 1; //El contador nc permite
acceder a cada una de las posiciones del array
candenas, con datos de tipo TCadenaPDB.
p.cad[nc].ID:=
p.atom[n].Subunidad; //Este campo contiene la ID de
cada subunidad.
p.cad[nc].AtomIni:= n; //Este campo contiene el serial number del primer átomo de cada
subunidad.
p.cad[nc].AtomFin:= n; //Este campo contiene el serial number del último átomo de cada
subunidad.
p.cad[nc].ResIni:=
nr; //Este campo contiene el serial number del primer residuo de cada subunidad.
p.cad[nc].ResFin:=
nr; //Este campo contiene el serial number del
último residuo de cada subunidad.
end else //Si no se está iniciando una subunidad, que es lo que ocurre en la
mayoría de los casos:
begin
p.cad[nc].AtomFin:= n; //Este campo contiene el serial number del último átomo de cada
subunidad.
p.cad[nc].ResFin:=
nr; //Este campo contiene el serial number del último residuo de cada subunidad.
end;
end;
end;
p.secuencia:= ''; //Con esta línea y la siguiente se consigue crear un string con la secuencia primaria de la
proteína en código de una letra.
for j:= 1 to nr do
p.secuencia:= p.secuencia + aa3to1(p.res[j].ID);
p.NumFichas:= n; //Este campo contiene el número de átomos total de la proteína.
p.NumRes:= nr; //Este campo contiene el número de residuos total de la proteína.
p.NumCad:= nc; //Este campo contiene el número de cadenas total de la proteína.
Las últimas líneas
de esta función permiten ajustar el tamaño de los distintos arrays declarados dinámicamente al
principio al número de elementos que posean para cada proteína dada y asignar
la salida de la función a la variable local de tipo TPDB p:
setlength(p.atom,
n+1);
setlength(p.indice, n+1);
setlength(p.res, nr+1);
setlength(p.cad,
nc+1);
result:= p;
Bibliografía
- Apuntes de clase de la asignatura “Ingeniería de Proteínas” del Grado en Bioquímica.
- https://wiki.freepascal.org/. Fecha de acceso: 19 de junio de 2019.