<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of mcompositor.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mtexturepixmapitem.h"
#include "mtexturepixmapitem_p.h"

#include <QPainterPath>
#include <QRect>
#include <QGLContext>
#include <QX11Info>
#include <vector>

#include <X11/Xlib.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xrender.h>
#include <X11/extensions/Xfixes.h>

//#define GL_GLEXT_PROTOTYPES

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

static PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = 0;
static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = 0;
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = 0;
static EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };

class EglTextureManager
{
public:
    // TODO: this should be dynamic
    // use QCache instead like Qt's GL backend
    static const int sz = 20;

    EglTextureManager() {
        glGenTextures(sz, tex);
        for (int i = 0; i < sz; i++)
            textures.push_back(tex[i]);
    }

    ~EglTextureManager() {
        glDeleteTextures(sz, tex);
    }

    GLuint getTexture() {
        if (textures.empty()) {
            qWarning("Empty texture stack");
            return 0;
        }
        GLuint ret = textures.back();
        textures.pop_back();
        return ret;
    }

    void closeTexture(GLuint texture) {
        // clear this texture
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA,
                     GL_UNSIGNED_BYTE, 0);
        textures.push_back(texture);
    }

    GLuint tex[sz+1];
    std::vector<GLuint> textures;
};

class EglResourceManager
{
public:
    EglResourceManager()
        : has_tfp(false) {
        if (!dpy)
            dpy = eglGetDisplay(EGLNativeDisplayType(QX11Info::display()));

        QString exts = QLatin1String(eglQueryString(dpy, EGL_EXTENSIONS));
        if ((exts.contains("EGL_KHR_image") &&
             exts.contains("EGL_KHR_image_pixmap") &&
             exts.contains("EGL_KHR_gl_texture_2D_image")) || 1) {
            has_tfp = true;
            eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC) eglGetProcAddress("eglCreateImageKHR");
            eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress("eglDestroyImageKHR");
            glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) eglGetProcAddress("glEGLImageTargetTexture2DOES");
        } else {
            qCritical() << "EGL extensions:" << exts;
            qFatal("no EGL tfp support, aborting\n");
        }
        texman = new EglTextureManager();
    }

    bool texturePixmapSupport() {
        return has_tfp;
    }

    EglTextureManager *texman;
    static EGLConfig config;
    static EGLConfig configAlpha;
    static EGLDisplay dpy;

    bool has_tfp;
};

EglResourceManager *MTexturePixmapPrivate::eglresource = 0;
EGLConfig EglResourceManager::config = 0;
EGLConfig EglResourceManager::configAlpha = 0;
EGLDisplay EglResourceManager::dpy = 0;

void MTexturePixmapItem::init()
{
    if (isValid() && (windowAttributes()->map_state != IsViewable)) {
        qWarning("MTexturePixmapItem::%s(): Failed getting offscreen pixmap",
                 __func__);
        d->setValid(false);
        return;
    } else if (!isValid())
        return;

    if (!d->eglresource)
        d->eglresource = new EglResourceManager();

    d->custom_tfp = !d->eglresource->texturePixmapSupport();
    if (d->custom_tfp) {
        initCustomTfp();
        return;
    }
    saveBackingStore();
    d->ctx->makeCurrent();
    d->egl_image = eglCreateImageKHR(d->eglresource->dpy, 0,
                                     EGL_NATIVE_PIXMAP_KHR,
                                     (EGLClientBuffer)d->windowp,
                                     attribs);
    if (d->egl_image == EGL_NO_IMAGE_KHR)
        qWarning("MTexturePixmapItem::%s(): Cannot create EGL image: 0x%x",
                 __func__, eglGetError());

    d->textureId = d->eglresource->texman->getTexture();
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, d->textureId);

    if (d->egl_image != EGL_NO_IMAGE_KHR)
        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, d->egl_image);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}

MTexturePixmapItem::MTexturePixmapItem(Window window, QGLWidget *glwidget, QGraphicsItem *parent)
    : MCompositeWindow(window, parent),
      d(new MTexturePixmapPrivate(window, glwidget, this))
{
    if (!d->ctx)
        d->ctx = const_cast<QGLContext *>(glwidget->context());
    init();
}

void MTexturePixmapItem::saveBackingStore(bool renew)
{
    d->saveBackingStore(renew);
}

void MTexturePixmapItem::rebindPixmap()
{
    if (d->egl_image != EGL_NO_IMAGE_KHR) {
        eglDestroyImageKHR(d->eglresource->dpy, d->egl_image);
        glBindTexture(GL_TEXTURE_2D, 0);
    }

    if (!d->windowp) {
        d->egl_image = EGL_NO_IMAGE_KHR;
        return;
    }

    d->ctx->makeCurrent();
    d->egl_image = eglCreateImageKHR(d->eglresource->dpy, 0,
                                     EGL_NATIVE_PIXMAP_KHR,
                                     (EGLClientBuffer)d->windowp,
                                     attribs);
    if (d->egl_image == EGL_NO_IMAGE_KHR)
        qWarning("MTexturePixmapItem::%s(): Cannot create EGL image: 0x%x",
                 __func__, eglGetError());

    glBindTexture(GL_TEXTURE_2D, d->textureId);
    if (d->egl_image != EGL_NO_IMAGE_KHR)
        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, d->egl_image);
}

void MTexturePixmapItem::enableDirectFbRendering()
{
    d->damageTracking(false);

    if (d->direct_fb_render && d->egl_image == EGL_NO_IMAGE_KHR)
        return;

    d->direct_fb_render = true;

    glBindTexture(GL_TEXTURE_2D, 0);
    eglDestroyImageKHR(d->eglresource->dpy, d->egl_image);
    d->egl_image = EGL_NO_IMAGE_KHR;
    if (d->windowp) {
        XFreePixmap(QX11Info::display(), d->windowp);
        d->windowp = 0;
    }
    XCompositeUnredirectWindow(QX11Info::display(), window(),
                               CompositeRedirectManual);
}

void MTexturePixmapItem::enableRedirectedRendering()
{
    d->damageTracking(true);

    if (!d->direct_fb_render && d->egl_image != EGL_NO_IMAGE_KHR)
        return;

    d->direct_fb_render = false;
    XCompositeRedirectWindow(QX11Info::display(), window(),
                             CompositeRedirectManual);
    saveBackingStore(true);
    updateWindowPixmap();
}

bool MTexturePixmapItem::isDirectRendered() const
{
    return d->direct_fb_render;
}

MTexturePixmapItem::~MTexturePixmapItem()
{
    cleanup();
    delete d;
}

void MTexturePixmapItem::initCustomTfp()
{
    d->ctextureId = d->eglresource->texman->getTexture();

    glBindTexture(GL_TEXTURE_2D, d->ctextureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

void MTexturePixmapItem::cleanup()
{
    eglDestroyImageKHR(d->eglresource->dpy, d->egl_image);
    d->egl_image = EGL_NO_IMAGE_KHR;
    XSync(QX11Info::display(), FALSE);

    if (!d->custom_tfp)
        d->eglresource->texman->closeTexture(d->textureId);
    else
        d->eglresource->texman->closeTexture(d->ctextureId);

#if (QT_VERSION < 0x040600)
    eglMakeCurrent(d->eglresource->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
#endif

    XFreePixmap(QX11Info::display(), d->windowp);
}

void MTexturePixmapItem::updateWindowPixmap(XRectangle *rects, int num)
{
    if (isTransitioning() || d->direct_fb_render || !windowVisible())
        return;

    QRegion r;
    for (int i = 0; i < num; ++i)
        r += QRegion(rects[i].x, rects[i].y, rects[i].width, rects[i].height);
    d->damageRegion = r;

    // Our very own custom texture from pixmap
    if (d->custom_tfp) {
        QPixmap qp = QPixmap::fromX11Pixmap(d->windowp);

        QImage img = d->glwidget->convertToGLFormat(qp.toImage());
        glBindTexture(GL_TEXTURE_2D, d->ctextureId);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width(), img.height(), 0,
                     GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
        update();
    } else {
        if (d->egl_image == EGL_NO_IMAGE_KHR)
            saveBackingStore(true);
        d->glwidget->update();
    }
}

void MTexturePixmapItem::paint(QPainter *painter,
                                 const QStyleOptionGraphicsItem *option,
                                 QWidget *widget)
{
    Q_UNUSED(option)
    Q_UNUSED(widget)

    if (d->direct_fb_render) {
        glBindTexture(GL_TEXTURE_2D, 0);
        return;
    }

#if QT_VERSION < 0x040600
    if (painter->paintEngine()->type()  != QPaintEngine::OpenGL)
        return;
#else
    if (painter->paintEngine()->type()  != QPaintEngine::OpenGL2) {
        return;
    }
#endif
    QGLWidget *gl = (QGLWidget *) painter->device();
    if (!d->ctx)
        d->ctx = const_cast<QGLContext *>(gl->context());

    if (d->has_alpha || opacity() < 1.0f) {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
    glBindTexture(GL_TEXTURE_2D, d->custom_tfp ? d->ctextureId : d->textureId);

    if (d->damageRegion.numRects() > 1) {
        d->drawTexture(painter->combinedTransform(), boundingRect(), opacity());
        glEnable(GL_SCISSOR_TEST);
        for (int i = 0; i < d->damageRegion.numRects(); ++i) {
            glScissor(d->damageRegion.rects().at(i).x(),
                      d->brect.height() -
                      (d->damageRegion.rects().at(i).y() +
                       d->damageRegion.rects().at(i).height()),
                      d->damageRegion.rects().at(i).width(),
                      d->damageRegion.rects().at(i).height());
            d->drawTexture(painter->combinedTransform(), boundingRect(), opacity());
        }
        glDisable(GL_SCISSOR_TEST);
    } else
        d->drawTexture(painter->combinedTransform(), boundingRect(), opacity());

    // Explicitly disable blending. for some reason, the latest drivers
    // still has blending left-over even if we call glDisable(GL_BLEND)
    glBlendFunc(GL_ONE, GL_ZERO);
    glDisable(GL_BLEND);
}

void MTexturePixmapItem::windowRaised()
{
    d->windowRaised();
}

void MTexturePixmapItem::resize(int w, int h)
{
    d->resize(w, h);
}

QSizeF MTexturePixmapItem::sizeHint(Qt::SizeHint, const QSizeF &) const
{
    return boundingRect().size();
}

QRectF MTexturePixmapItem::boundingRect() const
{
    return d->brect;
}

QPainterPath MTexturePixmapItem::shape() const
{
    QPainterPath path;
    path.addRect(boundingRect());
    return path;
}

bool MTexturePixmapItem::hasAlpha() const
{
    return d->has_alpha;
}

void MTexturePixmapItem::clearTexture()
{
    glBindTexture(GL_TEXTURE_2D, d->custom_tfp ? d->ctextureId : d->textureId);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA,
                 GL_UNSIGNED_BYTE, 0);
}
<commit_msg>Changes: Removed scissoring optimizations for now. Think of alternative solution later RevBy: TrustMe<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of mcompositor.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mtexturepixmapitem.h"
#include "mtexturepixmapitem_p.h"

#include <QPainterPath>
#include <QRect>
#include <QGLContext>
#include <QX11Info>
#include <vector>

#include <X11/Xlib.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xrender.h>
#include <X11/extensions/Xfixes.h>

//#define GL_GLEXT_PROTOTYPES

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

static PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = 0;
static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = 0;
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = 0;
static EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };

class EglTextureManager
{
public:
    // TODO: this should be dynamic
    // use QCache instead like Qt's GL backend
    static const int sz = 20;

    EglTextureManager() {
        glGenTextures(sz, tex);
        for (int i = 0; i < sz; i++)
            textures.push_back(tex[i]);
    }

    ~EglTextureManager() {
        glDeleteTextures(sz, tex);
    }

    GLuint getTexture() {
        if (textures.empty()) {
            qWarning("Empty texture stack");
            return 0;
        }
        GLuint ret = textures.back();
        textures.pop_back();
        return ret;
    }

    void closeTexture(GLuint texture) {
        // clear this texture
        glBindTexture(GL_TEXTURE_2D, texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA,
                     GL_UNSIGNED_BYTE, 0);
        textures.push_back(texture);
    }

    GLuint tex[sz+1];
    std::vector<GLuint> textures;
};

class EglResourceManager
{
public:
    EglResourceManager()
        : has_tfp(false) {
        if (!dpy)
            dpy = eglGetDisplay(EGLNativeDisplayType(QX11Info::display()));

        QString exts = QLatin1String(eglQueryString(dpy, EGL_EXTENSIONS));
        if ((exts.contains("EGL_KHR_image") &&
             exts.contains("EGL_KHR_image_pixmap") &&
             exts.contains("EGL_KHR_gl_texture_2D_image")) || 1) {
            has_tfp = true;
            eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC) eglGetProcAddress("eglCreateImageKHR");
            eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress("eglDestroyImageKHR");
            glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) eglGetProcAddress("glEGLImageTargetTexture2DOES");
        } else {
            qCritical() << "EGL extensions:" << exts;
            qFatal("no EGL tfp support, aborting\n");
        }
        texman = new EglTextureManager();
    }

    bool texturePixmapSupport() {
        return has_tfp;
    }

    EglTextureManager *texman;
    static EGLConfig config;
    static EGLConfig configAlpha;
    static EGLDisplay dpy;

    bool has_tfp;
};

EglResourceManager *MTexturePixmapPrivate::eglresource = 0;
EGLConfig EglResourceManager::config = 0;
EGLConfig EglResourceManager::configAlpha = 0;
EGLDisplay EglResourceManager::dpy = 0;

void MTexturePixmapItem::init()
{
    if (isValid() && (windowAttributes()->map_state != IsViewable)) {
        qWarning("MTexturePixmapItem::%s(): Failed getting offscreen pixmap",
                 __func__);
        d->setValid(false);
        return;
    } else if (!isValid())
        return;

    if (!d->eglresource)
        d->eglresource = new EglResourceManager();

    d->custom_tfp = !d->eglresource->texturePixmapSupport();
    if (d->custom_tfp) {
        initCustomTfp();
        return;
    }
    saveBackingStore();
    d->ctx->makeCurrent();
    d->egl_image = eglCreateImageKHR(d->eglresource->dpy, 0,
                                     EGL_NATIVE_PIXMAP_KHR,
                                     (EGLClientBuffer)d->windowp,
                                     attribs);
    if (d->egl_image == EGL_NO_IMAGE_KHR)
        qWarning("MTexturePixmapItem::%s(): Cannot create EGL image: 0x%x",
                 __func__, eglGetError());

    d->textureId = d->eglresource->texman->getTexture();
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, d->textureId);

    if (d->egl_image != EGL_NO_IMAGE_KHR)
        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, d->egl_image);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}

MTexturePixmapItem::MTexturePixmapItem(Window window, QGLWidget *glwidget, QGraphicsItem *parent)
    : MCompositeWindow(window, parent),
      d(new MTexturePixmapPrivate(window, glwidget, this))
{
    if (!d->ctx)
        d->ctx = const_cast<QGLContext *>(glwidget->context());
    init();
}

void MTexturePixmapItem::saveBackingStore(bool renew)
{
    d->saveBackingStore(renew);
}

void MTexturePixmapItem::rebindPixmap()
{
    if (d->egl_image != EGL_NO_IMAGE_KHR) {
        eglDestroyImageKHR(d->eglresource->dpy, d->egl_image);
        glBindTexture(GL_TEXTURE_2D, 0);
    }

    if (!d->windowp) {
        d->egl_image = EGL_NO_IMAGE_KHR;
        return;
    }

    d->ctx->makeCurrent();
    d->egl_image = eglCreateImageKHR(d->eglresource->dpy, 0,
                                     EGL_NATIVE_PIXMAP_KHR,
                                     (EGLClientBuffer)d->windowp,
                                     attribs);
    if (d->egl_image == EGL_NO_IMAGE_KHR)
        qWarning("MTexturePixmapItem::%s(): Cannot create EGL image: 0x%x",
                 __func__, eglGetError());

    glBindTexture(GL_TEXTURE_2D, d->textureId);
    if (d->egl_image != EGL_NO_IMAGE_KHR)
        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, d->egl_image);
}

void MTexturePixmapItem::enableDirectFbRendering()
{
    d->damageTracking(false);

    if (d->direct_fb_render && d->egl_image == EGL_NO_IMAGE_KHR)
        return;

    d->direct_fb_render = true;

    glBindTexture(GL_TEXTURE_2D, 0);
    eglDestroyImageKHR(d->eglresource->dpy, d->egl_image);
    d->egl_image = EGL_NO_IMAGE_KHR;
    if (d->windowp) {
        XFreePixmap(QX11Info::display(), d->windowp);
        d->windowp = 0;
    }
    XCompositeUnredirectWindow(QX11Info::display(), window(),
                               CompositeRedirectManual);
}

void MTexturePixmapItem::enableRedirectedRendering()
{
    d->damageTracking(true);

    if (!d->direct_fb_render && d->egl_image != EGL_NO_IMAGE_KHR)
        return;

    d->direct_fb_render = false;
    XCompositeRedirectWindow(QX11Info::display(), window(),
                             CompositeRedirectManual);
    saveBackingStore(true);
    updateWindowPixmap();
}

bool MTexturePixmapItem::isDirectRendered() const
{
    return d->direct_fb_render;
}

MTexturePixmapItem::~MTexturePixmapItem()
{
    cleanup();
    delete d;
}

void MTexturePixmapItem::initCustomTfp()
{
    d->ctextureId = d->eglresource->texman->getTexture();

    glBindTexture(GL_TEXTURE_2D, d->ctextureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

void MTexturePixmapItem::cleanup()
{
    eglDestroyImageKHR(d->eglresource->dpy, d->egl_image);
    d->egl_image = EGL_NO_IMAGE_KHR;
    XSync(QX11Info::display(), FALSE);

    if (!d->custom_tfp)
        d->eglresource->texman->closeTexture(d->textureId);
    else
        d->eglresource->texman->closeTexture(d->ctextureId);

#if (QT_VERSION < 0x040600)
    eglMakeCurrent(d->eglresource->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
#endif

    XFreePixmap(QX11Info::display(), d->windowp);
}

void MTexturePixmapItem::updateWindowPixmap(XRectangle *rects, int num)
{
    if (isTransitioning() || d->direct_fb_render || !windowVisible())
        return;

    QRegion r;
    for (int i = 0; i < num; ++i)
        r += QRegion(rects[i].x, rects[i].y, rects[i].width, rects[i].height);
    d->damageRegion = r;

    // Our very own custom texture from pixmap
    if (d->custom_tfp) {
        QPixmap qp = QPixmap::fromX11Pixmap(d->windowp);

        QImage img = d->glwidget->convertToGLFormat(qp.toImage());
        glBindTexture(GL_TEXTURE_2D, d->ctextureId);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width(), img.height(), 0,
                     GL_RGBA, GL_UNSIGNED_BYTE, img.bits());
        update();
    } else {
        if (d->egl_image == EGL_NO_IMAGE_KHR)
            saveBackingStore(true);
        d->glwidget->update();
    }
}

void MTexturePixmapItem::paint(QPainter *painter,
                                 const QStyleOptionGraphicsItem *option,
                                 QWidget *widget)
{
    Q_UNUSED(option)
    Q_UNUSED(widget)

    if (d->direct_fb_render) {
        glBindTexture(GL_TEXTURE_2D, 0);
        return;
    }

#if QT_VERSION < 0x040600
    if (painter->paintEngine()->type()  != QPaintEngine::OpenGL)
        return;
#else
    if (painter->paintEngine()->type()  != QPaintEngine::OpenGL2) {
        return;
    }
#endif
    QGLWidget *gl = (QGLWidget *) painter->device();
    if (!d->ctx)
        d->ctx = const_cast<QGLContext *>(gl->context());

    if (d->has_alpha || opacity() < 1.0f) {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
    glBindTexture(GL_TEXTURE_2D, d->custom_tfp ? d->ctextureId : d->textureId);

    // FIXME: not optimal. probably would be better to replace with
    // eglSwapBuffersRegionNOK()
    /*
    if (d->damageRegion.numRects() > 1) {
        d->drawTexture(painter->combinedTransform(), boundingRect(), opacity());
        glEnable(GL_SCISSOR_TEST);
        for (int i = 0; i < d->damageRegion.numRects(); ++i) {
            glScissor(d->damageRegion.rects().at(i).x(),
                      d->brect.height() -
                      (d->damageRegion.rects().at(i).y() +
                       d->damageRegion.rects().at(i).height()),
                      d->damageRegion.rects().at(i).width(),
                      d->damageRegion.rects().at(i).height());
            d->drawTexture(painter->combinedTransform(), boundingRect(), opacity());        }
        glDisable(GL_SCISSOR_TEST);
    } else
    */
    d->drawTexture(painter->combinedTransform(), boundingRect(), opacity());

    // Explicitly disable blending. for some reason, the latest drivers
    // still has blending left-over even if we call glDisable(GL_BLEND)
    glBlendFunc(GL_ONE, GL_ZERO);
    glDisable(GL_BLEND);
}

void MTexturePixmapItem::windowRaised()
{
    d->windowRaised();
}

void MTexturePixmapItem::resize(int w, int h)
{
    d->resize(w, h);
}

QSizeF MTexturePixmapItem::sizeHint(Qt::SizeHint, const QSizeF &) const
{
    return boundingRect().size();
}

QRectF MTexturePixmapItem::boundingRect() const
{
    return d->brect;
}

QPainterPath MTexturePixmapItem::shape() const
{
    QPainterPath path;
    path.addRect(boundingRect());
    return path;
}

bool MTexturePixmapItem::hasAlpha() const
{
    return d->has_alpha;
}

void MTexturePixmapItem::clearTexture()
{
    glBindTexture(GL_TEXTURE_2D, d->custom_tfp ? d->ctextureId : d->textureId);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA,
                 GL_UNSIGNED_BYTE, 0);
}
<|endoftext|>


<commit_before><commit_msg>PVS: V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected.<commit_after><|endoftext|>


<commit_before>#include "atom_type.h"

namespace sirius {

void Atom_type::read_input_core(JSON_tree& parser)
{
    std::string core_str;
    parser["core"] >> core_str;
    if (int size = (int)core_str.size())
    {
        if (size % 2)
        {
            std::string s = std::string("wrong core configuration string : ") + core_str;
            TERMINATE(s);
        }
        int j = 0;
        while (j < size)
        {
            char c1 = core_str[j++];
            char c2 = core_str[j++];

            int n = -1;
            int l = -1;

            std::istringstream iss(std::string(1, c1));
            iss >> n;

            if (n <= 0 || iss.fail())
            {
                std::string s = std::string("wrong principal quantum number : " ) + std::string(1, c1);
                TERMINATE(s);
            }

            switch (c2)
            {
                case 's':
                {
                    l = 0;
                    break;
                }
                case 'p':
                {
                    l = 1;
                    break;
                }
                case 'd':
                {
                    l = 2;
                    break;
                }
                case 'f':
                {
                    l = 3;
                    break;
                }
                default:
                {
                    std::string s = std::string("wrong angular momentum label : " ) + std::string(1, c2);
                    TERMINATE(s);
                }
            }

            atomic_level_descriptor level;
            level.n = n;
            level.l = l;
            level.core = true;
            for (int ist = 0; ist < 28; ist++)
            {
                if ((level.n == atomic_conf[zn_ - 1][ist][0]) && (level.l == atomic_conf[zn_ - 1][ist][1]))
                {
                    level.k = atomic_conf[zn_ - 1][ist][2];
                    level.occupancy = double(atomic_conf[zn_ - 1][ist][3]);
                    atomic_levels_.push_back(level);
                }
            }
        }
    }
}

void Atom_type::read_input_aw(JSON_tree& parser)
{
    radial_solution_descriptor rsd;
    radial_solution_descriptor_set rsd_set;

    // default augmented wave basis
    rsd.n = -1;
    rsd.l = -1;
    for (int order = 0; order < parser["valence"][0]["basis"].size(); order++)
    {
        parser["valence"][0]["basis"][order]["enu"] >> rsd.enu;
        parser["valence"][0]["basis"][order]["dme"] >> rsd.dme;
        parser["valence"][0]["basis"][order]["auto"] >> rsd.auto_enu;
        aw_default_l_.push_back(rsd);
    }

    for (int j = 1; j < parser["valence"].size(); j++)
    {
        parser["valence"][j]["l"] >> rsd.l;
        parser["valence"][j]["n"] >> rsd.n;
        rsd_set.clear();
        for (int order = 0; order < parser["valence"][j]["basis"].size(); order++)
        {
            parser["valence"][j]["basis"][order]["enu"] >> rsd.enu;
            parser["valence"][j]["basis"][order]["dme"] >> rsd.dme;
            parser["valence"][j]["basis"][order]["auto"] >> rsd.auto_enu;
            rsd_set.push_back(rsd);
        }
        aw_specific_l_.push_back(rsd_set);
    }
}

void Atom_type::read_input_lo(JSON_tree& parser)
{
    radial_solution_descriptor rsd;
    radial_solution_descriptor_set rsd_set;

    int l;
    for (int j = 0; j < parser["lo"].size(); j++)
    {
        parser["lo"][j]["l"] >> l;

        if (parser["lo"][j].exist("basis"))
        {
            local_orbital_descriptor lod;
            lod.l = l;
            rsd.l = l;
            rsd_set.clear();
            for (int order = 0; order < parser["lo"][j]["basis"].size(); order++)
            {
                parser["lo"][j]["basis"][order]["n"] >> rsd.n;
                parser["lo"][j]["basis"][order]["enu"] >> rsd.enu;
                parser["lo"][j]["basis"][order]["dme"] >> rsd.dme;
                parser["lo"][j]["basis"][order]["auto"] >> rsd.auto_enu;
                rsd_set.push_back(rsd);
            }
            lod.rsd_set = rsd_set;
            lo_descriptors_.push_back(lod);
        }
    }
}

void Atom_type::read_input(const std::string& fname)
{
    JSON_tree parser(fname);

    if (!parameters_.full_potential())
    {
        parser["pseudo_potential"]["header"]["element"] >> symbol_;

        double zp;
        parser["pseudo_potential"]["header"]["z_valence"] >> zp;
        zn_ = int(zp + 1e-10);

        int nmesh;
        parser["pseudo_potential"]["header"]["mesh_size"] >> nmesh;

        parser["pseudo_potential"]["radial_grid"] >> uspp_.r;

        parser["pseudo_potential"]["local_potential"] >> uspp_.vloc;

        uspp_.core_charge_density = parser["pseudo_potential"]["core_charge_density"].get(std::vector<double>(nmesh, 0));

        parser["pseudo_potential"]["total_charge_density"] >> uspp_.total_charge_density;

        if ((int)uspp_.r.size() != nmesh)
        {
            TERMINATE("wrong mesh size");
        }
        if ((int)uspp_.vloc.size() != nmesh ||
            (int)uspp_.core_charge_density.size() != nmesh ||
            (int)uspp_.total_charge_density.size() != nmesh)
        {
            std::cout << uspp_.vloc.size()  << " " << uspp_.core_charge_density.size() << " " << uspp_.total_charge_density.size() << std::endl;
            TERMINATE("wrong array size");
        }

        num_mt_points_ = nmesh;
        mt_radius_ = uspp_.r[nmesh - 1];

        set_radial_grid(nmesh, &uspp_.r[0]);

        parser["pseudo_potential"]["header"]["number_of_proj"] >> uspp_.num_beta_radial_functions;

        uspp_.beta_radial_functions = mdarray<double, 2>(num_mt_points_, uspp_.num_beta_radial_functions);
        uspp_.beta_radial_functions.zero();

        uspp_.num_beta_radial_points.resize(uspp_.num_beta_radial_functions);
        uspp_.beta_l.resize(uspp_.num_beta_radial_functions);

        int lmax_beta = 0;
        local_orbital_descriptor lod;
        for (int i = 0; i < uspp_.num_beta_radial_functions; i++)
        {
            parser["pseudo_potential"]["beta_projectors"][i]["cutoff_radius_index"] >> uspp_.num_beta_radial_points[i];
            std::vector<double> beta;
            parser["pseudo_potential"]["beta_projectors"][i]["radial_function"] >> beta;
            if ((int)beta.size() != uspp_.num_beta_radial_points[i]) TERMINATE("wrong size of beta function");
            std::memcpy(&uspp_.beta_radial_functions(0, i), &beta[0], beta.size() * sizeof(double));

            parser["pseudo_potential"]["beta_projectors"][i]["angular_momentum"] >> uspp_.beta_l[i];
            lmax_beta = std::max(lmax_beta, uspp_.beta_l[i]);
        }

        uspp_.d_mtrx_ion = mdarray<double, 2>(uspp_.num_beta_radial_functions, uspp_.num_beta_radial_functions);
        uspp_.d_mtrx_ion.zero();
        std::vector<double> dion;
        parser["pseudo_potential"]["D_ion"] >> dion;

        for (int i = 0; i < uspp_.num_beta_radial_functions; i++)
        {
            for (int j = 0; j < uspp_.num_beta_radial_functions; j++)
                uspp_.d_mtrx_ion(i, j) = dion[j * uspp_.num_beta_radial_functions + i];
        }

        if (parser["pseudo_potential"].exist("augmentation"))
        {
            uspp_.q_radial_functions_l = mdarray<double, 3>(num_mt_points_, uspp_.num_beta_radial_functions * (uspp_.num_beta_radial_functions + 1) / 2, 2 * lmax_beta + 1);
            uspp_.q_radial_functions_l.zero();

            for (int k = 0; k < parser["pseudo_potential"]["augmentation"].size(); k++)
            {
                int i, j, l;
                parser["pseudo_potential"]["augmentation"][k]["i"] >> i;
                parser["pseudo_potential"]["augmentation"][k]["j"] >> j;
                int idx = j * (j + 1) / 2 + i;
                parser["pseudo_potential"]["augmentation"][k]["angular_momentum"] >> l;
                std::vector<double> qij;
                parser["pseudo_potential"]["augmentation"][k]["radial_function"] >> qij;
                if ((int)qij.size() != num_mt_points_) TERMINATE("wrong size of qij");

                std::memcpy(&uspp_.q_radial_functions_l(0, idx, l), &qij[0], num_mt_points_ * sizeof(double));
            }
        }

        if (parser["pseudo_potential"].exist("wave_functions"))
        {
            int nwf = parser["pseudo_potential"]["wave_functions"].size();
            uspp_.wf_pseudo_ = mdarray<double, 2>(num_mt_points_, nwf);
            uspp_.l_wf_pseudo_ = std::vector<int>(nwf);
            for (int k = 0; k < nwf; k++)
            {
                std::vector<double> f;
                parser["pseudo_potential"]["wave_functions"][k]["radial_function"] >> f;
                std::memcpy(&uspp_.wf_pseudo_(0, k), &f[0], num_mt_points_ * sizeof(double));

                parser["pseudo_potential"]["wave_functions"][k]["angular_momentum"] >> uspp_.l_wf_pseudo_[k];
            }
        }
    }

    if (parameters_.full_potential())
    {
        parser["name"] >> name_;
        parser["symbol"] >> symbol_;
        parser["mass"] >> mass_;
        parser["number"] >> zn_;
        parser["rmin"] >> radial_grid_origin_;
        parser["rmt"] >> mt_radius_;
        parser["nrmt"] >> num_mt_points_;

        read_input_core(parser);

        read_input_aw(parser);

        read_input_lo(parser);
    }
}

}
<commit_msg>simplify reading of json species file<commit_after>#include "atom_type.h"

namespace sirius {

void Atom_type::read_input_core(JSON_tree& parser)
{
    std::string core_str;
    parser["core"] >> core_str;
    if (int size = (int)core_str.size())
    {
        if (size % 2)
        {
            std::string s = std::string("wrong core configuration string : ") + core_str;
            TERMINATE(s);
        }
        int j = 0;
        while (j < size)
        {
            char c1 = core_str[j++];
            char c2 = core_str[j++];

            int n = -1;
            int l = -1;

            std::istringstream iss(std::string(1, c1));
            iss >> n;

            if (n <= 0 || iss.fail())
            {
                std::string s = std::string("wrong principal quantum number : " ) + std::string(1, c1);
                TERMINATE(s);
            }

            switch (c2)
            {
                case 's':
                {
                    l = 0;
                    break;
                }
                case 'p':
                {
                    l = 1;
                    break;
                }
                case 'd':
                {
                    l = 2;
                    break;
                }
                case 'f':
                {
                    l = 3;
                    break;
                }
                default:
                {
                    std::string s = std::string("wrong angular momentum label : " ) + std::string(1, c2);
                    TERMINATE(s);
                }
            }

            atomic_level_descriptor level;
            level.n = n;
            level.l = l;
            level.core = true;
            for (int ist = 0; ist < 28; ist++)
            {
                if ((level.n == atomic_conf[zn_ - 1][ist][0]) && (level.l == atomic_conf[zn_ - 1][ist][1]))
                {
                    level.k = atomic_conf[zn_ - 1][ist][2];
                    level.occupancy = double(atomic_conf[zn_ - 1][ist][3]);
                    atomic_levels_.push_back(level);
                }
            }
        }
    }
}

void Atom_type::read_input_aw(JSON_tree& parser)
{
    radial_solution_descriptor rsd;
    radial_solution_descriptor_set rsd_set;

    // default augmented wave basis
    rsd.n = -1;
    rsd.l = -1;
    for (int order = 0; order < parser["valence"][0]["basis"].size(); order++)
    {
        parser["valence"][0]["basis"][order]["enu"] >> rsd.enu;
        parser["valence"][0]["basis"][order]["dme"] >> rsd.dme;
        parser["valence"][0]["basis"][order]["auto"] >> rsd.auto_enu;
        aw_default_l_.push_back(rsd);
    }

    for (int j = 1; j < parser["valence"].size(); j++)
    {
        parser["valence"][j]["l"] >> rsd.l;
        parser["valence"][j]["n"] >> rsd.n;
        rsd_set.clear();
        for (int order = 0; order < parser["valence"][j]["basis"].size(); order++)
        {
            parser["valence"][j]["basis"][order]["enu"] >> rsd.enu;
            parser["valence"][j]["basis"][order]["dme"] >> rsd.dme;
            parser["valence"][j]["basis"][order]["auto"] >> rsd.auto_enu;
            rsd_set.push_back(rsd);
        }
        aw_specific_l_.push_back(rsd_set);
    }
}

void Atom_type::read_input_lo(JSON_tree& parser)
{
    radial_solution_descriptor rsd;
    radial_solution_descriptor_set rsd_set;

    int l;
    for (int j = 0; j < parser["lo"].size(); j++)
    {
        parser["lo"][j]["l"] >> l;

        if (parser["lo"][j].exist("basis"))
        {
            local_orbital_descriptor lod;
            lod.l = l;
            rsd.l = l;
            rsd_set.clear();
            for (int order = 0; order < parser["lo"][j]["basis"].size(); order++)
            {
                parser["lo"][j]["basis"][order]["n"] >> rsd.n;
                parser["lo"][j]["basis"][order]["enu"] >> rsd.enu;
                parser["lo"][j]["basis"][order]["dme"] >> rsd.dme;
                parser["lo"][j]["basis"][order]["auto"] >> rsd.auto_enu;
                rsd_set.push_back(rsd);
            }
            lod.rsd_set = rsd_set;
            lo_descriptors_.push_back(lod);
        }
    }
}

void Atom_type::read_input(const std::string& fname)
{
    JSON_tree parser(fname);

    if (!parameters_.full_potential())
    {
        parser["pseudo_potential"]["header"]["element"] >> symbol_;

        double zp;
        parser["pseudo_potential"]["header"]["z_valence"] >> zp;
        zn_ = int(zp + 1e-10);

        int nmesh;
        parser["pseudo_potential"]["header"]["mesh_size"] >> nmesh;

        parser["pseudo_potential"]["radial_grid"] >> uspp_.r;

        parser["pseudo_potential"]["local_potential"] >> uspp_.vloc;

        uspp_.core_charge_density = parser["pseudo_potential"]["core_charge_density"].get(std::vector<double>(nmesh, 0));

        parser["pseudo_potential"]["total_charge_density"] >> uspp_.total_charge_density;

        if ((int)uspp_.r.size() != nmesh)
        {
            TERMINATE("wrong mesh size");
        }
        if ((int)uspp_.vloc.size() != nmesh ||
            (int)uspp_.core_charge_density.size() != nmesh ||
            (int)uspp_.total_charge_density.size() != nmesh)
        {
            std::cout << uspp_.vloc.size()  << " " << uspp_.core_charge_density.size() << " " << uspp_.total_charge_density.size() << std::endl;
            TERMINATE("wrong array size");
        }

        num_mt_points_ = nmesh;
        mt_radius_ = uspp_.r[nmesh - 1];

        set_radial_grid(nmesh, &uspp_.r[0]);

        parser["pseudo_potential"]["header"]["number_of_proj"] >> uspp_.num_beta_radial_functions;

        uspp_.beta_radial_functions = mdarray<double, 2>(num_mt_points_, uspp_.num_beta_radial_functions);
        uspp_.beta_radial_functions.zero();

        uspp_.num_beta_radial_points.resize(uspp_.num_beta_radial_functions);
        uspp_.beta_l.resize(uspp_.num_beta_radial_functions);

        int lmax_beta = 0;
        local_orbital_descriptor lod;
        for (int i = 0; i < uspp_.num_beta_radial_functions; i++)
        {
            std::vector<double> beta;
            parser["pseudo_potential"]["beta_projectors"][i]["radial_function"] >> beta;
            if ((int)beta.size() > num_mt_points_)
            {
                std::stringstream s;
                s << "wrong size of beta functions for atom type " << symbol_ << " (label: " << label_ << ")" << std::endl
                  << "size of beta radial functions in the file: " << beta.size() << std::endl
                  << "radial grid size: " << num_mt_points_;
                TERMINATE(s);
            }
            uspp_.num_beta_radial_points[i] = static_cast<int>(beta.size());
            std::memcpy(&uspp_.beta_radial_functions(0, i), &beta[0], uspp_.num_beta_radial_points[i] * sizeof(double));

            parser["pseudo_potential"]["beta_projectors"][i]["angular_momentum"] >> uspp_.beta_l[i];
            lmax_beta = std::max(lmax_beta, uspp_.beta_l[i]);
        }

        uspp_.d_mtrx_ion = mdarray<double, 2>(uspp_.num_beta_radial_functions, uspp_.num_beta_radial_functions);
        uspp_.d_mtrx_ion.zero();
        std::vector<double> dion;
        parser["pseudo_potential"]["D_ion"] >> dion;

        for (int i = 0; i < uspp_.num_beta_radial_functions; i++)
        {
            for (int j = 0; j < uspp_.num_beta_radial_functions; j++)
                uspp_.d_mtrx_ion(i, j) = dion[j * uspp_.num_beta_radial_functions + i];
        }

        if (parser["pseudo_potential"].exist("augmentation"))
        {
            uspp_.q_radial_functions_l = mdarray<double, 3>(num_mt_points_, uspp_.num_beta_radial_functions * (uspp_.num_beta_radial_functions + 1) / 2, 2 * lmax_beta + 1);
            uspp_.q_radial_functions_l.zero();

            for (int k = 0; k < parser["pseudo_potential"]["augmentation"].size(); k++)
            {
                int i, j, l;
                parser["pseudo_potential"]["augmentation"][k]["i"] >> i;
                parser["pseudo_potential"]["augmentation"][k]["j"] >> j;
                int idx = j * (j + 1) / 2 + i;
                parser["pseudo_potential"]["augmentation"][k]["angular_momentum"] >> l;
                std::vector<double> qij;
                parser["pseudo_potential"]["augmentation"][k]["radial_function"] >> qij;
                if ((int)qij.size() != num_mt_points_) TERMINATE("wrong size of qij");

                std::memcpy(&uspp_.q_radial_functions_l(0, idx, l), &qij[0], num_mt_points_ * sizeof(double));
            }
        }

        if (parser["pseudo_potential"].exist("atomic_wave_functions"))
        {
            int nwf = parser["pseudo_potential"]["atomic_wave_functions"].size();
            for (int k = 0; k < nwf; k++)
            {
                std::pair<int, std::vector<double> > wf;
                parser["pseudo_potential"]["atomic_wave_functions"][k]["radial_function"] >> wf.second;

                if ((int)wf.second.size() != num_mt_points_)
                {
                    std::stringstream s;
                    s << "wrong size of atomic functions for atom type " << symbol_ << " (label: " << label_ << ")" << std::endl
                      << "size of atomic radial functions in the file: " << wf.second.size() << std::endl
                      << "radial grid size: " << num_mt_points_;
                    TERMINATE(s);
                }
                parser["pseudo_potential"]["atomic_wave_functions"][k]["angular_momentum"] >> wf.first;
                uspp_.atomic_pseudo_wfs_.push_back(wf);
            }
        }
    }

    if (parameters_.full_potential())
    {
        parser["name"] >> name_;
        parser["symbol"] >> symbol_;
        parser["mass"] >> mass_;
        parser["number"] >> zn_;
        parser["rmin"] >> radial_grid_origin_;
        parser["rmt"] >> mt_radius_;
        parser["nrmt"] >> num_mt_points_;

        read_input_core(parser);

        read_input_aw(parser);

        read_input_lo(parser);
    }
}

}
<|endoftext|>


<commit_before>#include <cstring>
#ifdef __clang__
#include <vector>
#endif

#if defined _OPENMP
#include <omp.h>
#else
#define omp_get_thread_num()  0
#endif

#ifdef _MSC_VER
#pragma warning(disable:4127)
#endif
#define __DYN_GRID_SIZE
#include "common.h"
#include "metrix.h"
#include "aligned_malloc.h"

#define as256p(p) (reinterpret_cast<__m256d*>(p))
#define as256pc(p) (reinterpret_cast<const __m256d*>(p))

#ifndef __DEGRID
#define GRID_MOD
#define VIS_MOD const
inline
void addGrids(
    complexd dst[]
  , const complexd srcs[]
  , int nthreads
  , int grid_pitch
  , int grid_size
  )
{
  int siz = grid_size*grid_pitch;
#pragma omp parallel for
  for (unsigned int i = 0; i < siz*sizeof(complexd)/(256/8); i++) {
    __m256d sum = as256pc(srcs)[i];
    // __m256d sum = _mm256_loadu_pd(reinterpret_cast<const double*>(as256pc(srcs)+i));

    for (int g = 1; g < nthreads; g ++)
      sum = _mm256_add_pd(sum, as256pc(srcs + g * siz)[i]);

    as256p(dst)[i] = sum;
  }
}
#else
#define GRID_MOD const
#define VIS_MOD
#endif

template <
    int over
  , bool is_half_gcf
  >
// grid must be initialized to 0s.
void gridKernel_scatter(
    double scale
  , double wstep
  , int baselines
  , const int gcf_supps[/* baselines */]
  , GRID_MOD complexd grids[]
    // We have a [w_planes][over][over]-shaped array of pointers to
    // variable-sized gcf layers, but we precompute (in pregrid)
    // exact index into this array, thus we use plain pointer here
  , const complexd * gcf[]
  , const Double3 * _uvw[]
  , VIS_MOD complexd * _vis[]
  , int ts_ch
  , int grid_pitch
  , int grid_size
  ) {
  int siz = grid_size*grid_pitch;
#pragma omp parallel
  {
    GRID_MOD complexd * _grid = grids + omp_get_thread_num() * siz;
#ifndef __DEGRID
    memset(_grid, 0, sizeof(complexd) * siz);
#else
    memset(_vis, 0, sizeof(complexd) * baselines * ts_ch);
#endif
    __ACC(complexd, grid, grid_pitch);

#pragma omp for schedule(dynamic)
    for(int bl = 0; bl < baselines; bl++) {
      int max_supp_here;
      max_supp_here = gcf_supps[bl];

      const Double3 * uvw;
      uvw = _uvw[bl];

      // VLA requires "--std=gnu..." extension
      Pregridded pa[ts_ch];
#ifndef __DEGRID
      // Clang doesn't allow non-POD types in VLAs,
      //  thus we use much more heavyweight vector here.
      #ifndef __clang__
      complexd vis[ts_ch];
      #else
      std::vector<complexd> vis(ts_ch);
      #endif
#endif
      for(int n=0; n<ts_ch; n++){
#ifndef __DEGRID
        vis[n] = rotw(_vis[bl][n], uvw[n].w);
#endif
        pregridPoint<over, is_half_gcf>(scale, wstep, uvw[n], pa[n], grid_size);
      }
#ifdef __DEGRID
      complexd * vis;
      vis = _vis[bl];
#endif
      for (int su = 0; su < max_supp_here; su++) { // Moved from 2-levels below according to Romein
        for (int i = 0; i < ts_ch; i++) {
          Pregridded p = pa[i];
          for (int sv = 0; sv < max_supp_here; sv++) {
            // Don't forget our u v are already translated by -max_supp_here/2
            int gsu, gsv;
            gsu = p.u + su;
            gsv = p.v + sv;

            complexd supportPixel;
            #define __layeroff su * max_supp_here + sv
            if (is_half_gcf) {
              int index;
              index = p.gcf_layer_index;
              // Negative index indicates that original w was mirrored
              // and we shall negate the index to obtain correct
              // offset *and* conjugate the result.
              if (index < 0) {
                supportPixel = conj(gcf[-index][__layeroff]);
              } else {
                supportPixel = gcf[index][__layeroff];
              }
            } else {
                supportPixel = gcf[p.gcf_layer_index][__layeroff];
            }
#ifndef __DEGRID
            grid[gsu][gsv] += vis[i] * supportPixel;
#else
            vis[i] += rotw(grid[gsu][gsv] * supportPixel, uvw[i].w);
#endif
          }
        }
      }
    }
  }
}

#ifndef __DEGRID
template <
    int over
  , bool is_half_gcf
  >
// grid must be initialized to 0s.
void gridKernel_scatter_full(
    double scale
  , double wstep
  , int baselines
  , const int gcf_supps[/* baselines */]
  , complexd grid[]
    // We have a [w_planes][over][over]-shaped array of pointers to
    // variable-sized gcf layers, but we precompute (in pregrid)
    // exact index into this array, thus we use plain pointer here
  , const complexd * gcf[]
  , const Double3 * uvw[]
  , const complexd * vis[]
  , int ts_ch
  , int grid_pitch
  , int grid_size
  ) {
#if defined _OPENMP
  int siz = grid_size*grid_pitch;
  int nthreads;

#pragma omp parallel
#pragma omp single
  nthreads = omp_get_num_threads();

  // Nullify incoming grid, allocate thread-local grids
  memset(grid, 0, sizeof(complexd) * siz);
  complexd * tmpgrids = alignedMallocArray<complexd>(siz * nthreads, 32);

  gridKernel_scatter<
      over
    , is_half_gcf
    >(scale, wstep, baselines, gcf_supps, tmpgrids, gcf, uvw, vis, ts_ch, grid_pitch, grid_size);
  addGrids(grid, tmpgrids, nthreads, grid_pitch, grid_size);
  free(tmpgrids);
#else
  gridKernel_scatter<
      over
    , is_half_gcf
    >(scale, wstep, baselines, gcf_supps, grid, gcf, uvw, vis, ts_ch, grid_pitch, grid_size);
#endif
}

#define gridKernelCPU(hgcfSuff, isHgcf)                   \
extern "C"                                                \
void gridKernelCPU##hgcfSuff(                             \
    double scale                                          \
  , double wstep                                          \
  , int baselines                                         \
  , const int gcf_supps[/* baselines */]                  \
  , complexd grid[]                                       \
  , const complexd * gcf[]                                \
  , const Double3 * uvw[]                                 \
  , const complexd * vis[]                                \
  , int ts_ch                                             \
  , int grid_pitch                                        \
  , int grid_size                                         \
  ){                                                      \
  gridKernel_scatter_full<OVER, isHgcf>                   \
    ( scale, wstep, baselines, gcf_supps                  \
    , grid, gcf, uvw, vis, ts_ch, grid_pitch, grid_size); \
}

gridKernelCPU(HalfGCF, true)
gridKernelCPU(FullGCF, false)

// Normalization is done inplace!
extern "C"
void normalize(
    int n
  , complexd src[]
  , int grid_pitch
  , int grid_size
  )
{
  int siz = grid_size*grid_pitch;
  double norm = 1.0/double(siz);
#pragma omp parallel for
  for (int i = 0; i < siz; i++) {
    src[i] *= norm;
  }
}

#else

#define deGridKernelCPU(hgcfSuff, isHgcf)                 \
extern "C"                                                \
void deGridKernelCPU##hgcfSuff(                           \
    double scale                                          \
  , double wstep                                          \
  , int baselines                                         \
  , const int gcf_supps[/* baselines */]                  \
  , const complexd grid[]                                 \
  , const complexd * gcf[]                                \
  , const Double3 * uvw[]                                 \
  , complexd * vis[]                                      \
  , int ts_ch                                             \
  , int grid_pitch                                        \
  , int grid_size                                         \
  ){                                                      \
  gridKernel_scatter<OVER, isHgcf>                        \
    ( scale, wstep, baselines, gcf_supps                  \
    , grid, gcf, uvw, vis, ts_ch, grid_pitch, grid_size); \
}

deGridKernelCPU(HalfGCF, true)
deGridKernelCPU(FullGCF, false)

#endif
<commit_msg>Fix degridder's vis data zeroing bug.<commit_after>#include <cstring>
#ifdef __clang__
#include <vector>
#endif

#if defined _OPENMP
#include <omp.h>
#else
#define omp_get_thread_num()  0
#endif

#ifdef _MSC_VER
#pragma warning(disable:4127)
#endif
#define __DYN_GRID_SIZE
#include "common.h"
#include "metrix.h"
#include "aligned_malloc.h"

#define as256p(p) (reinterpret_cast<__m256d*>(p))
#define as256pc(p) (reinterpret_cast<const __m256d*>(p))

#ifndef __DEGRID
#define GRID_MOD
#define VIS_MOD const
inline
void addGrids(
    complexd dst[]
  , const complexd srcs[]
  , int nthreads
  , int grid_pitch
  , int grid_size
  )
{
  int siz = grid_size*grid_pitch;
#pragma omp parallel for
  for (unsigned int i = 0; i < siz*sizeof(complexd)/(256/8); i++) {
    __m256d sum = as256pc(srcs)[i];
    // __m256d sum = _mm256_loadu_pd(reinterpret_cast<const double*>(as256pc(srcs)+i));

    for (int g = 1; g < nthreads; g ++)
      sum = _mm256_add_pd(sum, as256pc(srcs + g * siz)[i]);

    as256p(dst)[i] = sum;
  }
}
#else
#define GRID_MOD const
#define VIS_MOD
#endif

template <
    int over
  , bool is_half_gcf
  >
// grid must be initialized to 0s.
void gridKernel_scatter(
    double scale
  , double wstep
  , int baselines
  , const int gcf_supps[/* baselines */]
  , GRID_MOD complexd grids[]
    // We have a [w_planes][over][over]-shaped array of pointers to
    // variable-sized gcf layers, but we precompute (in pregrid)
    // exact index into this array, thus we use plain pointer here
  , const complexd * gcf[]
  , const Double3 * _uvw[]
  , VIS_MOD complexd * _vis[]
  , int ts_ch
  , int grid_pitch
  , int grid_size
  ) {
  int siz = grid_size*grid_pitch;
#pragma omp parallel
  {
    GRID_MOD complexd * _grid = grids + omp_get_thread_num() * siz;
#ifndef __DEGRID
    memset(_grid, 0, sizeof(complexd) * siz);
#endif
    __ACC(complexd, grid, grid_pitch);

#pragma omp for schedule(dynamic)
    for(int bl = 0; bl < baselines; bl++) {
      int max_supp_here;
      max_supp_here = gcf_supps[bl];

      const Double3 * uvw;
      uvw = _uvw[bl];

      // VLA requires "--std=gnu..." extension
      Pregridded pa[ts_ch];
#ifndef __DEGRID
      // Clang doesn't allow non-POD types in VLAs,
      //  thus we use much more heavyweight vector here.
      #ifndef __clang__
      complexd vis[ts_ch];
      #else
      std::vector<complexd> vis(ts_ch);
      #endif
#else
      complexd * vis;
      vis = _vis[bl];
#endif
      for(int n=0; n<ts_ch; n++){
#ifndef __DEGRID
        vis[n] = rotw(_vis[bl][n], uvw[n].w);
#else
        vis[n] = {0.0, 0.0};
#endif
        pregridPoint<over, is_half_gcf>(scale, wstep, uvw[n], pa[n], grid_size);
      }
      for (int su = 0; su < max_supp_here; su++) { // Moved from 2-levels below according to Romein
        for (int i = 0; i < ts_ch; i++) {
          Pregridded p = pa[i];
          for (int sv = 0; sv < max_supp_here; sv++) {
            // Don't forget our u v are already translated by -max_supp_here/2
            int gsu, gsv;
            gsu = p.u + su;
            gsv = p.v + sv;

            complexd supportPixel;
            #define __layeroff su * max_supp_here + sv
            if (is_half_gcf) {
              int index;
              index = p.gcf_layer_index;
              // Negative index indicates that original w was mirrored
              // and we shall negate the index to obtain correct
              // offset *and* conjugate the result.
              if (index < 0) {
                supportPixel = conj(gcf[-index][__layeroff]);
              } else {
                supportPixel = gcf[index][__layeroff];
              }
            } else {
                supportPixel = gcf[p.gcf_layer_index][__layeroff];
            }
#ifndef __DEGRID
            grid[gsu][gsv] += vis[i] * supportPixel;
#else
            vis[i] += rotw(grid[gsu][gsv] * supportPixel, uvw[i].w);
#endif
          }
        }
      }
    }
  }
}

#ifndef __DEGRID
template <
    int over
  , bool is_half_gcf
  >
// grid must be initialized to 0s.
void gridKernel_scatter_full(
    double scale
  , double wstep
  , int baselines
  , const int gcf_supps[/* baselines */]
  , complexd grid[]
    // We have a [w_planes][over][over]-shaped array of pointers to
    // variable-sized gcf layers, but we precompute (in pregrid)
    // exact index into this array, thus we use plain pointer here
  , const complexd * gcf[]
  , const Double3 * uvw[]
  , const complexd * vis[]
  , int ts_ch
  , int grid_pitch
  , int grid_size
  ) {
#if defined _OPENMP
  int siz = grid_size*grid_pitch;
  int nthreads;

#pragma omp parallel
#pragma omp single
  nthreads = omp_get_num_threads();

  // Nullify incoming grid, allocate thread-local grids
  memset(grid, 0, sizeof(complexd) * siz);
  complexd * tmpgrids = alignedMallocArray<complexd>(siz * nthreads, 32);

  gridKernel_scatter<
      over
    , is_half_gcf
    >(scale, wstep, baselines, gcf_supps, tmpgrids, gcf, uvw, vis, ts_ch, grid_pitch, grid_size);
  addGrids(grid, tmpgrids, nthreads, grid_pitch, grid_size);
  free(tmpgrids);
#else
  gridKernel_scatter<
      over
    , is_half_gcf
    >(scale, wstep, baselines, gcf_supps, grid, gcf, uvw, vis, ts_ch, grid_pitch, grid_size);
#endif
}

#define gridKernelCPU(hgcfSuff, isHgcf)                   \
extern "C"                                                \
void gridKernelCPU##hgcfSuff(                             \
    double scale                                          \
  , double wstep                                          \
  , int baselines                                         \
  , const int gcf_supps[/* baselines */]                  \
  , complexd grid[]                                       \
  , const complexd * gcf[]                                \
  , const Double3 * uvw[]                                 \
  , const complexd * vis[]                                \
  , int ts_ch                                             \
  , int grid_pitch                                        \
  , int grid_size                                         \
  ){                                                      \
  gridKernel_scatter_full<OVER, isHgcf>                   \
    ( scale, wstep, baselines, gcf_supps                  \
    , grid, gcf, uvw, vis, ts_ch, grid_pitch, grid_size); \
}

gridKernelCPU(HalfGCF, true)
gridKernelCPU(FullGCF, false)

// Normalization is done inplace!
extern "C"
void normalize(
    int n
  , complexd src[]
  , int grid_pitch
  , int grid_size
  )
{
  int siz = grid_size*grid_pitch;
  double norm = 1.0/double(siz);
#pragma omp parallel for
  for (int i = 0; i < siz; i++) {
    src[i] *= norm;
  }
}

#else

#define deGridKernelCPU(hgcfSuff, isHgcf)                 \
extern "C"                                                \
void deGridKernelCPU##hgcfSuff(                           \
    double scale                                          \
  , double wstep                                          \
  , int baselines                                         \
  , const int gcf_supps[/* baselines */]                  \
  , const complexd grid[]                                 \
  , const complexd * gcf[]                                \
  , const Double3 * uvw[]                                 \
  , complexd * vis[]                                      \
  , int ts_ch                                             \
  , int grid_pitch                                        \
  , int grid_size                                         \
  ){                                                      \
  gridKernel_scatter<OVER, isHgcf>                        \
    ( scale, wstep, baselines, gcf_supps                  \
    , grid, gcf, uvw, vis, ts_ch, grid_pitch, grid_size); \
}

deGridKernelCPU(HalfGCF, true)
deGridKernelCPU(FullGCF, false)

#endif
<|endoftext|>


<commit_before>/***************************************************************************
 *                                                                         *
 * Copyright (C) 2007-2015 by frePPLe bvba                                 *
 *                                                                         *
 * This library is free software; you can redistribute it and/or modify it *
 * under the terms of the GNU Affero General Public License as published   *
 * by the Free Software Foundation; either version 3 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 Affero General Public License for more details.                     *
 *                                                                         *
 * You should have received a copy of the GNU Affero General Public        *
 * License along with this program.                                        *
 * If not, see <http://www.gnu.org/licenses/>.                             *
 *                                                                         *
 ***************************************************************************/

#define FREPPLE_CORE
#include "frepple/solver.h"
namespace frepple
{

DECLARE_EXPORT const MetaClass* SolverMRP::metadata;
const Keyword SolverMRP::tag_iterationthreshold("iterationthreshold");
const Keyword SolverMRP::tag_iterationaccuracy("iterationaccuracy");
const Keyword SolverMRP::tag_lazydelay("lazydelay");
const Keyword SolverMRP::tag_allowsplits("allowsplits");
const Keyword SolverMRP::tag_rotateresources("rotateresources");
const Keyword SolverMRP::tag_planSafetyStockFirst("plansafetystockfirst");
const Keyword SolverMRP::tag_iterationmax("iterationmax");

void LibrarySolver::initialize()
{
  // Initialize only once
  static bool init = false;
  if (init)
  {
    logger << "Warning: Calling frepple::LibrarySolver::initialize() more "
        << "than once." << endl;
    return;
  }
  init = true;

  // Register all classes.
  int nok = 0;
  nok += SolverMRP::initialize();
  nok += OperatorDelete::initialize();
  if (nok) throw RuntimeException("Error registering new Python types");
}

int SolverMRP::initialize()
{
  // Initialize the metadata
  metadata = MetaClass::registerClass<SolverMRP>(
    "solver", "solver_mrp", Object::create<SolverMRP>, true
    );
  registerFields<SolverMRP>(const_cast<MetaClass*>(metadata));

  // Initialize the Python class
  PythonType& x = FreppleClass<SolverMRP, Solver>::getPythonType();
  x.setName("solver_mrp");
  x.setDoc("frePPLe solver_mrp");
  x.supportgetattro();
  x.supportsetattro();
  x.supportcreate(create);
  x.addMethod("solve", solve, METH_NOARGS, "run the solver");
  x.addMethod("commit", commit, METH_NOARGS, "commit the plan changes");
  x.addMethod("rollback", rollback, METH_NOARGS, "rollback the plan changes");
  const_cast<MetaClass*>(metadata)->pythonClass = x.type_object();
  return x.typeReady();
}

PyObject* SolverMRP::create(PyTypeObject* pytype, PyObject* args, PyObject* kwds)
{
  try
  {
    // Create the solver
    SolverMRP *s = new SolverMRP();

    // Iterate over extra keywords, and set attributes.   @todo move this responsibility to the readers...
    if (kwds)
    {
      PyObject *key, *value;
      Py_ssize_t pos = 0;
      while (PyDict_Next(kwds, &pos, &key, &value))
      {
        PythonData field(value);
        PyObject* key_utf8 = PyUnicode_AsUTF8String(key);
        DataKeyword attr(PyBytes_AsString(key_utf8));
        Py_DECREF(key_utf8);
        const MetaFieldBase* fmeta = SolverMRP::metadata->findField(attr.getHash());
        if (!fmeta)
          fmeta = Solver::metadata->findField(attr.getHash());
        if (fmeta)
          // Update the attribute
          fmeta->setField(s, field);
        else
          s->setProperty(attr.getName(), value);
      };
    }

    // Return the object. The reference count doesn't need to be increased
    // as we do with other objects, because we want this object to be available
    // for the garbage collector of Python.
    return static_cast<PyObject*>(s);
  }
  catch (...)
  {
    PythonType::evalException();
    return NULL;
  }
}

DECLARE_EXPORT bool SolverMRP::demand_comparison(const Demand* l1, const Demand* l2)
{
  if (l1->getPriority() != l2->getPriority())
    return l1->getPriority() < l2->getPriority();
  else if (l1->getDue() != l2->getDue())
    return l1->getDue() < l2->getDue();
  else
    return l1->getQuantity() < l2->getQuantity();
}

DECLARE_EXPORT void SolverMRP::SolverMRPdata::commit()
{
  // Check
  SolverMRP* solver = getSolver();
  if (!demands || !solver)
    throw LogicException("Missing demands or solver.");

  // Message
  if (solver->getLogLevel()>0)
    logger << "Start solving cluster " << cluster << " at " << Date::now() << endl;

  // Solve the planning problem
  try
  {
    // TODO Propagate & solve initial shortages and overloads

    // Sort the demands of this problem.
    // We use a stable sort to get reproducible results between platforms
    // and STL implementations.
    stable_sort(demands->begin(), demands->end(), demand_comparison);

    // Solve for safety stock in buffers.
    if (solver->getPlanSafetyStockFirst())
    {
      constrainedPlanning = (solver->getPlanType() == 1);
      solveSafetyStock(solver);
    }

    // Loop through the list of all demands in this planning problem
    safety_stock_planning = false;
    constrainedPlanning = (solver->getPlanType() == 1);
    for (deque<Demand*>::const_iterator i = demands->begin();
        i != demands->end(); ++i)
    {
      iteration_count = 0;
      try
      {
        // Plan the demand
        (*i)->solve(*solver, this);
      }
      catch (...)
      {
        // Error message
        logger << "Error: Caught an exception while solving demand '"
            << (*i)->getName() << "':" << endl;
        try {throw;}
        catch (const bad_exception&) {logger << "  bad exception" << endl;}
        catch (const exception& e) {logger << "  " << e.what() << endl;}
        catch (...) {logger << "  Unknown type" << endl;}
      }
    }

    // Clean the list of demands of this cluster
    demands->clear();

    // Completely recreate all purchasing operation plans
    for (set<const OperationItemSupplier*>::iterator o = purchase_operations.begin();
      o != purchase_operations.end(); ++o
      )
    {
      // Erase existing proposed purchases
      const_cast<OperationItemSupplier*>(*o)->deleteOperationPlans(false);
      // Create new proposed purchases, unless they can be recreated when
      // we solve for the safety stock a few lines below
      if (solver->getPlanSafetyStockFirst())
      {
        try
        {
          (*o)->getBuffer()->solve(*solver, this);
          CommandManager::commit();
        }
        catch(...)
        {
          CommandManager::rollback();
        }
      }
    }
    purchase_operations.clear();

    // Solve for safety stock in buffers.
    if (!solver->getPlanSafetyStockFirst()) solveSafetyStock(solver);
  }
  catch (...)
  {
    // We come in this exception handling code only if there is a problem with
    // with this cluster that goes beyond problems with single orders.
    // If the problem is with single orders, the exception handling code above
    // will do a proper rollback.

    // Error message
    logger << "Error: Caught an exception while solving cluster "
        << cluster << ":" << endl;
    try {throw;}
    catch (const bad_exception&) {logger << "  bad exception" << endl;}
    catch (const exception& e) {logger << "  " << e.what() << endl;}
    catch (...) {logger << "  Unknown type" << endl;}

    // Clean up the operationplans of this cluster
    for (Operation::iterator f=Operation::begin(); f!=Operation::end(); ++f)
      if (f->getCluster() == cluster)
        f->deleteOperationPlans();

    // Clean the list of demands of this cluster
    demands->clear();
  }

  // Message
  if (solver->getLogLevel()>0)
    logger << "End solving cluster " << cluster << " at " << Date::now() << endl;
}

void SolverMRP::SolverMRPdata::solveSafetyStock(SolverMRP* solver)
{
  OperatorDelete cleanup(this);
  safety_stock_planning = true;
  if (getLogLevel() > 0)
    logger << "Start safety stock replenishment pass   " << solver->getConstraints() << endl;
  vector< list<Buffer*> > bufs(HasLevel::getNumberOfLevels() + 1);
  for (Buffer::iterator buf = Buffer::begin(); buf != Buffer::end(); ++buf)
    if (buf->getCluster() == cluster
      && ( buf->getMinimum() || buf->getMinimumCalendar()
        || buf->getType() == *BufferProcure::metadata )
      )
      bufs[(buf->getLevel()>=0) ? buf->getLevel() : 0].push_back(&*buf);
  for (vector< list<Buffer*> >::iterator b_list = bufs.begin(); b_list != bufs.end(); ++b_list)
    for (list<Buffer*>::iterator b = b_list->begin(); b != b_list->end(); ++b)
      try
      {
        state->curBuffer = NULL;
        // A quantity of -1 is a flag for the buffer solver to solve safety stock.
        state->q_qty = -1.0;
        state->q_date = Date::infinitePast;
        state->a_cost = 0.0;
        state->a_penalty = 0.0;
        planningDemand = NULL;
        state->curDemand = NULL;
        state->curOwnerOpplan = NULL;
        // Call the buffer solver
        iteration_count = 0;
        (*b)->solve(*solver, this);
        // Check for excess
        if ((*b)->getType() != *BufferProcure::metadata)
          (*b)->solve(cleanup, this);
        CommandManager::commit();
      }
      catch(...)
      {
        CommandManager::rollback();
      }

  if (getLogLevel() > 0)
    logger << "Finished safety stock replenishment pass" << endl;
  safety_stock_planning = false;
}

DECLARE_EXPORT void SolverMRP::update_user_exits()
{
  setUserExitBuffer(getPyObjectProperty(Tags::userexit_buffer.getName()));
  setUserExitDemand(getPyObjectProperty(Tags::userexit_demand.getName()));
  setUserExitFlow(getPyObjectProperty(Tags::userexit_flow.getName()));
  setUserExitOperation(getPyObjectProperty(Tags::userexit_operation.getName()));
  setUserExitResource(getPyObjectProperty(Tags::userexit_resource.getName()));
}

DECLARE_EXPORT void SolverMRP::solve(void *v)
{
  // Configure user exits
  update_user_exits();

  // Count how many clusters we have to plan
  int cl = HasLevel::getNumberOfClusters() + 1;

  // Categorize all demands in their cluster
  demands_per_cluster.resize(cl);
  for (Demand::iterator i = Demand::begin(); i != Demand::end(); ++i)
    demands_per_cluster[i->getCluster()].push_back(&*i);

  // Delete of operationplans of the affected clusters
  // This deletion is not multi-threaded... But on the other hand we need to
  // loop through the operations only once (rather than as many times as there
  // are clusters)
  if (getErasePreviousFirst())
  {
    if (getLogLevel()>0) logger << "Deleting previous plan" << endl;
    for (Operation::iterator e=Operation::begin(); e!=Operation::end(); ++e)
      e->deleteOperationPlans();
  }

  // Solve in parallel threads.
  // When not solving in silent and autocommit mode, we only use a single
  // solver thread.
  // Otherwise we use as many worker threads as processor cores.
  ThreadGroup threads;
  if (getLogLevel()>0 || !getAutocommit())
    threads.setMaxParallel(1);

  // Register all clusters to be solved
  for (int j = 0; j < cl; ++j)
    threads.add(
      SolverMRPdata::runme,
      new SolverMRPdata(this, j, &(demands_per_cluster[j]))
      );

  // Run the planning command threads and wait for them to exit
  threads.execute();

  // @todo Check the resource setups that were broken - needs to be removed
  for (Resource::iterator gres = Resource::begin(); gres != Resource::end(); ++gres)
    if (gres->getSetupMatrix()) gres->updateSetups();
}

DECLARE_EXPORT PyObject* SolverMRP::solve(PyObject *self, PyObject *args)
{
  // Parse the argument
  PyObject *dem = NULL;
  if (args && !PyArg_ParseTuple(args, "|O:solve", &dem)) return NULL;
  if (dem && !PyObject_TypeCheck(dem, Demand::metadata->pythonClass))
  {
    PyErr_SetString(PythonDataException, "solve(d) argument must be a demand");
    return NULL;
  }

  Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
  try
  {
    SolverMRP* sol = static_cast<SolverMRP*>(self);
    if (!dem)
    {
      // Complete replan
      sol->setAutocommit(true);
      sol->solve();
    }
    else
    {
      // Incrementally plan a single demand
      sol->setAutocommit(false);
      sol->update_user_exits();
      static_cast<Demand*>(dem)->solve(*sol, &(sol->getCommands()));
    }
  }
  catch(...)
  {
    Py_BLOCK_THREADS;
    PythonType::evalException();
    return NULL;
  }
  Py_END_ALLOW_THREADS   // Reclaim Python interpreter
  return Py_BuildValue("");
}

DECLARE_EXPORT PyObject* SolverMRP::commit(PyObject *self, PyObject *args)
{
  Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
  try
  {
    SolverMRP * me = static_cast<SolverMRP*>(self);
    me->scanExcess(&(me->commands));
    me->commands.CommandManager::commit();
  }
  catch(...)
  {
    Py_BLOCK_THREADS;
    PythonType::evalException();
    return NULL;
  }
  Py_END_ALLOW_THREADS   // Reclaim Python interpreter
  return Py_BuildValue("");
}

DECLARE_EXPORT PyObject* SolverMRP::rollback(PyObject *self, PyObject *args)
{
  Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
  try
  {
    static_cast<SolverMRP*>(self)->commands.rollback();
  }
  catch(...)
  {
    Py_BLOCK_THREADS;
    PythonType::evalException();
    return NULL;
  }
  Py_END_ALLOW_THREADS   // Reclaim Python interpreter
  return Py_BuildValue("");
}

} // end namespace
<commit_msg>revised cleanup sweep for PO replenishments<commit_after>/***************************************************************************
 *                                                                         *
 * Copyright (C) 2007-2015 by frePPLe bvba                                 *
 *                                                                         *
 * This library is free software; you can redistribute it and/or modify it *
 * under the terms of the GNU Affero General Public License as published   *
 * by the Free Software Foundation; either version 3 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 Affero General Public License for more details.                     *
 *                                                                         *
 * You should have received a copy of the GNU Affero General Public        *
 * License along with this program.                                        *
 * If not, see <http://www.gnu.org/licenses/>.                             *
 *                                                                         *
 ***************************************************************************/

#define FREPPLE_CORE
#include "frepple/solver.h"
namespace frepple
{

DECLARE_EXPORT const MetaClass* SolverMRP::metadata;
const Keyword SolverMRP::tag_iterationthreshold("iterationthreshold");
const Keyword SolverMRP::tag_iterationaccuracy("iterationaccuracy");
const Keyword SolverMRP::tag_lazydelay("lazydelay");
const Keyword SolverMRP::tag_allowsplits("allowsplits");
const Keyword SolverMRP::tag_rotateresources("rotateresources");
const Keyword SolverMRP::tag_planSafetyStockFirst("plansafetystockfirst");
const Keyword SolverMRP::tag_iterationmax("iterationmax");

void LibrarySolver::initialize()
{
  // Initialize only once
  static bool init = false;
  if (init)
  {
    logger << "Warning: Calling frepple::LibrarySolver::initialize() more "
        << "than once." << endl;
    return;
  }
  init = true;

  // Register all classes.
  int nok = 0;
  nok += SolverMRP::initialize();
  nok += OperatorDelete::initialize();
  if (nok) throw RuntimeException("Error registering new Python types");
}

int SolverMRP::initialize()
{
  // Initialize the metadata
  metadata = MetaClass::registerClass<SolverMRP>(
    "solver", "solver_mrp", Object::create<SolverMRP>, true
    );
  registerFields<SolverMRP>(const_cast<MetaClass*>(metadata));

  // Initialize the Python class
  PythonType& x = FreppleClass<SolverMRP, Solver>::getPythonType();
  x.setName("solver_mrp");
  x.setDoc("frePPLe solver_mrp");
  x.supportgetattro();
  x.supportsetattro();
  x.supportcreate(create);
  x.addMethod("solve", solve, METH_NOARGS, "run the solver");
  x.addMethod("commit", commit, METH_NOARGS, "commit the plan changes");
  x.addMethod("rollback", rollback, METH_NOARGS, "rollback the plan changes");
  const_cast<MetaClass*>(metadata)->pythonClass = x.type_object();
  return x.typeReady();
}

PyObject* SolverMRP::create(PyTypeObject* pytype, PyObject* args, PyObject* kwds)
{
  try
  {
    // Create the solver
    SolverMRP *s = new SolverMRP();

    // Iterate over extra keywords, and set attributes.   @todo move this responsibility to the readers...
    if (kwds)
    {
      PyObject *key, *value;
      Py_ssize_t pos = 0;
      while (PyDict_Next(kwds, &pos, &key, &value))
      {
        PythonData field(value);
        PyObject* key_utf8 = PyUnicode_AsUTF8String(key);
        DataKeyword attr(PyBytes_AsString(key_utf8));
        Py_DECREF(key_utf8);
        const MetaFieldBase* fmeta = SolverMRP::metadata->findField(attr.getHash());
        if (!fmeta)
          fmeta = Solver::metadata->findField(attr.getHash());
        if (fmeta)
          // Update the attribute
          fmeta->setField(s, field);
        else
          s->setProperty(attr.getName(), value);
      };
    }

    // Return the object. The reference count doesn't need to be increased
    // as we do with other objects, because we want this object to be available
    // for the garbage collector of Python.
    return static_cast<PyObject*>(s);
  }
  catch (...)
  {
    PythonType::evalException();
    return NULL;
  }
}

DECLARE_EXPORT bool SolverMRP::demand_comparison(const Demand* l1, const Demand* l2)
{
  if (l1->getPriority() != l2->getPriority())
    return l1->getPriority() < l2->getPriority();
  else if (l1->getDue() != l2->getDue())
    return l1->getDue() < l2->getDue();
  else
    return l1->getQuantity() < l2->getQuantity();
}

DECLARE_EXPORT void SolverMRP::SolverMRPdata::commit()
{
  // Check
  SolverMRP* solver = getSolver();
  if (!demands || !solver)
    throw LogicException("Missing demands or solver.");

  // Message
  if (solver->getLogLevel()>0)
    logger << "Start solving cluster " << cluster << " at " << Date::now() << endl;

  // Solve the planning problem
  try
  {
    // TODO Propagate & solve initial shortages and overloads

    // Sort the demands of this problem.
    // We use a stable sort to get reproducible results between platforms
    // and STL implementations.
    stable_sort(demands->begin(), demands->end(), demand_comparison);

    // Solve for safety stock in buffers.
    if (solver->getPlanSafetyStockFirst())
    {
      constrainedPlanning = (solver->getPlanType() == 1);
      solveSafetyStock(solver);
    }

    // Loop through the list of all demands in this planning problem
    safety_stock_planning = false;
    constrainedPlanning = (solver->getPlanType() == 1);
    for (deque<Demand*>::const_iterator i = demands->begin();
        i != demands->end(); ++i)
    {
      iteration_count = 0;
      try
      {
        // Plan the demand
        (*i)->solve(*solver, this);
      }
      catch (...)
      {
        // Error message
        logger << "Error: Caught an exception while solving demand '"
            << (*i)->getName() << "':" << endl;
        try {throw;}
        catch (const bad_exception&) {logger << "  bad exception" << endl;}
        catch (const exception& e) {logger << "  " << e.what() << endl;}
        catch (...) {logger << "  Unknown type" << endl;}
      }
    }

    // Clean the list of demands of this cluster
    demands->clear();

    // Completely recreate all purchasing operation plans
    for (set<const OperationItemSupplier*>::iterator o = purchase_operations.begin();
      o != purchase_operations.end(); ++o
      )
    {
      // TODO This code assumes the buffer is ONLY replenished through these purchases.
      // When it is replenished through an alternate, it will not give the results we expect.

      // Erase existing proposed purchases
      const_cast<OperationItemSupplier*>(*o)->deleteOperationPlans(false);
      // Create new proposed purchases
      try
      {
        safety_stock_planning = true;
        state->curBuffer = NULL;
        state->q_qty = -1.0;
        state->q_date = Date::infinitePast;
        state->a_cost = 0.0;
        state->a_penalty = 0.0;
        state->curDemand = NULL;
        state->curOwnerOpplan = NULL;
        state->a_qty = 0;
        (*o)->getBuffer()->solve(*solver, this);
        CommandManager::commit();
      }
      catch(...)
      {
        CommandManager::rollback();
      }
    }
    purchase_operations.clear();

    // Solve for safety stock in buffers.
    if (!solver->getPlanSafetyStockFirst())
      solveSafetyStock(solver);
  }
  catch (...)
  {
    // We come in this exception handling code only if there is a problem with
    // with this cluster that goes beyond problems with single orders.
    // If the problem is with single orders, the exception handling code above
    // will do a proper rollback.

    // Error message
    logger << "Error: Caught an exception while solving cluster "
        << cluster << ":" << endl;
    try {throw;}
    catch (const bad_exception&) {logger << "  bad exception" << endl;}
    catch (const exception& e) {logger << "  " << e.what() << endl;}
    catch (...) {logger << "  Unknown type" << endl;}

    // Clean up the operationplans of this cluster
    for (Operation::iterator f=Operation::begin(); f!=Operation::end(); ++f)
      if (f->getCluster() == cluster)
        f->deleteOperationPlans();

    // Clean the list of demands of this cluster
    demands->clear();
  }

  // Message
  if (solver->getLogLevel()>0)
    logger << "End solving cluster " << cluster << " at " << Date::now() << endl;
}

void SolverMRP::SolverMRPdata::solveSafetyStock(SolverMRP* solver)
{
  OperatorDelete cleanup(this);
  safety_stock_planning = true;
  if (getLogLevel() > 0)
    logger << "Start safety stock replenishment pass   " << solver->getConstraints() << endl;
  vector< list<Buffer*> > bufs(HasLevel::getNumberOfLevels() + 1);
  for (Buffer::iterator buf = Buffer::begin(); buf != Buffer::end(); ++buf)
    if (buf->getCluster() == cluster
      && ( buf->getMinimum() || buf->getMinimumCalendar()
        || buf->getType() == *BufferProcure::metadata )
      )
      bufs[(buf->getLevel()>=0) ? buf->getLevel() : 0].push_back(&*buf);
  for (vector< list<Buffer*> >::iterator b_list = bufs.begin(); b_list != bufs.end(); ++b_list)
    for (list<Buffer*>::iterator b = b_list->begin(); b != b_list->end(); ++b)
      try
      {
        state->curBuffer = NULL;
        // A quantity of -1 is a flag for the buffer solver to solve safety stock.
        state->q_qty = -1.0;
        state->q_date = Date::infinitePast;
        state->a_cost = 0.0;
        state->a_penalty = 0.0;
        planningDemand = NULL;
        state->curDemand = NULL;
        state->curOwnerOpplan = NULL;
        // Call the buffer solver
        iteration_count = 0;
        (*b)->solve(*solver, this);
        // Check for excess
        if ((*b)->getType() != *BufferProcure::metadata)
          (*b)->solve(cleanup, this);
        CommandManager::commit();
      }
      catch(...)
      {
        CommandManager::rollback();
      }

  if (getLogLevel() > 0)
    logger << "Finished safety stock replenishment pass" << endl;
  safety_stock_planning = false;
}

DECLARE_EXPORT void SolverMRP::update_user_exits()
{
  setUserExitBuffer(getPyObjectProperty(Tags::userexit_buffer.getName()));
  setUserExitDemand(getPyObjectProperty(Tags::userexit_demand.getName()));
  setUserExitFlow(getPyObjectProperty(Tags::userexit_flow.getName()));
  setUserExitOperation(getPyObjectProperty(Tags::userexit_operation.getName()));
  setUserExitResource(getPyObjectProperty(Tags::userexit_resource.getName()));
}

DECLARE_EXPORT void SolverMRP::solve(void *v)
{
  // Configure user exits
  update_user_exits();

  // Count how many clusters we have to plan
  int cl = HasLevel::getNumberOfClusters() + 1;

  // Categorize all demands in their cluster
  demands_per_cluster.resize(cl);
  for (Demand::iterator i = Demand::begin(); i != Demand::end(); ++i)
    demands_per_cluster[i->getCluster()].push_back(&*i);

  // Delete of operationplans of the affected clusters
  // This deletion is not multi-threaded... But on the other hand we need to
  // loop through the operations only once (rather than as many times as there
  // are clusters)
  if (getErasePreviousFirst())
  {
    if (getLogLevel()>0) logger << "Deleting previous plan" << endl;
    for (Operation::iterator e=Operation::begin(); e!=Operation::end(); ++e)
      e->deleteOperationPlans();
  }

  // Solve in parallel threads.
  // When not solving in silent and autocommit mode, we only use a single
  // solver thread.
  // Otherwise we use as many worker threads as processor cores.
  ThreadGroup threads;
  if (getLogLevel()>0 || !getAutocommit())
    threads.setMaxParallel(1);

  // Register all clusters to be solved
  for (int j = 0; j < cl; ++j)
    threads.add(
      SolverMRPdata::runme,
      new SolverMRPdata(this, j, &(demands_per_cluster[j]))
      );

  // Run the planning command threads and wait for them to exit
  threads.execute();

  // @todo Check the resource setups that were broken - needs to be removed
  for (Resource::iterator gres = Resource::begin(); gres != Resource::end(); ++gres)
    if (gres->getSetupMatrix()) gres->updateSetups();
}

DECLARE_EXPORT PyObject* SolverMRP::solve(PyObject *self, PyObject *args)
{
  // Parse the argument
  PyObject *dem = NULL;
  if (args && !PyArg_ParseTuple(args, "|O:solve", &dem)) return NULL;
  if (dem && !PyObject_TypeCheck(dem, Demand::metadata->pythonClass))
  {
    PyErr_SetString(PythonDataException, "solve(d) argument must be a demand");
    return NULL;
  }

  Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
  try
  {
    SolverMRP* sol = static_cast<SolverMRP*>(self);
    if (!dem)
    {
      // Complete replan
      sol->setAutocommit(true);
      sol->solve();
    }
    else
    {
      // Incrementally plan a single demand
      sol->setAutocommit(false);
      sol->update_user_exits();
      static_cast<Demand*>(dem)->solve(*sol, &(sol->getCommands()));
    }
  }
  catch(...)
  {
    Py_BLOCK_THREADS;
    PythonType::evalException();
    return NULL;
  }
  Py_END_ALLOW_THREADS   // Reclaim Python interpreter
  return Py_BuildValue("");
}

DECLARE_EXPORT PyObject* SolverMRP::commit(PyObject *self, PyObject *args)
{
  Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
  try
  {
    SolverMRP * me = static_cast<SolverMRP*>(self);
    me->scanExcess(&(me->commands));
    me->commands.CommandManager::commit();
  }
  catch(...)
  {
    Py_BLOCK_THREADS;
    PythonType::evalException();
    return NULL;
  }
  Py_END_ALLOW_THREADS   // Reclaim Python interpreter
  return Py_BuildValue("");
}

DECLARE_EXPORT PyObject* SolverMRP::rollback(PyObject *self, PyObject *args)
{
  Py_BEGIN_ALLOW_THREADS   // Free Python interpreter for other threads
  try
  {
    static_cast<SolverMRP*>(self)->commands.rollback();
  }
  catch(...)
  {
    Py_BLOCK_THREADS;
    PythonType::evalException();
    return NULL;
  }
  Py_END_ALLOW_THREADS   // Reclaim Python interpreter
  return Py_BuildValue("");
}

} // end namespace
<|endoftext|>