Program Listing for File PDBReader.h

Return to documentation for file (include/gamer/PDBReader.h)

/*
 * ***************************************************************************
 * This file is part of the GAMer software.
 * Copyright (C) 2016-2018
 * by Christopher Lee, John Moody, Rommie Amaro, J. Andrew McCammon,
 *    and Michael Holst
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more pdbreader_details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * ***************************************************************************
 */

/*
 * Parts of this are adapted from the PDBParser developed in Chandrajit
 * Bajaj's group at The University of Texas at Austin for GAMer by Zeyun Yu.
 */

#pragma once

#include <algorithm>
#include <regex>
#include <iostream>
#include <string>
#include <fstream>
#include <array>

#include "gamer/gamer.h"
#include "gamer/Vertex.h"


namespace gamer
{

namespace pdbreader_detail
{
const double            EPSILON = 1e-3;
static const std::regex PDB(".*.pdb", std::regex::icase | std::regex::optimize);
static const std::regex PQR(".*.pqr", std::regex::icase | std::regex::optimize);
static const std::regex XYZR(".*.xyzr", std::regex::icase | std::regex::optimize);
static const std::regex atom("ATOM.*\n*", std::regex::optimize);

struct PDBelementInformation
{
    const char  * atomName;
    const char  * residueName;
    float         radius;
    float         red;
    float         green;
    float         blue;
    int           hydrophobicity;
    unsigned char residueIndex;
};

static const std::size_t     MAX_BIOCHEM_ELEMENTS = 167;

static PDBelementInformation PDBelementTable[MAX_BIOCHEM_ELEMENTS] =
{
    {" N  ", "GLY", 1.625f, 0.0f, 0.0f, 1.0f,  1, 10 },
    {" CA ", "GLY", 1.750f, 0.3f, 0.3f, 0.3f, -1, 10 },
    {" C  ", "GLY", 1.875f, 0.3f, 0.3f, 0.3f,  1, 10 },
    {" O  ", "GLY", 1.480f, 1.0f, 0.0f, 0.0f,  1, 10 },
    {" N  ", "ALA", 1.625f, 0.0f, 0.0f, 1.0f,  1,  1 },
    {" CA ", "ALA", 1.750f, 0.3f, 0.3f, 0.3f, -1,  1 },
    {" C  ", "ALA", 1.875f, 0.3f, 0.3f, 0.3f,  1,  1 },
    {" O  ", "ALA", 1.480f, 1.0f, 0.0f, 0.0f,  1,  1 },
    {" CB ", "ALA", 1.750f, 0.3f, 0.3f, 0.3f, -1,  1 },
    {" N  ", "VAL", 1.625f, 0.0f, 0.0f, 1.0f,  1, 23 },
    {" CA ", "VAL", 1.750f, 0.3f, 0.3f, 0.3f, -1, 23 },
    {" C  ", "VAL", 1.875f, 0.3f, 0.3f, 0.3f,  1, 23 },
    {" O  ", "VAL", 1.480f, 1.0f, 0.0f, 0.0f,  1, 23 },
    {" CB ", "VAL", 1.750f, 0.3f, 0.3f, 0.3f, -1, 23 },
    {" CG1", "VAL", 1.750f, 0.3f, 0.3f, 0.3f, -1, 23 },
    {" CG2", "VAL", 1.750f, 0.3f, 0.3f, 0.3f, -1, 23 },
    {" N  ", "LEU", 1.625f, 0.0f, 0.0f, 1.0f,  1, 13 },
    {" CA ", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13 },
    {" C  ", "LEU", 1.875f, 0.3f, 0.3f, 0.3f,  1, 13 },
    {" O  ", "LEU", 1.480f, 1.0f, 0.0f, 0.0f,  1, 13 },
    {" CB ", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13 },
    {" CG ", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13 },
    {" CD1", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13 },
    {" CD2", "LEU", 1.750f, 0.3f, 0.3f, 0.3f, -1, 13 },
    {" N  ", "ILE", 1.625f, 0.0f, 0.0f, 1.0f,  1, 12 },
    {" CA ", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12 },
    {" C  ", "ILE", 1.875f, 0.3f, 0.3f, 0.3f,  1, 12 },
    {" O  ", "ILE", 1.480f, 1.0f, 0.0f, 0.0f,  1, 12 },
    {" CB ", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12 },
    {" CG1", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12 },
    {" CG2", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12 },
    {" CD1", "ILE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 12 },
    {" N  ", "MET", 1.625f, 0.0f, 0.0f, 1.0f,  1, 15 },
    {" CA ", "MET", 1.750f, 0.3f, 0.3f, 0.3f, -1, 15 },
    {" C  ", "MET", 1.875f, 0.3f, 0.3f, 0.3f,  1, 15 },
    {" O  ", "MET", 1.480f, 1.0f, 0.0f, 0.0f,  1, 15 },
    {" CB ", "MET", 1.750f, 0.3f, 0.3f, 0.3f, -1, 15 },
    {" CG ", "MET", 1.750f, 0.3f, 0.3f, 0.3f, -1, 15 },
    {" SD ", "MET", 1.775f, 1.0f, 1.0f, 0.0f,  1, 15 },
    {" CE ", "MET", 1.750f, 0.3f, 0.3f, 0.3f, -1, 15 },
    {" N  ", "PRO", 1.625f, 0.0f, 0.0f, 1.0f, -1, 17 },
    {" CA ", "PRO", 1.750f, 0.3f, 0.3f, 0.3f, -1, 17 },
    {" C  ", "PRO", 1.875f, 0.3f, 0.3f, 0.3f,  1, 17 },
    {" O  ", "PRO", 1.480f, 1.0f, 0.0f, 0.0f,  1, 17 },
    {" CB ", "PRO", 1.750f, 0.3f, 0.3f, 0.3f, -1, 17 },
    {" CG ", "PRO", 1.750f, 0.3f, 0.3f, 0.3f, -1, 17 },
    {" CD ", "PRO", 1.750f, 0.3f, 0.3f, 0.3f, -1, 17 },
    {" N  ", "PHE", 1.625f, 0.0f, 0.0f, 1.0f,  1, 16 },
    {" CA ", "PHE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 16 },
    {" C  ", "PHE", 1.875f, 0.3f, 0.3f, 0.3f,  1, 16 },
    {" O  ", "PHE", 1.480f, 1.0f, 0.0f, 0.0f,  1, 16 },
    {" CB ", "PHE", 1.750f, 0.3f, 0.3f, 0.3f, -1, 16 },
    {" CG ", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16 },
    {" CD1", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16 },
    {" CD2", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16 },
    {" CE1", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16 },
    {" CE2", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16 },
    {" CZ ", "PHE", 1.775f, 0.3f, 0.3f, 0.3f, -1, 16 },
    {" N  ", "TRP", 1.625f, 0.0f, 0.0f, 1.0f,  1, 20 },
    {" CA ", "TRP", 1.750f, 0.3f, 0.3f, 0.3f, -1, 20 },
    {" C  ", "TRP", 1.875f, 0.3f, 0.3f, 0.3f,  1, 20 },
    {" O  ", "TRP", 1.480f, 1.0f, 0.0f, 0.0f,  1, 20 },
    {" CB ", "TRP", 1.750f, 0.3f, 0.3f, 0.3f, -1, 20 },
    {" CG ", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 },
    {" CD1", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 },
    {" CD2", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 },
    {" NE1", "TRP", 1.625f, 0.2f, 0.2f, 1.0f,  1, 20 },
    {" CE2", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 },
    {" CE3", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 },
    {" CZ2", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 },
    {" CZ3", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 },
    {" CH2", "TRP", 1.775f, 0.3f, 0.3f, 0.3f, -1, 20 },
    {" N  ", "SER", 1.625f, 0.0f, 0.0f, 1.0f,  1, 18 },
    {" CA ", "SER", 1.750f, 0.3f, 0.3f, 0.3f, -1, 18 },
    {" C  ", "SER", 1.875f, 0.3f, 0.3f, 0.3f,  1, 18 },
    {" O  ", "SER", 1.480f, 1.0f, 0.0f, 0.0f,  1, 18 },
    {" CB ", "SER", 1.750f, 0.3f, 0.3f, 0.3f, -1, 18 },
    {" OG ", "SER", 1.560f, 1.0f, 0.0f, 0.0f,  1, 18 },
    {" N  ", "THR", 1.625f, 0.0f, 0.0f, 1.0f,  1, 19 },
    {" CA ", "THR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 19 },
    {" C  ", "THR", 1.875f, 0.3f, 0.3f, 0.3f,  1, 19 },
    {" O  ", "THR", 1.480f, 1.0f, 0.0f, 0.0f,  1, 19 },
    {" CB ", "THR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 19 },
    {" OG1", "THR", 1.560f, 1.0f, 0.0f, 0.0f,  1, 19 },
    {" CG2", "THR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 19 },
    {" N  ", "ASN", 1.625f, 0.0f, 0.0f, 1.0f,  1,  3 },
    {" CA ", "ASN", 1.750f, 0.3f, 0.3f, 0.3f, -1,  3 },
    {" C  ", "ASN", 1.875f, 0.3f, 0.3f, 0.3f,  1,  3 },
    {" O  ", "ASN", 1.480f, 1.0f, 0.0f, 0.0f,  1,  3 },
    {" CB ", "ASN", 1.750f, 0.3f, 0.3f, 0.3f, -1,  3 },
    {" CG ", "ASN", 1.875f, 0.3f, 0.3f, 0.3f,  1,  3 },
    {" OD1", "ASN", 1.480f, 1.0f, 0.0f, 0.0f,  1,  3 },
    {" ND2", "ASN", 1.625f, 0.2f, 0.2f, 1.0f,  1,  3 },
    {" N  ", "GLN", 1.625f, 0.0f, 0.0f, 1.0f,  1,  7 },
    {" CA ", "GLN", 1.750f, 0.3f, 0.3f, 0.3f, -1,  7 },
    {" C  ", "GLN", 1.875f, 0.3f, 0.3f, 0.3f,  1,  7 },
    {" O  ", "GLN", 1.480f, 1.0f, 0.0f, 0.0f,  1,  7 },
    {" CB ", "GLN", 1.750f, 0.3f, 0.3f, 0.3f, -1,  7 },
    {" CG ", "GLN", 1.750f, 0.3f, 0.3f, 0.3f, -1,  7 },
    {" CD ", "GLN", 1.875f, 0.3f, 0.3f, 0.3f,  1,  7 },
    {" OE1", "GLN", 1.480f, 1.0f, 0.0f, 0.0f,  1,  7 },
    {" NE2", "GLN", 1.625f, 0.2f, 0.2f, 1.0f,  1,  7 },
    {" N  ", "TYR", 1.625f, 0.0f, 0.0f, 1.0f,  1, 21 },
    {" CA ", "TYR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 21 },
    {" C  ", "TYR", 1.875f, 0.3f, 0.3f, 0.3f,  1, 21 },
    {" O  ", "TYR", 1.480f, 1.0f, 0.0f, 0.0f,  1, 21 },
    {" CB ", "TYR", 1.750f, 0.3f, 0.3f, 0.3f, -1, 21 },
    {" CG ", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21 },
    {" CD1", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21 },
    {" CD2", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21 },
    {" CE1", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21 },
    {" CE2", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21 },
    {" CZ ", "TYR", 1.775f, 0.3f, 0.3f, 0.3f, -1, 21 },
    {" OH ", "TYR", 1.535f, 1.0f, 0.0f, 0.0f,  1, 21 },
    {" N  ", "CYS", 1.625f, 0.0f, 0.0f, 1.0f,  1,  6 },
    {" CA ", "CYS", 1.750f, 0.3f, 0.3f, 0.3f, -1,  6 },
    {" C  ", "CYS", 1.875f, 0.3f, 0.3f, 0.3f,  1,  6 },
    {" O  ", "CYS", 1.480f, 1.0f, 0.0f, 0.0f,  1,  6 },
    {" CB ", "CYS", 1.750f, 0.3f, 0.3f, 0.3f, -1,  6 },
    {" SG ", "CYS", 1.775f, 1.0f, 1.0f, 0.0f,  1,  6 },
    {" N  ", "LYS", 1.625f, 0.0f, 0.0f, 1.0f,  1, 14 },
    {" CA ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14 },
    {" C  ", "LYS", 1.875f, 0.3f, 0.3f, 0.3f,  1, 14 },
    {" O  ", "LYS", 1.480f, 1.0f, 0.0f, 0.0f,  1, 14 },
    {" CB ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14 },
    {" CG ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14 },
    {" CD ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14 },
    {" CE ", "LYS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 14 },
    {" NZ ", "LYS", 1.625f, 0.2f, 0.2f, 1.0f,  1, 14 },
    {" N  ", "ARG", 1.625f, 0.0f, 0.0f, 1.0f,  1,  2 },
    {" CA ", "ARG", 1.750f, 0.3f, 0.3f, 0.3f, -1,  2 },
    {" C  ", "ARG", 1.875f, 0.3f, 0.3f, 0.3f,  1,  2 },
    {" O  ", "ARG", 1.480f, 1.0f, 0.0f, 0.0f,  1,  2 },
    {" CB ", "ARG", 1.750f, 0.3f, 0.3f, 0.3f, -1,  2 },
    {" CG ", "ARG", 1.750f, 0.3f, 0.3f, 0.3f, -1,  2 },
    {" CD ", "ARG", 1.750f, 0.3f, 0.3f, 0.3f, -1,  2 },
    {" NE ", "ARG", 1.625f, 0.2f, 0.2f, 1.0f,  1,  2 },
    {" CZ ", "ARG", 1.125f, 0.3f, 0.3f, 0.3f,  1,  2 },
    {" NH1", "ARG", 1.625f, 0.2f, 0.2f, 1.0f,  1,  2 },
    {" NH2", "ARG", 1.625f, 0.2f, 0.2f, 1.0f,  1,  2 },
    {" N  ", "HIS", 1.625f, 0.0f, 0.0f, 1.0f,  1, 11 },
    {" CA ", "HIS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 11 },
    {" C  ", "HIS", 1.875f, 0.3f, 0.3f, 0.3f,  1, 11 },
    {" O  ", "HIS", 1.480f, 1.0f, 0.0f, 0.0f,  1, 11 },
    {" CB ", "HIS", 1.750f, 0.3f, 0.3f, 0.3f, -1, 11 },
    {" CG ", "HIS", 1.775f, 0.3f, 0.3f, 0.3f, -1, 11 },
    {" ND1", "HIS", 1.625f, 0.2f, 0.2f, 1.0f,  1, 11 },
    {" CD2", "HIS", 1.775f, 0.3f, 0.3f, 0.3f, -1, 11 },
    {" CE1", "HIS", 1.775f, 0.3f, 0.3f, 0.3f,  1, 11 },
    {" NE2", "HIS", 1.625f, 0.2f, 0.2f, 1.0f,  1, 11 },
    {" N  ", "ASP", 1.625f, 0.0f, 0.0f, 1.0f,  1,  4 },
    {" CA ", "ASP", 1.750f, 0.3f, 0.3f, 0.3f, -1,  4 },
    {" C  ", "ASP", 1.875f, 0.3f, 0.3f, 0.3f,  1,  4 },
    {" O  ", "ASP", 1.480f, 1.0f, 0.0f, 0.0f,  1,  4 },
    {" CB ", "ASP", 1.750f, 0.3f, 0.3f, 0.3f, -1,  4 },
    {" CG ", "ASP", 1.875f, 0.3f, 0.3f, 0.3f,  1,  4 },
    {" OD1", "ASP", 1.480f, 1.0f, 1.0f, 1.0f,  1,  4 },
    {" OD2", "ASP", 1.480f, 1.0f, 0.0f, 0.0f,  1,  4 },
    {" N  ", "GLU", 1.625f, 0.0f, 0.0f, 1.0f,  1,  8 },
    {" CA ", "GLU", 1.750f, 0.3f, 0.3f, 0.3f, -1,  8 },
    {" C  ", "GLU", 1.875f, 0.3f, 0.3f, 0.3f,  1,  8 },
    {" O  ", "GLU", 1.480f, 1.0f, 0.0f, 0.0f,  1,  8 },
    {" CB ", "GLU", 1.750f, 0.3f, 0.3f, 0.3f, -1,  8 },
    {" CG ", "GLU", 1.750f, 0.3f, 0.3f, 0.3f, -1,  8 },
    {" CD ", "GLU", 1.875f, 0.3f, 0.3f, 0.3f,  1,  8 },
    {" OE1", "GLU", 1.480f, 1.0f, 0.0f, 0.0f,  1,  8 },
    {" OE2", "GLU", 1.480f, 1.0f, 0.0f, 0.0f,  1,  8 }
    // {"SI  ", "UNL", 1.875f, 1.0f, 1.0f, 1.0f,  1, 27 },
    // {" O  ", "UNL", 1.480f, 1.0f, 0.0f, 0.0f,  1, 27 }
};

static std::map<std::string, std::map<std::string, PDBelementInformation> > PDBelementMap;
} // End namespace pdbreader_detail


struct Atom {
    Vector3f pos;
    double   radius;
};

static void initElementMap()
{
    // If the element map is empty build it...
    if (pdbreader_detail::PDBelementMap.size() == 0)
    {
        for (int i = 0; i < pdbreader_detail::MAX_BIOCHEM_ELEMENTS; ++i)
        {
            const std::string atomName = pdbreader_detail::PDBelementTable[i].atomName;
            const std::string residueName = pdbreader_detail::PDBelementTable[i].residueName;
            pdbreader_detail::PDBelementMap[residueName][atomName] = pdbreader_detail::PDBelementTable[i];
        }
    }
}

template <typename Inserter>
bool readPDB(const std::string &filename, Inserter inserter)
{
    std::ifstream infile(filename);
    std::string   line;

    initElementMap(); // Init the element map if it

    if (infile.is_open())
    {
        while (std::getline(infile, line))
        {
            std::smatch match;
            if (std::regex_match(line, match, pdbreader_detail::atom))
            {
                Atom  atom;
                // See PDB file formatting guidelines
                float x = std::atof(line.substr(30, 8).c_str());
                float y = std::atof(line.substr(38, 8).c_str());
                float z = std::atof(line.substr(46, 8).c_str());
                atom.pos = Vector({x, y, z});

                atom.radius = 1.0f; // default radius
                std::string atomName = line.substr(12, 4);
                std::string residueName = line.substr(17, 3);

                auto        innerMapIT = pdbreader_detail::PDBelementMap.find(residueName);

                if (innerMapIT != pdbreader_detail::PDBelementMap.end())
                {
                    auto innerMap = innerMapIT->second;
                    auto typeIT   = innerMap.find(atomName);
                    if (typeIT != innerMap.end())
                    {
                        atom.radius = typeIT->second.radius;
                    }
                    else
                    {
                        std::cout << "Could not find atomtype of '"
                                  << atomName << "' in residue '"
                                  << residueName << "'. "
                                  << "Using default radius." << std::endl;
                    }
                }
                else
                {
                    std::cout << "Could not find ResidueName '"
                              << residueName << "' in table. "
                              << "Using default radius." << std::endl;
                }
                *inserter++ = atom;
            }
        }
        return true;
    }
    else
    {
        std::cerr << "Unable to open \"" << filename << "\"" << std::endl;
        return false;
    }
}

template <typename Inserter>
bool readPQR(const std::string &filename, Inserter inserter)
{
    std::ifstream infile(filename);
    std::string   line;

    initElementMap(); // Init the element map if it

    if (infile.is_open())
    {
        while (std::getline(infile, line))
        {
            std::smatch match;
            if (std::regex_match(line, match, pdbreader_detail::atom))
            {
                Atom  atom;
                // See PDB file formatting guidelines
                float x = std::atof(line.substr(30, 8).c_str());
                float y = std::atof(line.substr(38, 8).c_str());
                float z = std::atof(line.substr(46, 8).c_str());
                atom.pos = Vector({x, y, z});
                atom.radius = std::atof(line.substr(62, 7).c_str());
                *inserter++ = atom;
            }
        }
        return true;
    }
    else
    {
        std::cerr << "Unable to open \"" << filename << "\"" << std::endl;
        return false;
    }
}

template <typename Iterator, typename BlurFunc>
void getMinMax(Iterator begin, Iterator end, Vector3f &min, Vector3f &max, BlurFunc &&f)
{
    float maxRad = 0.0;
    float tmpRad;

    min[0] = min[1] = min[2] = std::numeric_limits<float>::infinity();
    max[0] = max[1] = max[2] = -std::numeric_limits<float>::infinity();

    for (auto curr = begin; curr != end; ++curr)
    {
        float x = curr->pos[0];
        float y = curr->pos[1];
        float z = curr->pos[2];

        if (min[0] > x)
            min[0] = x;
        if (max[0] < x)
            max[0] = x;

        if (min[1] > y)
            min[1] = y;
        if (max[1] < y)
            max[1] = y;

        if (min[2] > z)
            min[2] = z;
        if (max[2] < z)
            max[2] = z;

        tmpRad = f(curr->radius); // * sqrt(1.0 + log(pdbreader_detail::EPSILON)
                                  // / blobbyness);
        if (maxRad < tmpRad)
            maxRad = tmpRad;
    }

    min -= Vector3f({maxRad, maxRad, maxRad});
    max += Vector3f({maxRad, maxRad, maxRad});
}

template <typename Iterator>
void blurAtoms(Iterator begin, Iterator end,
               float* dataset,
               const Vector3f &min,
               const Vector3f &maxMin,
               const Vector3i &dim,
               float blobbyness)
{

    // Functor to calculate gaussian blur
    auto evalDensity = [blobbyness](const Atom &atom, Vector3f &pnt, float maxRadius)
        -> float {
            double   expval;

            Vector3f tmp = atom.pos - pnt;
            double   r   = tmp|tmp;
            double   r0  = atom.radius*atom.radius;

            // expval = BLOBBYNESS*(r/r0 - 1.0);
            expval = blobbyness*(r-r0);

            // Truncate gaussian
            if (sqrt(r) > maxRadius)
            {
                return 0.0;
            }
            return (float) exp(expval);
        };

    Vector3f span;
    span = (maxMin).ElementwiseDivision(static_cast<Vector3f>((dim - Vector3i({1, 1, 1}))));

    float radFactor = sqrt(1.0 + log(pdbreader_detail::EPSILON)/(2.0 * blobbyness));

    for (auto curr = begin; curr != end; ++curr)
    {
        float    maxRad = curr->radius * radFactor;
        // compute the dataset coordinates of the atom's center
        Vector3f tmpVec = (curr->pos-min).ElementwiseDivision(span);
        Vector3i c;
        std::transform(tmpVec.begin(), tmpVec.end(), c.begin(), [](float v) -> int {
                return round(v);
            });

        // std::cout << "Max Radius: " << maxRad << std::endl;

        // compute the bounding box of the atom (maxRad^3)
        Vector3i amin;
        Vector3i amax;
        for (int j = 0; j < 3; ++j)
        {
            int   tmp;
            float tmpRad = maxRad/span[j];

            tmp = (int)(c[j] - tmpRad - 1);
            amin[j] = (tmp < 0) ? 0 : tmp; // check if tmp is < 0
            tmp = (int)(c[j] + tmpRad + 1);
            amax[j] = (tmp > (dim[j] - 1)) ? (dim[j] - 1) : tmp;
        }

        // std::cout << amin << " " << amax << std::endl;

        // Blur kernel in bounding box
        for (int k = amin[2]; k <= amax[2]; k++)
        {
            for (int j = amin[1]; j <= amax[1]; j++)
            {
                for (int i = amin[0]; i <= amax[0]; i++)
                {
                    Vector3f pnt = min + Vector3f({static_cast<float>(i),
                                                   static_cast<float>(j),
                                                   static_cast<float>(k)}).ElementwiseProduct(span);
                    dataset[Vect2Index(i, j, k, dim)] += evalDensity(*curr, pnt, maxRad);
                }
            }
        }
    }
}

template <typename Iterator>
void gridSAS(const Iterator begin, const Iterator end, const Vector3i &dim, float* dataset)
{
    // For atom in atoms :
    for (auto curr = begin; curr != end; ++curr)
    {
        float    radius = curr->radius;
        Vector3f pos = curr->pos;
        // compute the dataset coordinates of the atom's center
        Vector3i c;
        std::transform(pos.begin(), pos.end(), c.begin(), [](float v) -> int {
                return round(v);
            });

        // compute bounding box for atom
        Vector3i amin;
        Vector3i amax;
        for (int j = 0; j < 3; ++j)
        {
            int tmp;
            tmp = (int)(c[j] - radius - 1);
            amin[j] = (tmp < 0) ? 0 : tmp; // check if tmp is < 0
            tmp = (int)(c[j] + radius + 1);
            amax[j] = (tmp > (dim[j] - 1)) ? (dim[j] - 1) : tmp;
        }

        // Blur kernel in bounding box
        for (int k = amin[2]; k <= amax[2]; k++)
        {
            for (int j = amin[1]; j <= amax[1]; j++)
            {
                for (int i = amin[0]; i <= amax[0]; i++)
                {
                    Vector3f coord = Vector3f({static_cast<float>(i), static_cast<float>(j), static_cast<float>(k)});
                    coord -= curr->pos;
                    float    dist = -(std::sqrt(coord|coord)-radius); // inside
                                                                      // is
                                                                      // positive
                    int      idx  = Vect2Index(i, j, k, dim);

                    if (dist > dataset[idx])
                    {
                        dataset[idx] = dist;
                    }
                }
            }
        }
    }
}


template <typename Iterator>
void gridSES(const Iterator begin, const Iterator end, const Vector3i &dim,
             float* dataset, const float radius)
{
    for (auto curr = begin; curr != end; ++curr)
    {
        Vector3f pos = (*curr).position;

        // compute bounding box for atom
        Vector3i amin;
        Vector3i amax;
        for (int i = 0; i < 3; ++i)
        {
            int tmp;
            tmp = (int)(pos[i] - radius - 1);
            amin[i] = (tmp < 0) ? 0 : tmp; // check if tmp is < 0
            tmp = (int)(pos[i] + radius + 1);
            amax[i] = (tmp > (dim[i] - 1)) ? (dim[i] - 1) : tmp;
        }

        // Blur kernel in bounding box
        for (int k = amin[2]; k <= amax[2]; k++)
        {
            for (int j = amin[1]; j <= amax[1]; j++)
            {
                for (int i = amin[0]; i <= amax[0]; i++)
                {
                    Vector3f coord = Vector3f({static_cast<float>(i), static_cast<float>(j), static_cast<float>(k)});
                    coord -= pos;
                    float    dist = -(std::sqrt(coord|coord)-radius);
                    int      idx  = Vect2Index(i, j, k, dim);
                    if (dist > dataset[idx])
                    {
                        dataset[idx] = dist;
                    }
                }
            }
        }
    }
}

// TODO: (1) these functions should all take arrays of x,y,z,r instead of filenames

std::unique_ptr<SurfaceMesh> readPDB_molsurf(const std::string &filename);

std::unique_ptr<SurfaceMesh> readPDB_gauss(const std::string &filename, float blobbyness, float isovalue);

std::unique_ptr<SurfaceMesh> readPDB_distgrid(const std::string &filename, const float radius);

std::unique_ptr<SurfaceMesh> readPQR_molsurf(const std::string &filename);

std::unique_ptr<SurfaceMesh> readPQR_gauss(const std::string &filename, float blobbyness, float isovalue);

} // end namespace gamer