<commit_before>/*
	This file is part of cpp-ethereum.

	cpp-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	cpp-ethereum 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 General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file EthereumHost.cpp
 * @author Gav Wood <i@gavwood.com>
 * @date 2014
 */

#include "EthereumHost.h"

#include <set>
#include <chrono>
#include <thread>
#include <libdevcore/Common.h>
#include <libp2p/Host.h>
#include <libp2p/Session.h>
#include <libethcore/Exceptions.h>
#include "BlockChain.h"
#include "TransactionQueue.h"
#include "BlockQueue.h"
#include "EthereumPeer.h"
#include "DownloadMan.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace p2p;

EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId):
	HostCapability<EthereumPeer>(),
	Worker		("ethsync"),
	m_chain		(_ch),
	m_tq		(_tq),
	m_bq		(_bq),
	m_networkId	(_networkId)
{
	m_latestBlockSent = _ch.currentHash();
}

EthereumHost::~EthereumHost()
{
	for (auto i: peerSessions())
		i.first->cap<EthereumPeer>().get()->abortSync();
}

bool EthereumHost::ensureInitialised()
{
	if (!m_latestBlockSent)
	{
		// First time - just initialise.
		m_latestBlockSent = m_chain.currentHash();
		clog(NetNote) << "Initialising: latest=" << m_latestBlockSent.abridged();

		for (auto const& i: m_tq.transactions())
			m_transactionsSent.insert(i.first);
		return true;
	}
	return false;
}

void EthereumHost::noteNeedsSyncing(EthereumPeer* _who)
{
	// if already downloading hash-chain, ignore.
	if (isSyncing())
	{
		clog(NetAllDetail) << "Sync in progress: Just set to help out.";
		if (m_syncer->m_asking == Asking::Blocks)
			_who->transition(Asking::Blocks);
	}
	else
		// otherwise check to see if we should be downloading...
		_who->attemptSync();
}

void EthereumHost::changeSyncer(EthereumPeer* _syncer)
{
	if (_syncer)
		clog(NetAllDetail) << "Changing syncer to" << _syncer->session()->socketId();
	else
		clog(NetAllDetail) << "Clearing syncer.";

	m_syncer = _syncer;
	if (isSyncing())
	{
		if (_syncer->m_asking == Asking::Blocks)
			for (auto j: peerSessions())
			{
				auto e = j.first->cap<EthereumPeer>().get();
				if (e != _syncer && e->m_asking == Asking::Nothing)
					e->transition(Asking::Blocks);
			}
	}
	else
	{
		// start grabbing next hash chain if there is one.
		for (auto j: peerSessions())
		{
			j.first->cap<EthereumPeer>()->attemptSync();
			if (isSyncing())
				return;
		}
		clog(NetNote) << "No more peers to sync with.";
	}
}

void EthereumHost::noteDoneBlocks(EthereumPeer* _who, bool _clemency)
{
	if (m_man.isComplete())
	{
		// Done our chain-get.
		clog(NetNote) << "Chain download complete.";
		// 1/100th for each useful block hash.
		_who->addRating(m_man.chain().size() / 100);
		m_man.reset();
	}
	else if (_who->isSyncing())
	{
		if (_clemency)
			clog(NetNote) << "Chain download failed. Aborted while incomplete.";
		else
		{
			// Done our chain-get.
			clog(NetNote) << "Chain download failed. Peer with blocks didn't have them all. This peer is bad and should be punished.";

			m_banned.insert(_who->session()->id());			// We know who you are!
			_who->disable("Peer sent hashes but was unable to provide the blocks.");
		}
		m_man.reset();
	}
}

void EthereumHost::reset()
{
	if (m_syncer)
		m_syncer->abortSync();

	m_man.resetToChain(h256s());

	m_latestBlockSent = h256();
	m_transactionsSent.clear();
}

void EthereumHost::doWork()
{
	bool netChange = ensureInitialised();
	auto h = m_chain.currentHash();
	// If we've finished our initial sync (including getting all the blocks into the chain so as to reduce invalid transactions), start trading transactions & blocks
	if (!isSyncing() && m_chain.isKnown(m_latestBlockSent))
	{
		if (m_newTransactions)
		{
			m_newTransactions = false;
			maintainTransactions();
		}
		if (m_newBlocks)
		{
			m_newBlocks = false;
			maintainBlocks(h);
		}
	}

	for (auto p: peerSessions())
		if (shared_ptr<EthereumPeer> const& ep = p.first->cap<EthereumPeer>())
			ep->tick();

//	return netChange;
	// TODO: Figure out what to do with netChange.
	(void)netChange;
}

void EthereumHost::maintainTransactions()
{
	// Send any new transactions.
	map<std::shared_ptr<EthereumPeer>, h256s> peerTransactions;
	auto ts = m_tq.transactions();
	for (auto const& i: ts)
	{
		bool unsent = !m_transactionsSent.count(i.first);
		for (auto const& p: randomSelection(25, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); }))
			peerTransactions[p].push_back(i.first);
	}
	for (auto p: peerSessions())
		if (auto ep = p.first->cap<EthereumPeer>())
		{
			bytes b;
			unsigned n = 0;
			for (auto const& h: peerTransactions[ep])
			{
				b += ts[h].rlp();
				++n;
			}
			for (auto const& t: ts)
				m_transactionsSent.insert(t.first);

			ep->clearKnownTransactions();

			if (n || ep->m_requireTransactions)
			{
				RLPStream ts;
				ep->prep(ts, TransactionsPacket, n).appendRaw(b, n);
				ep->sealAndSend(ts);
			}
			ep->m_requireTransactions = false;
		}
}

std::vector<std::shared_ptr<EthereumPeer>> EthereumHost::randomSelection(unsigned _percent, std::function<bool(EthereumPeer*)> const& _allow)
{
	std::vector<std::shared_ptr<EthereumPeer>> candidates;
	candidates.reserve(peerSessions().size());
	for (auto const& j: peerSessions())
	{
		auto pp = j.first->cap<EthereumPeer>();
		if (_allow(pp.get()))
			candidates.push_back(pp);
	}

	std::vector<std::shared_ptr<EthereumPeer>> ret;
	for (unsigned i = (peerSessions().size() * _percent + 99) / 100; i-- && candidates.size();)
	{
		unsigned n = rand() % candidates.size();
		ret.push_back(std::move(candidates[n]));
		candidates.erase(candidates.begin() + n);
	}
	return ret;
}

void EthereumHost::maintainBlocks(h256 _currentHash)
{
	// Send any new blocks.
	if (m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty)
	{
		clog(NetMessageSummary) << "Sending a new block (current is" << _currentHash << ", was" << m_latestBlockSent << ")";

		for (auto const& p: randomSelection(25, [&](EthereumPeer* p){return !p->m_knownBlocks.count(_currentHash); }))
		{
			RLPStream ts;
			p->prep(ts, NewBlockPacket, 2).appendRaw(m_chain.block(), 1).append(m_chain.details().totalDifficulty);

			Guard l(p->x_knownBlocks);
			p->sealAndSend(ts);
			p->m_knownBlocks.clear();
		}
		m_latestBlockSent = _currentHash;
	}
}
<commit_msg>Minor cleanup for node's tx management.<commit_after>/*
	This file is part of cpp-ethereum.

	cpp-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	cpp-ethereum 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 General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file EthereumHost.cpp
 * @author Gav Wood <i@gavwood.com>
 * @date 2014
 */

#include "EthereumHost.h"

#include <set>
#include <chrono>
#include <thread>
#include <libdevcore/Common.h>
#include <libp2p/Host.h>
#include <libp2p/Session.h>
#include <libethcore/Exceptions.h>
#include "BlockChain.h"
#include "TransactionQueue.h"
#include "BlockQueue.h"
#include "EthereumPeer.h"
#include "DownloadMan.h"
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace p2p;

EthereumHost::EthereumHost(BlockChain const& _ch, TransactionQueue& _tq, BlockQueue& _bq, u256 _networkId):
	HostCapability<EthereumPeer>(),
	Worker		("ethsync"),
	m_chain		(_ch),
	m_tq		(_tq),
	m_bq		(_bq),
	m_networkId	(_networkId)
{
	m_latestBlockSent = _ch.currentHash();
}

EthereumHost::~EthereumHost()
{
	for (auto i: peerSessions())
		i.first->cap<EthereumPeer>().get()->abortSync();
}

bool EthereumHost::ensureInitialised()
{
	if (!m_latestBlockSent)
	{
		// First time - just initialise.
		m_latestBlockSent = m_chain.currentHash();
		clog(NetNote) << "Initialising: latest=" << m_latestBlockSent.abridged();

		for (auto const& i: m_tq.transactions())
			m_transactionsSent.insert(i.first);
		return true;
	}
	return false;
}

void EthereumHost::noteNeedsSyncing(EthereumPeer* _who)
{
	// if already downloading hash-chain, ignore.
	if (isSyncing())
	{
		clog(NetAllDetail) << "Sync in progress: Just set to help out.";
		if (m_syncer->m_asking == Asking::Blocks)
			_who->transition(Asking::Blocks);
	}
	else
		// otherwise check to see if we should be downloading...
		_who->attemptSync();
}

void EthereumHost::changeSyncer(EthereumPeer* _syncer)
{
	if (_syncer)
		clog(NetAllDetail) << "Changing syncer to" << _syncer->session()->socketId();
	else
		clog(NetAllDetail) << "Clearing syncer.";

	m_syncer = _syncer;
	if (isSyncing())
	{
		if (_syncer->m_asking == Asking::Blocks)
			for (auto j: peerSessions())
			{
				auto e = j.first->cap<EthereumPeer>().get();
				if (e != _syncer && e->m_asking == Asking::Nothing)
					e->transition(Asking::Blocks);
			}
	}
	else
	{
		// start grabbing next hash chain if there is one.
		for (auto j: peerSessions())
		{
			j.first->cap<EthereumPeer>()->attemptSync();
			if (isSyncing())
				return;
		}
		clog(NetNote) << "No more peers to sync with.";
	}
}

void EthereumHost::noteDoneBlocks(EthereumPeer* _who, bool _clemency)
{
	if (m_man.isComplete())
	{
		// Done our chain-get.
		clog(NetNote) << "Chain download complete.";
		// 1/100th for each useful block hash.
		_who->addRating(m_man.chain().size() / 100);
		m_man.reset();
	}
	else if (_who->isSyncing())
	{
		if (_clemency)
			clog(NetNote) << "Chain download failed. Aborted while incomplete.";
		else
		{
			// Done our chain-get.
			clog(NetNote) << "Chain download failed. Peer with blocks didn't have them all. This peer is bad and should be punished.";

			m_banned.insert(_who->session()->id());			// We know who you are!
			_who->disable("Peer sent hashes but was unable to provide the blocks.");
		}
		m_man.reset();
	}
}

void EthereumHost::reset()
{
	if (m_syncer)
		m_syncer->abortSync();

	m_man.resetToChain(h256s());

	m_latestBlockSent = h256();
	m_transactionsSent.clear();
}

void EthereumHost::doWork()
{
	bool netChange = ensureInitialised();
	auto h = m_chain.currentHash();
	// If we've finished our initial sync (including getting all the blocks into the chain so as to reduce invalid transactions), start trading transactions & blocks
	if (!isSyncing() && m_chain.isKnown(m_latestBlockSent))
	{
		if (m_newTransactions)
		{
			m_newTransactions = false;
			maintainTransactions();
		}
		if (m_newBlocks)
		{
			m_newBlocks = false;
			maintainBlocks(h);
		}
	}

	for (auto p: peerSessions())
		if (shared_ptr<EthereumPeer> const& ep = p.first->cap<EthereumPeer>())
			ep->tick();

//	return netChange;
	// TODO: Figure out what to do with netChange.
	(void)netChange;
}

void EthereumHost::maintainTransactions()
{
	// Send any new transactions.
	map<std::shared_ptr<EthereumPeer>, h256s> peerTransactions;
	auto ts = m_tq.transactions();
	for (auto const& i: ts)
	{
		bool unsent = !m_transactionsSent.count(i.first);
		for (auto const& p: randomSelection(25, [&](EthereumPeer* p) { return p->m_requireTransactions || (unsent && !p->m_knownTransactions.count(i.first)); }))
			peerTransactions[p].push_back(i.first);
	}
	for (auto const& t: ts)
		m_transactionsSent.insert(t.first);
	for (auto p: peerSessions())
		if (auto ep = p.first->cap<EthereumPeer>())
		{
			bytes b;
			unsigned n = 0;
			for (auto const& h: peerTransactions[ep])
			{
				ep->m_knownTransactions.insert(h);
				b += ts[h].rlp();
				++n;
			}

			ep->clearKnownTransactions();

			if (n || ep->m_requireTransactions)
			{
				RLPStream ts;
				ep->prep(ts, TransactionsPacket, n).appendRaw(b, n);
				ep->sealAndSend(ts);
			}
			ep->m_requireTransactions = false;
		}
}

std::vector<std::shared_ptr<EthereumPeer>> EthereumHost::randomSelection(unsigned _percent, std::function<bool(EthereumPeer*)> const& _allow)
{
	std::vector<std::shared_ptr<EthereumPeer>> candidates;
	candidates.reserve(peerSessions().size());
	for (auto const& j: peerSessions())
	{
		auto pp = j.first->cap<EthereumPeer>();
		if (_allow(pp.get()))
			candidates.push_back(pp);
	}

	std::vector<std::shared_ptr<EthereumPeer>> ret;
	for (unsigned i = (peerSessions().size() * _percent + 99) / 100; i-- && candidates.size();)
	{
		unsigned n = rand() % candidates.size();
		ret.push_back(std::move(candidates[n]));
		candidates.erase(candidates.begin() + n);
	}
	return ret;
}

void EthereumHost::maintainBlocks(h256 _currentHash)
{
	// Send any new blocks.
	if (m_chain.details(m_latestBlockSent).totalDifficulty < m_chain.details(_currentHash).totalDifficulty)
	{
		clog(NetMessageSummary) << "Sending a new block (current is" << _currentHash << ", was" << m_latestBlockSent << ")";

		for (auto const& p: randomSelection(25, [&](EthereumPeer* p){return !p->m_knownBlocks.count(_currentHash); }))
		{
			RLPStream ts;
			p->prep(ts, NewBlockPacket, 2).appendRaw(m_chain.block(), 1).append(m_chain.details().totalDifficulty);

			Guard l(p->x_knownBlocks);
			p->sealAndSend(ts);
			p->m_knownBlocks.clear();
		}
		m_latestBlockSent = _currentHash;
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Copyright (c) 2011-2012 Litecoin Developers
// Copyright (c) 2013 DogeCoin Developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "irc.h"
#include "net.h"
#include "strlcpy.h"
#include "base58.h"

using namespace std;
using namespace boost;

int nGotIRCAddresses = 0;

void ThreadIRCSeed2(void* parg);

#pragma pack(push, 1)
struct ircaddr
{
    struct in_addr ip;
    short port;
};
#pragma pack(pop)

string EncodeAddress(const CService& addr)
{
    struct ircaddr tmp;
    if (addr.GetInAddr(&tmp.ip))
    {
        tmp.port = htons(addr.GetPort());

        vector<unsigned char> vch(UBEGIN(tmp), UEND(tmp));
        return string("u") + EncodeBase58Check(vch);
    }
    return "";
}

bool DecodeAddress(string str, CService& addr)
{
    vector<unsigned char> vch;
    if (!DecodeBase58Check(str.substr(1), vch))
        return false;

    struct ircaddr tmp;
    if (vch.size() != sizeof(tmp))
        return false;
    memcpy(&tmp, &vch[0], sizeof(tmp));

    addr = CService(tmp.ip, ntohs(tmp.port));
    return true;
}

static bool Send(SOCKET hSocket, const char* pszSend)
{
    if (strstr(pszSend, "PONG") != pszSend)
        printf("IRC SENDING: %s\n", pszSend);
    const char* psz = pszSend;
    const char* pszEnd = psz + strlen(psz);
    while (psz < pszEnd)
    {
        int ret = send(hSocket, psz, pszEnd - psz, MSG_NOSIGNAL);
        if (ret < 0)
            return false;
        psz += ret;
    }
    return true;
}

bool RecvLineIRC(SOCKET hSocket, string& strLine)
{
    loop
    {
        bool fRet = RecvLine(hSocket, strLine);
        if (fRet)
        {
            if (fShutdown)
                return false;
            vector<string> vWords;
            ParseString(strLine, ' ', vWords);
            if (vWords.size() >= 1 && vWords[0] == "PING")
            {
                strLine[1] = 'O';
                strLine += '\r';
                Send(hSocket, strLine.c_str());
                continue;
            }
        }
        return fRet;
    }
}

int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL, const char* psz4=NULL)
{
    loop
    {
        string strLine;
        strLine.reserve(10000);
        if (!RecvLineIRC(hSocket, strLine))
            return 0;
        printf("IRC %s\n", strLine.c_str());
        if (psz1 && strLine.find(psz1) != string::npos)
            return 1;
        if (psz2 && strLine.find(psz2) != string::npos)
            return 2;
        if (psz3 && strLine.find(psz3) != string::npos)
            return 3;
        if (psz4 && strLine.find(psz4) != string::npos)
            return 4;
    }
}

bool Wait(int nSeconds)
{
    if (fShutdown)
        return false;
    printf("IRC waiting %d seconds to reconnect\n", nSeconds);
    for (int i = 0; i < nSeconds; i++)
    {
        if (fShutdown)
            return false;
        Sleep(1000);
    }
    return true;
}

bool RecvCodeLine(SOCKET hSocket, const char* psz1, string& strRet)
{
    strRet.clear();
    loop
    {
        string strLine;
        if (!RecvLineIRC(hSocket, strLine))
            return false;

        vector<string> vWords;
        ParseString(strLine, ' ', vWords);
        if (vWords.size() < 2)
            continue;

        if (vWords[1] == psz1)
        {
            printf("IRC %s\n", strLine.c_str());
            strRet = strLine;
            return true;
        }
    }
}

bool GetIPFromIRC(SOCKET hSocket, string strMyName, CNetAddr& ipRet)
{
    Send(hSocket, strprintf("USERHOST %s\r", strMyName.c_str()).c_str());

    string strLine;
    if (!RecvCodeLine(hSocket, "302", strLine))
        return false;

    vector<string> vWords;
    ParseString(strLine, ' ', vWords);
    if (vWords.size() < 4)
        return false;

    string str = vWords[3];
    if (str.rfind("@") == string::npos)
        return false;
    string strHost = str.substr(str.rfind("@")+1);

    // Hybrid IRC used by lfnet always returns IP when you userhost yourself,
    // but in case another IRC is ever used this should work.
    printf("GetIPFromIRC() got userhost %s\n", strHost.c_str());
    CNetAddr addr(strHost, true);
    if (!addr.IsValid())
        return false;
    ipRet = addr;

    return true;
}

void ThreadIRCSeed(void* parg)
{
    IMPLEMENT_RANDOMIZE_STACK(ThreadIRCSeed(parg));

    // Make this thread recognisable as the IRC seeding thread
    RenameThread("bitcoin-ircseed");

    try
    {
        ThreadIRCSeed2(parg);
    }
    catch (std::exception& e) {
        PrintExceptionContinue(&e, "ThreadIRCSeed()");
    } catch (...) {
        PrintExceptionContinue(NULL, "ThreadIRCSeed()");
    }
    printf("ThreadIRCSeed exited\n");
}

void ThreadIRCSeed2(void* parg)
{
    /* Dont advertise on IRC if we don't allow incoming connections */
    if (mapArgs.count("-connect") || fNoListen)
        return;

    if (!GetBoolArg("-irc", false))
        return;

    printf("ThreadIRCSeed started\n");
    int nErrorWait = 10;
    int nRetryWait = 10;

    while (!fShutdown)
    {
        CService addrConnect("92.243.23.21", 6667); // irc.lfnet.org

        CService addrIRC("irc.lfnet.org", 6667, true);
        if (addrIRC.IsValid())
            addrConnect = addrIRC;

        SOCKET hSocket;
        if (!ConnectSocket(addrConnect, hSocket))
        {
            printf("IRC connect failed\n");
            nErrorWait = nErrorWait * 11 / 10;
            if (Wait(nErrorWait += 60))
                continue;
            else
                return;
        }

        if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname", "ignoring hostname"))
        {
            closesocket(hSocket);
            hSocket = INVALID_SOCKET;
            nErrorWait = nErrorWait * 11 / 10;
            if (Wait(nErrorWait += 60))
                continue;
            else
                return;
        }

        CNetAddr addrIPv4("1.2.3.4"); // arbitrary IPv4 address to make GetLocal prefer IPv4 addresses
        CService addrLocal;
        string strMyName;
        if (GetLocal(addrLocal, &addrIPv4))
            strMyName = EncodeAddress(GetLocalAddress(&addrConnect));
        if (strMyName == "")
            strMyName = strprintf("x%u", GetRand(1000000000));

        Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
        Send(hSocket, strprintf("USER %s 8 * : %s\r", strMyName.c_str(), strMyName.c_str()).c_str());

        int nRet = RecvUntil(hSocket, " 004 ", " 433 ");
        if (nRet != 1)
        {
            closesocket(hSocket);
            hSocket = INVALID_SOCKET;
            if (nRet == 2)
            {
                printf("IRC name already in use\n");
                Wait(10);
                continue;
            }
            nErrorWait = nErrorWait * 11 / 10;
            if (Wait(nErrorWait += 60))
                continue;
            else
                return;
        }
        Sleep(500);

        // Get our external IP from the IRC server and re-nick before joining the channel
        CNetAddr addrFromIRC;
        if (GetIPFromIRC(hSocket, strMyName, addrFromIRC))
        {
            printf("GetIPFromIRC() returned %s\n", addrFromIRC.ToString().c_str());
            if (addrFromIRC.IsRoutable())
            {
                // IRC lets you to re-nick
                AddLocal(addrFromIRC, LOCAL_IRC);
                strMyName = EncodeAddress(GetLocalAddress(&addrConnect));
                Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
            }
        }

        if (fTestNet) {
            Send(hSocket, "JOIN #dogecoinTEST3\r");
            Send(hSocket, "WHO #dogecoinTEST3\r");
        } else {
            // randomly join #dogecoin00-#dogecoin99
            int channel_number = GetRandInt(100);
            channel_number = 0; // DogeCoin: for now, just use one channel
            Send(hSocket, strprintf("JOIN #dogecoin%02d\r", channel_number).c_str());
            Send(hSocket, strprintf("WHO #dogecoin%02d\r", channel_number).c_str());
        }

        int64 nStart = GetTime();
        string strLine;
        strLine.reserve(10000);
        while (!fShutdown && RecvLineIRC(hSocket, strLine))
        {
            if (strLine.empty() || strLine.size() > 900 || strLine[0] != ':')
                continue;

            vector<string> vWords;
            ParseString(strLine, ' ', vWords);
            if (vWords.size() < 2)
                continue;

            char pszName[10000];
            pszName[0] = '\0';

            if (vWords[1] == "352" && vWords.size() >= 8)
            {
                // index 7 is limited to 16 characters
                // could get full length name at index 10, but would be different from join messages
                strlcpy(pszName, vWords[7].c_str(), sizeof(pszName));
                printf("IRC got who\n");
            }

            if (vWords[1] == "JOIN" && vWords[0].size() > 1)
            {
                // :username!username@50000007.F000000B.90000002.IP JOIN :#channelname
                strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName));
                if (strchr(pszName, '!'))
                    *strchr(pszName, '!') = '\0';
                printf("IRC got join\n");
            }

            if (pszName[0] == 'u')
            {
                CAddress addr;
                if (DecodeAddress(pszName, addr))
                {
                    addr.nTime = GetAdjustedTime();
                    if (addrman.Add(addr, addrConnect, 51 * 60))
                        printf("IRC got new address: %s\n", addr.ToString().c_str());
                    nGotIRCAddresses++;
                }
                else
                {
                    printf("IRC decode failed\n");
                }
            }
        }
        closesocket(hSocket);
        hSocket = INVALID_SOCKET;

        if (GetTime() - nStart > 20 * 60)
        {
            nErrorWait /= 3;
            nRetryWait /= 3;
        }

        nRetryWait = nRetryWait * 11 / 10;
        if (!Wait(nRetryWait += 60))
            return;
    }
}

#ifdef TEST
int main(int argc, char *argv[])
{
    WSADATA wsadata;
    if (WSAStartup(MAKEWORD(2,2), &wsadata) != NO_ERROR)
    {
        printf("Error at WSAStartup()\n");
        return false;
    }

    ThreadIRCSeed(NULL);

    WSACleanup();
    return 0;
}
#endif
<commit_msg>network is now over 3k peers , get them to join 50 random channels!<commit_after>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Copyright (c) 2011-2012 Litecoin Developers
// Copyright (c) 2013 DogeCoin Developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "irc.h"
#include "net.h"
#include "strlcpy.h"
#include "base58.h"

using namespace std;
using namespace boost;

int nGotIRCAddresses = 0;

void ThreadIRCSeed2(void* parg);

#pragma pack(push, 1)
struct ircaddr
{
    struct in_addr ip;
    short port;
};
#pragma pack(pop)

string EncodeAddress(const CService& addr)
{
    struct ircaddr tmp;
    if (addr.GetInAddr(&tmp.ip))
    {
        tmp.port = htons(addr.GetPort());

        vector<unsigned char> vch(UBEGIN(tmp), UEND(tmp));
        return string("u") + EncodeBase58Check(vch);
    }
    return "";
}

bool DecodeAddress(string str, CService& addr)
{
    vector<unsigned char> vch;
    if (!DecodeBase58Check(str.substr(1), vch))
        return false;

    struct ircaddr tmp;
    if (vch.size() != sizeof(tmp))
        return false;
    memcpy(&tmp, &vch[0], sizeof(tmp));

    addr = CService(tmp.ip, ntohs(tmp.port));
    return true;
}

static bool Send(SOCKET hSocket, const char* pszSend)
{
    if (strstr(pszSend, "PONG") != pszSend)
        printf("IRC SENDING: %s\n", pszSend);
    const char* psz = pszSend;
    const char* pszEnd = psz + strlen(psz);
    while (psz < pszEnd)
    {
        int ret = send(hSocket, psz, pszEnd - psz, MSG_NOSIGNAL);
        if (ret < 0)
            return false;
        psz += ret;
    }
    return true;
}

bool RecvLineIRC(SOCKET hSocket, string& strLine)
{
    loop
    {
        bool fRet = RecvLine(hSocket, strLine);
        if (fRet)
        {
            if (fShutdown)
                return false;
            vector<string> vWords;
            ParseString(strLine, ' ', vWords);
            if (vWords.size() >= 1 && vWords[0] == "PING")
            {
                strLine[1] = 'O';
                strLine += '\r';
                Send(hSocket, strLine.c_str());
                continue;
            }
        }
        return fRet;
    }
}

int RecvUntil(SOCKET hSocket, const char* psz1, const char* psz2=NULL, const char* psz3=NULL, const char* psz4=NULL)
{
    loop
    {
        string strLine;
        strLine.reserve(10000);
        if (!RecvLineIRC(hSocket, strLine))
            return 0;
        printf("IRC %s\n", strLine.c_str());
        if (psz1 && strLine.find(psz1) != string::npos)
            return 1;
        if (psz2 && strLine.find(psz2) != string::npos)
            return 2;
        if (psz3 && strLine.find(psz3) != string::npos)
            return 3;
        if (psz4 && strLine.find(psz4) != string::npos)
            return 4;
    }
}

bool Wait(int nSeconds)
{
    if (fShutdown)
        return false;
    printf("IRC waiting %d seconds to reconnect\n", nSeconds);
    for (int i = 0; i < nSeconds; i++)
    {
        if (fShutdown)
            return false;
        Sleep(1000);
    }
    return true;
}

bool RecvCodeLine(SOCKET hSocket, const char* psz1, string& strRet)
{
    strRet.clear();
    loop
    {
        string strLine;
        if (!RecvLineIRC(hSocket, strLine))
            return false;

        vector<string> vWords;
        ParseString(strLine, ' ', vWords);
        if (vWords.size() < 2)
            continue;

        if (vWords[1] == psz1)
        {
            printf("IRC %s\n", strLine.c_str());
            strRet = strLine;
            return true;
        }
    }
}

bool GetIPFromIRC(SOCKET hSocket, string strMyName, CNetAddr& ipRet)
{
    Send(hSocket, strprintf("USERHOST %s\r", strMyName.c_str()).c_str());

    string strLine;
    if (!RecvCodeLine(hSocket, "302", strLine))
        return false;

    vector<string> vWords;
    ParseString(strLine, ' ', vWords);
    if (vWords.size() < 4)
        return false;

    string str = vWords[3];
    if (str.rfind("@") == string::npos)
        return false;
    string strHost = str.substr(str.rfind("@")+1);

    // Hybrid IRC used by lfnet always returns IP when you userhost yourself,
    // but in case another IRC is ever used this should work.
    printf("GetIPFromIRC() got userhost %s\n", strHost.c_str());
    CNetAddr addr(strHost, true);
    if (!addr.IsValid())
        return false;
    ipRet = addr;

    return true;
}

void ThreadIRCSeed(void* parg)
{
    IMPLEMENT_RANDOMIZE_STACK(ThreadIRCSeed(parg));

    // Make this thread recognisable as the IRC seeding thread
    RenameThread("bitcoin-ircseed");

    try
    {
        ThreadIRCSeed2(parg);
    }
    catch (std::exception& e) {
        PrintExceptionContinue(&e, "ThreadIRCSeed()");
    } catch (...) {
        PrintExceptionContinue(NULL, "ThreadIRCSeed()");
    }
    printf("ThreadIRCSeed exited\n");
}

void ThreadIRCSeed2(void* parg)
{
    /* Dont advertise on IRC if we don't allow incoming connections */
    if (mapArgs.count("-connect") || fNoListen)
        return;

    if (!GetBoolArg("-irc", false))
        return;

    printf("ThreadIRCSeed started\n");
    int nErrorWait = 10;
    int nRetryWait = 10;

    while (!fShutdown)
    {
        CService addrConnect("92.243.23.21", 6667); // irc.lfnet.org

        CService addrIRC("irc.lfnet.org", 6667, true);
        if (addrIRC.IsValid())
            addrConnect = addrIRC;

        SOCKET hSocket;
        if (!ConnectSocket(addrConnect, hSocket))
        {
            printf("IRC connect failed\n");
            nErrorWait = nErrorWait * 11 / 10;
            if (Wait(nErrorWait += 60))
                continue;
            else
                return;
        }

        if (!RecvUntil(hSocket, "Found your hostname", "using your IP address instead", "Couldn't look up your hostname", "ignoring hostname"))
        {
            closesocket(hSocket);
            hSocket = INVALID_SOCKET;
            nErrorWait = nErrorWait * 11 / 10;
            if (Wait(nErrorWait += 60))
                continue;
            else
                return;
        }

        CNetAddr addrIPv4("1.2.3.4"); // arbitrary IPv4 address to make GetLocal prefer IPv4 addresses
        CService addrLocal;
        string strMyName;
        if (GetLocal(addrLocal, &addrIPv4))
            strMyName = EncodeAddress(GetLocalAddress(&addrConnect));
        if (strMyName == "")
            strMyName = strprintf("x%u", GetRand(1000000000));

        Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
        Send(hSocket, strprintf("USER %s 8 * : %s\r", strMyName.c_str(), strMyName.c_str()).c_str());

        int nRet = RecvUntil(hSocket, " 004 ", " 433 ");
        if (nRet != 1)
        {
            closesocket(hSocket);
            hSocket = INVALID_SOCKET;
            if (nRet == 2)
            {
                printf("IRC name already in use\n");
                Wait(10);
                continue;
            }
            nErrorWait = nErrorWait * 11 / 10;
            if (Wait(nErrorWait += 60))
                continue;
            else
                return;
        }
        Sleep(500);

        // Get our external IP from the IRC server and re-nick before joining the channel
        CNetAddr addrFromIRC;
        if (GetIPFromIRC(hSocket, strMyName, addrFromIRC))
        {
            printf("GetIPFromIRC() returned %s\n", addrFromIRC.ToString().c_str());
            if (addrFromIRC.IsRoutable())
            {
                // IRC lets you to re-nick
                AddLocal(addrFromIRC, LOCAL_IRC);
                strMyName = EncodeAddress(GetLocalAddress(&addrConnect));
                Send(hSocket, strprintf("NICK %s\r", strMyName.c_str()).c_str());
            }
        }

        if (fTestNet) {
            Send(hSocket, "JOIN #dogecoinTEST3\r");
            Send(hSocket, "WHO #dogecoinTEST3\r");
        } else {
            // randomly join #dogecoin00-#dogecoin99
            // network is now over 3k peers , get them to join 50 random channels!
            //            channel_number = 0;
            int channel_number = GetRandInt(50);

            Send(hSocket, strprintf("JOIN #dogecoin%02d\r", channel_number).c_str());
            Send(hSocket, strprintf("WHO #dogecoin%02d\r", channel_number).c_str());
        }

        int64 nStart = GetTime();
        string strLine;
        strLine.reserve(10000);
        while (!fShutdown && RecvLineIRC(hSocket, strLine))
        {
            if (strLine.empty() || strLine.size() > 900 || strLine[0] != ':')
                continue;

            vector<string> vWords;
            ParseString(strLine, ' ', vWords);
            if (vWords.size() < 2)
                continue;

            char pszName[10000];
            pszName[0] = '\0';

            if (vWords[1] == "352" && vWords.size() >= 8)
            {
                // index 7 is limited to 16 characters
                // could get full length name at index 10, but would be different from join messages
                strlcpy(pszName, vWords[7].c_str(), sizeof(pszName));
                printf("IRC got who\n");
            }

            if (vWords[1] == "JOIN" && vWords[0].size() > 1)
            {
                // :username!username@50000007.F000000B.90000002.IP JOIN :#channelname
                strlcpy(pszName, vWords[0].c_str() + 1, sizeof(pszName));
                if (strchr(pszName, '!'))
                    *strchr(pszName, '!') = '\0';
                printf("IRC got join\n");
            }

            if (pszName[0] == 'u')
            {
                CAddress addr;
                if (DecodeAddress(pszName, addr))
                {
                    addr.nTime = GetAdjustedTime();
                    if (addrman.Add(addr, addrConnect, 51 * 60))
                        printf("IRC got new address: %s\n", addr.ToString().c_str());
                    nGotIRCAddresses++;
                }
                else
                {
                    printf("IRC decode failed\n");
                }
            }
        }
        closesocket(hSocket);
        hSocket = INVALID_SOCKET;

        if (GetTime() - nStart > 20 * 60)
        {
            nErrorWait /= 3;
            nRetryWait /= 3;
        }

        nRetryWait = nRetryWait * 11 / 10;
        if (!Wait(nRetryWait += 60))
            return;
    }
}

#ifdef TEST
int main(int argc, char *argv[])
{
    WSADATA wsadata;
    if (WSAStartup(MAKEWORD(2,2), &wsadata) != NO_ERROR)
    {
        printf("Error at WSAStartup()\n");
        return false;
    }

    ThreadIRCSeed(NULL);

    WSACleanup();
    return 0;
}
#endif
<|endoftext|>


<commit_before>#include "RuntimeManager.h"

#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"

#include "Stack.h"
#include "Utils.h"

namespace dev
{
namespace eth
{
namespace jit
{

llvm::StructType* RuntimeManager::getRuntimeDataType()
{
	static llvm::StructType* type = nullptr;
	if (!type)
	{
		llvm::Type* elems[] =
		{
			Type::Size,		// gas
			Type::Size,		// gasPrice
			Type::BytePtr,	// callData
			Type::Size,		// callDataSize
			Type::Word,		// address
			Type::Word,		// caller
			Type::Word,		// origin
			Type::Word,		// callValue
			Type::Word,		// coinBase
			Type::Word,		// difficulty
			Type::Word,		// gasLimit
			Type::Size,		// blockNumber
			Type::Size,		// blockTimestamp
			Type::BytePtr,	// code
			Type::Size,		// codeSize
		};
		type = llvm::StructType::create(elems, "RuntimeData");
	}
	return type;
}

llvm::StructType* RuntimeManager::getRuntimeType()
{
	static llvm::StructType* type = nullptr;
	if (!type)
	{
		llvm::Type* elems[] =
		{
			Type::RuntimeDataPtr,	// data
			Type::EnvPtr,			// Env*
			Array::getType()		// memory
		};
		type = llvm::StructType::create(elems, "Runtime");
	}
	return type;
}

namespace
{
llvm::Twine getName(RuntimeData::Index _index)
{
	switch (_index)
	{
	default:						return "";
	case RuntimeData::Gas:			return "msg.gas";
	case RuntimeData::GasPrice:		return "tx.gasprice";
	case RuntimeData::CallData:		return "msg.data.ptr";
	case RuntimeData::CallDataSize:	return "msg.data.size";
	case RuntimeData::Address:		return "this.address";
	case RuntimeData::Caller:		return "msg.caller";
	case RuntimeData::Origin:		return "tx.origin";
	case RuntimeData::CallValue:	return "msg.value";
	case RuntimeData::CoinBase:		return "block.coinbase";
	case RuntimeData::Difficulty:	return "block.difficulty";
	case RuntimeData::GasLimit:		return "block.gaslimit";
	case RuntimeData::Number:		return "block.number";
	case RuntimeData::Timestamp:	return "block.timestamp";
	case RuntimeData::Code:			return "code.ptr";
	case RuntimeData::CodeSize:		return "code.size";
	}
}
}

RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeBegin, code_iterator _codeEnd):
	CompilerHelper(_builder),
	m_codeBegin(_codeBegin),
	m_codeEnd(_codeEnd)
{
	m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);

	// Unpack data
	auto rtPtr = getRuntimePtr();
	m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr");
	assert(m_dataPtr->getType() == Type::RuntimeDataPtr);
	m_gasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), m_dataPtr, 0, "gasPtr");
	assert(m_gasPtr->getType() == Type::Gas->getPointerTo());
	m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem");
	assert(m_memPtr->getType() == Array::getType()->getPointerTo());
	m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env");
	assert(m_envPtr->getType() == Type::EnvPtr);

	m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize");
	m_builder.CreateStore(m_builder.getInt64(0), m_stackSize);

	auto data = m_builder.CreateLoad(m_dataPtr, "data");
	for (unsigned i = 0; i < m_dataElts.size(); ++i)
		m_dataElts[i] = m_builder.CreateExtractValue(data, i, getName(RuntimeData::Index(i)));

	llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr};
	m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule());
	m_checkStackLimit->setDoesNotThrow();
	m_checkStackLimit->setDoesNotCapture(1);

	auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_checkStackLimit);
	auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_checkStackLimit);
	auto outOfStackBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfStack", m_checkStackLimit);

	auto currSizePtr = &m_checkStackLimit->getArgumentList().front();
	currSizePtr->setName("currSize");
	auto max = currSizePtr->getNextNode();
	max->setName("max");
	auto diff = max->getNextNode();
	diff->setName("diff");
	auto jmpBuf = diff->getNextNode();
	jmpBuf->setName("jmpBuf");

	InsertPointGuard guard{m_builder};
	m_builder.SetInsertPoint(checkBB);
	auto currSize = m_builder.CreateLoad(currSizePtr, "cur");
	auto maxSize = m_builder.CreateNUWAdd(currSize, max, "maxSize");
	auto ok = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(1024), "ok");
	m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue);

	m_builder.SetInsertPoint(updateBB);
	auto newSize = m_builder.CreateNSWAdd(currSize, diff);
	m_builder.CreateStore(newSize, currSizePtr);
	m_builder.CreateRetVoid();

	m_builder.SetInsertPoint(outOfStackBB);
	abort(jmpBuf);
	m_builder.CreateUnreachable();
}

void RuntimeManager::checkStackLimit(size_t _max, int _diff)
{
	createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()});
}

llvm::Value* RuntimeManager::getRuntimePtr()
{
	// Expect first argument of a function to be a pointer to Runtime
	auto func = m_builder.GetInsertBlock()->getParent();
	auto rtPtr = &func->getArgumentList().front();
	assert(rtPtr->getType() == Type::RuntimePtr);
	return rtPtr;
}

llvm::Value* RuntimeManager::getDataPtr()
{
	if (getMainFunction())
		return m_dataPtr;

	auto rtPtr = getRuntimePtr();
	auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data");
	assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo());
	return dataPtr;
}

llvm::Value* RuntimeManager::getEnvPtr()
{
	assert(getMainFunction());	// Available only in main function
	return m_envPtr;
}

llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index)
{
	auto ptr = getBuilder().CreateStructGEP(getRuntimeDataType(), getDataPtr(), _index);
	assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType());
	return ptr;
}

llvm::Value* RuntimeManager::get(RuntimeData::Index _index)
{
	return m_dataElts[_index];
}

void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value)
{
	auto ptr = getPtr(_index);
	assert(ptr->getType() == _value->getType()->getPointerTo());
	getBuilder().CreateStore(_value, ptr);
}

void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size)
{
	auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo());
	auto mem = getBuilder().CreateLoad(memPtr, "memory");
	auto returnDataPtr = getBuilder().CreateGEP(mem, _offset);
	set(RuntimeData::ReturnData, returnDataPtr);

	auto size64 = getBuilder().CreateTrunc(_size, Type::Size);
	set(RuntimeData::ReturnDataSize, size64);
}

void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress)
{
	set(RuntimeData::SuicideDestAddress, _balanceAddress);
}

void RuntimeManager::exit(ReturnCode _returnCode)
{
	if (m_stack)
		m_stack->free();

	m_builder.CreateRet(Constant::get(_returnCode));
}

void RuntimeManager::abort(llvm::Value* _jmpBuf)
{
	auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);
	createCall(longjmp, {_jmpBuf});
}

llvm::Value* RuntimeManager::get(Instruction _inst)
{
	switch (_inst)
	{
	default: assert(false); return nullptr;
	case Instruction::ADDRESS:		return get(RuntimeData::Address);
	case Instruction::CALLER:		return get(RuntimeData::Caller);
	case Instruction::ORIGIN:		return get(RuntimeData::Origin);
	case Instruction::CALLVALUE:	return get(RuntimeData::CallValue);
	case Instruction::GASPRICE:		return get(RuntimeData::GasPrice);
	case Instruction::COINBASE:		return get(RuntimeData::CoinBase);
	case Instruction::DIFFICULTY:	return get(RuntimeData::Difficulty);
	case Instruction::GASLIMIT:		return get(RuntimeData::GasLimit);
	case Instruction::NUMBER:		return get(RuntimeData::Number);
	case Instruction::TIMESTAMP:	return get(RuntimeData::Timestamp);
	}
}

llvm::Value* RuntimeManager::getCallData()
{
	return get(RuntimeData::CallData);
}

llvm::Value* RuntimeManager::getCode()
{
	// OPT Check what is faster
	//return get(RuntimeData::Code);
	return m_builder.CreateGlobalStringPtr({reinterpret_cast<char const*>(m_codeBegin), static_cast<size_t>(m_codeEnd - m_codeBegin)}, "code");
}

llvm::Value* RuntimeManager::getCodeSize()
{
	return Constant::get(m_codeEnd - m_codeBegin);
}

llvm::Value* RuntimeManager::getCallDataSize()
{
	auto value = get(RuntimeData::CallDataSize);
	assert(value->getType() == Type::Size);
	return getBuilder().CreateZExt(value, Type::Word);
}

llvm::Value* RuntimeManager::getGas()
{
	return getBuilder().CreateLoad(getGasPtr(), "gas");
}

llvm::Value* RuntimeManager::getGasPtr()
{
	assert(getMainFunction());
	return m_gasPtr;
}

llvm::Value* RuntimeManager::getMem()
{
	assert(getMainFunction());
	return m_memPtr;
}

void RuntimeManager::setGas(llvm::Value* _gas)
{
	assert(_gas->getType() == Type::Gas);
	set(RuntimeData::Gas, _gas);
}

}
}
}
<commit_msg>Copy gas counter to local function stack (alloca)<commit_after>#include "RuntimeManager.h"

#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"

#include "Stack.h"
#include "Utils.h"

namespace dev
{
namespace eth
{
namespace jit
{

llvm::StructType* RuntimeManager::getRuntimeDataType()
{
	static llvm::StructType* type = nullptr;
	if (!type)
	{
		llvm::Type* elems[] =
		{
			Type::Size,		// gas
			Type::Size,		// gasPrice
			Type::BytePtr,	// callData
			Type::Size,		// callDataSize
			Type::Word,		// address
			Type::Word,		// caller
			Type::Word,		// origin
			Type::Word,		// callValue
			Type::Word,		// coinBase
			Type::Word,		// difficulty
			Type::Word,		// gasLimit
			Type::Size,		// blockNumber
			Type::Size,		// blockTimestamp
			Type::BytePtr,	// code
			Type::Size,		// codeSize
		};
		type = llvm::StructType::create(elems, "RuntimeData");
	}
	return type;
}

llvm::StructType* RuntimeManager::getRuntimeType()
{
	static llvm::StructType* type = nullptr;
	if (!type)
	{
		llvm::Type* elems[] =
		{
			Type::RuntimeDataPtr,	// data
			Type::EnvPtr,			// Env*
			Array::getType()		// memory
		};
		type = llvm::StructType::create(elems, "Runtime");
	}
	return type;
}

namespace
{
llvm::Twine getName(RuntimeData::Index _index)
{
	switch (_index)
	{
	default:						return "";
	case RuntimeData::Gas:			return "msg.gas";
	case RuntimeData::GasPrice:		return "tx.gasprice";
	case RuntimeData::CallData:		return "msg.data.ptr";
	case RuntimeData::CallDataSize:	return "msg.data.size";
	case RuntimeData::Address:		return "this.address";
	case RuntimeData::Caller:		return "msg.caller";
	case RuntimeData::Origin:		return "tx.origin";
	case RuntimeData::CallValue:	return "msg.value";
	case RuntimeData::CoinBase:		return "block.coinbase";
	case RuntimeData::Difficulty:	return "block.difficulty";
	case RuntimeData::GasLimit:		return "block.gaslimit";
	case RuntimeData::Number:		return "block.number";
	case RuntimeData::Timestamp:	return "block.timestamp";
	case RuntimeData::Code:			return "code.ptr";
	case RuntimeData::CodeSize:		return "code.size";
	}
}
}

RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeBegin, code_iterator _codeEnd):
	CompilerHelper(_builder),
	m_codeBegin(_codeBegin),
	m_codeEnd(_codeEnd)
{
	m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);

	// Unpack data
	auto rtPtr = getRuntimePtr();
	m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr");
	assert(m_dataPtr->getType() == Type::RuntimeDataPtr);
	m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem");
	assert(m_memPtr->getType() == Array::getType()->getPointerTo());
	m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env");
	assert(m_envPtr->getType() == Type::EnvPtr);

	m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize");
	m_builder.CreateStore(m_builder.getInt64(0), m_stackSize);

	auto data = m_builder.CreateLoad(m_dataPtr, "data");
	for (unsigned i = 0; i < m_dataElts.size(); ++i)
		m_dataElts[i] = m_builder.CreateExtractValue(data, i, getName(RuntimeData::Index(i)));

	m_gasPtr = m_builder.CreateAlloca(Type::Gas, nullptr, "gas.ptr");
	m_builder.CreateStore(m_dataElts[RuntimeData::Index::Gas], m_gasPtr);

	llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr};
	m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule());
	m_checkStackLimit->setDoesNotThrow();
	m_checkStackLimit->setDoesNotCapture(1);

	auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_checkStackLimit);
	auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_checkStackLimit);
	auto outOfStackBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfStack", m_checkStackLimit);

	auto currSizePtr = &m_checkStackLimit->getArgumentList().front();
	currSizePtr->setName("currSize");
	auto max = currSizePtr->getNextNode();
	max->setName("max");
	auto diff = max->getNextNode();
	diff->setName("diff");
	auto jmpBuf = diff->getNextNode();
	jmpBuf->setName("jmpBuf");

	InsertPointGuard guard{m_builder};
	m_builder.SetInsertPoint(checkBB);
	auto currSize = m_builder.CreateLoad(currSizePtr, "cur");
	auto maxSize = m_builder.CreateNUWAdd(currSize, max, "maxSize");
	auto ok = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(1024), "ok");
	m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue);

	m_builder.SetInsertPoint(updateBB);
	auto newSize = m_builder.CreateNSWAdd(currSize, diff);
	m_builder.CreateStore(newSize, currSizePtr);
	m_builder.CreateRetVoid();

	m_builder.SetInsertPoint(outOfStackBB);
	abort(jmpBuf);
	m_builder.CreateUnreachable();
}

void RuntimeManager::checkStackLimit(size_t _max, int _diff)
{
	createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()});
}

llvm::Value* RuntimeManager::getRuntimePtr()
{
	// Expect first argument of a function to be a pointer to Runtime
	auto func = m_builder.GetInsertBlock()->getParent();
	auto rtPtr = &func->getArgumentList().front();
	assert(rtPtr->getType() == Type::RuntimePtr);
	return rtPtr;
}

llvm::Value* RuntimeManager::getDataPtr()
{
	if (getMainFunction())
		return m_dataPtr;

	auto rtPtr = getRuntimePtr();
	auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data");
	assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo());
	return dataPtr;
}

llvm::Value* RuntimeManager::getEnvPtr()
{
	assert(getMainFunction());	// Available only in main function
	return m_envPtr;
}

llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index)
{
	auto ptr = getBuilder().CreateStructGEP(getRuntimeDataType(), getDataPtr(), _index);
	assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType());
	return ptr;
}

llvm::Value* RuntimeManager::get(RuntimeData::Index _index)
{
	return m_dataElts[_index];
}

void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value)
{
	auto ptr = getPtr(_index);
	assert(ptr->getType() == _value->getType()->getPointerTo());
	getBuilder().CreateStore(_value, ptr);
}

void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size)
{
	auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo());
	auto mem = getBuilder().CreateLoad(memPtr, "memory");
	auto returnDataPtr = getBuilder().CreateGEP(mem, _offset);
	set(RuntimeData::ReturnData, returnDataPtr);

	auto size64 = getBuilder().CreateTrunc(_size, Type::Size);
	set(RuntimeData::ReturnDataSize, size64);
}

void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress)
{
	set(RuntimeData::SuicideDestAddress, _balanceAddress);
}

void RuntimeManager::exit(ReturnCode _returnCode)
{
	if (m_stack)
		m_stack->free();

	auto extGasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), getDataPtr(), RuntimeData::Index::Gas, "msg.gas.ptr");
	m_builder.CreateStore(getGas(), extGasPtr);
	m_builder.CreateRet(Constant::get(_returnCode));
}

void RuntimeManager::abort(llvm::Value* _jmpBuf)
{
	auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);
	createCall(longjmp, {_jmpBuf});
}

llvm::Value* RuntimeManager::get(Instruction _inst)
{
	switch (_inst)
	{
	default: assert(false); return nullptr;
	case Instruction::ADDRESS:		return get(RuntimeData::Address);
	case Instruction::CALLER:		return get(RuntimeData::Caller);
	case Instruction::ORIGIN:		return get(RuntimeData::Origin);
	case Instruction::CALLVALUE:	return get(RuntimeData::CallValue);
	case Instruction::GASPRICE:		return get(RuntimeData::GasPrice);
	case Instruction::COINBASE:		return get(RuntimeData::CoinBase);
	case Instruction::DIFFICULTY:	return get(RuntimeData::Difficulty);
	case Instruction::GASLIMIT:		return get(RuntimeData::GasLimit);
	case Instruction::NUMBER:		return get(RuntimeData::Number);
	case Instruction::TIMESTAMP:	return get(RuntimeData::Timestamp);
	}
}

llvm::Value* RuntimeManager::getCallData()
{
	return get(RuntimeData::CallData);
}

llvm::Value* RuntimeManager::getCode()
{
	// OPT Check what is faster
	//return get(RuntimeData::Code);
	return m_builder.CreateGlobalStringPtr({reinterpret_cast<char const*>(m_codeBegin), static_cast<size_t>(m_codeEnd - m_codeBegin)}, "code");
}

llvm::Value* RuntimeManager::getCodeSize()
{
	return Constant::get(m_codeEnd - m_codeBegin);
}

llvm::Value* RuntimeManager::getCallDataSize()
{
	auto value = get(RuntimeData::CallDataSize);
	assert(value->getType() == Type::Size);
	return getBuilder().CreateZExt(value, Type::Word);
}

llvm::Value* RuntimeManager::getGas()
{
	return getBuilder().CreateLoad(getGasPtr(), "gas");
}

llvm::Value* RuntimeManager::getGasPtr()
{
	assert(getMainFunction());
	return m_gasPtr;
}

llvm::Value* RuntimeManager::getMem()
{
	assert(getMainFunction());
	return m_memPtr;
}

void RuntimeManager::setGas(llvm::Value* _gas)
{
	assert(_gas->getType() == Type::Gas);
	getBuilder().CreateStore(_gas, getGasPtr());
}

}
}
}
<|endoftext|>


<commit_before>//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#include "MaxQpsThread.h"
#include "FEProblem.h"
#include "Assembly.h"

#include "libmesh/fe_base.h"
#include "libmesh/threads.h"
#include LIBMESH_INCLUDE_UNORDERED_SET
LIBMESH_DEFINE_HASH_POINTERS
#include "libmesh/quadrature.h"

MaxQpsThread::MaxQpsThread(FEProblemBase & fe_problem)
  : _fe_problem(fe_problem), _max(0), _max_shape_funcs(0)
{
}

// Splitting Constructor
MaxQpsThread::MaxQpsThread(MaxQpsThread & x, Threads::split /*split*/)
  : _fe_problem(x._fe_problem),
    _max(x._max),
    _max_shape_funcs(x._max_shape_funcs)
{
}

void
MaxQpsThread::operator()(const ConstElemRange & range)
{
  ParallelUniqueId puid;
  _tid = puid.id;

  auto & assem = _fe_problem.assembly(_tid);

  // For short circuiting reinit.  With potential block-specific qrules we
  // need to track "seen" element types by their subdomains as well.
  std::set<std::pair<ElemType, SubdomainID>> seen_it;

  for (const auto & elem : range)
  {
    // Only reinit if the element type has not previously been seen
    if (!seen_it.insert(std::make_pair(elem->type(), elem->subdomain_id())).second)
      continue;

    // This ensures we can access the correct qrules if any block-specific
    // qrules have been created.
    assem.setCurrentSubdomainID(elem->subdomain_id());

    FEType fe_type(FIRST, LAGRANGE);
    unsigned int dim = elem->dim();
    unsigned int side = 0; // we assume that any element will have at least one side ;)

    // We cannot mess with the FE objects in Assembly, because we might need to request second
    // derivatives
    // later on. If we used them, we'd call reinit on them, thus making the call to request second
    // derivatives harmful (i.e. leading to segfaults/asserts). Thus, we have to use a locally
    // allocated object here.
    //
    // We'll use one for element interiors, which calculates nothing
    std::unique_ptr<FEBase> fe(FEBase::build(dim, fe_type));
    fe->get_nothing();

    // And another for element sides, which calculates the minimum
    // libMesh currently allows for that
    std::unique_ptr<FEBase> side_fe(FEBase::build(dim, fe_type));
    side_fe->get_xyz();

    // figure out the number of qps for volume
    auto qrule = assem.attachQRuleElem(dim, *fe);
    fe->reinit(elem);
    if (qrule->n_points() > _max)
      _max = qrule->n_points();

    unsigned int n_shape_funcs = fe->n_shape_functions();
    if (n_shape_funcs > _max_shape_funcs)
      _max_shape_funcs = n_shape_funcs;

    // figure out the number of qps for the face
    // NOTE: user might specify higher order rule for faces, thus possibly ending up with more qps
    // than in the volume
    auto qrule_face = assem.attachQRuleFace(dim, *side_fe);
    side_fe->reinit(elem, side);
    if (qrule_face->n_points() > _max)
      _max = qrule_face->n_points();

    // In initial conditions nodes are enumerated as pretend quadrature points
    // using the _qp index to access coupled variables. In order to be able to
    // use _zero (resized according to _max_qps) with _qp, we need to count nodes.
    if (elem->n_nodes() > _max)
      _max = elem->n_nodes();
  }
}

void
MaxQpsThread::join(const MaxQpsThread & y)
{
  if (y._max > _max)
    _max = y._max;

  if (y._max_shape_funcs > _max_shape_funcs)
    _max_shape_funcs = y._max_shape_funcs;
}
<commit_msg>Fix MaxQpsThread on meshes with NodeElems<commit_after>//* This file is part of the MOOSE framework
//* https://www.mooseframework.org
//*
//* All rights reserved, see COPYRIGHT for full restrictions
//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
//*
//* Licensed under LGPL 2.1, please see LICENSE for details
//* https://www.gnu.org/licenses/lgpl-2.1.html

#include "MaxQpsThread.h"
#include "FEProblem.h"
#include "Assembly.h"

#include "libmesh/fe_base.h"
#include "libmesh/threads.h"
#include LIBMESH_INCLUDE_UNORDERED_SET
LIBMESH_DEFINE_HASH_POINTERS
#include "libmesh/quadrature.h"

MaxQpsThread::MaxQpsThread(FEProblemBase & fe_problem)
  : _fe_problem(fe_problem), _max(0), _max_shape_funcs(0)
{
}

// Splitting Constructor
MaxQpsThread::MaxQpsThread(MaxQpsThread & x, Threads::split /*split*/)
  : _fe_problem(x._fe_problem),
    _max(x._max),
    _max_shape_funcs(x._max_shape_funcs)
{
}

void
MaxQpsThread::operator()(const ConstElemRange & range)
{
  ParallelUniqueId puid;
  _tid = puid.id;

  auto & assem = _fe_problem.assembly(_tid);

  // For short circuiting reinit.  With potential block-specific qrules we
  // need to track "seen" element types by their subdomains as well.
  std::set<std::pair<ElemType, SubdomainID>> seen_it;

  for (const auto & elem : range)
  {
    // Only reinit if the element type has not previously been seen
    if (!seen_it.insert(std::make_pair(elem->type(), elem->subdomain_id())).second)
      continue;

    // This ensures we can access the correct qrules if any block-specific
    // qrules have been created.
    assem.setCurrentSubdomainID(elem->subdomain_id());

    FEType fe_type(FIRST, LAGRANGE);
    unsigned int dim = elem->dim();
    unsigned int side = 0; // we assume that any element will have at least one side ;)

    // We cannot mess with the FE objects in Assembly, because we might need to request second
    // derivatives
    // later on. If we used them, we'd call reinit on them, thus making the call to request second
    // derivatives harmful (i.e. leading to segfaults/asserts). Thus, we have to use a locally
    // allocated object here.
    //
    // We'll use one for element interiors, which calculates nothing
    std::unique_ptr<FEBase> fe(FEBase::build(dim, fe_type));
    fe->get_nothing();

    // And another for element sides, which calculates the minimum
    // libMesh currently allows for that
    std::unique_ptr<FEBase> side_fe(FEBase::build(dim, fe_type));
    side_fe->get_xyz();

    // figure out the number of qps for volume
    auto qrule = assem.attachQRuleElem(dim, *fe);
    fe->reinit(elem);
    if (qrule->n_points() > _max)
      _max = qrule->n_points();

    unsigned int n_shape_funcs = fe->n_shape_functions();
    if (n_shape_funcs > _max_shape_funcs)
      _max_shape_funcs = n_shape_funcs;

    // figure out the number of qps for the face
    // NOTE: user might specify higher order rule for faces, thus possibly ending up with more qps
    // than in the volume
    auto qrule_face = assem.attachQRuleFace(dim, *side_fe);
    if (dim > 0) // side reinit in 0D makes no sense, but we may have NodeElems
    {
      side_fe->reinit(elem, side);
      if (qrule_face->n_points() > _max)
        _max = qrule_face->n_points();
    }

    // In initial conditions nodes are enumerated as pretend quadrature points
    // using the _qp index to access coupled variables. In order to be able to
    // use _zero (resized according to _max_qps) with _qp, we need to count nodes.
    if (elem->n_nodes() > _max)
      _max = elem->n_nodes();
  }
}

void
MaxQpsThread::join(const MaxQpsThread & y)
{
  if (y._max > _max)
    _max = y._max;

  if (y._max_shape_funcs > _max_shape_funcs)
    _max_shape_funcs = y._max_shape_funcs;
}
<|endoftext|>


<commit_before>/****************************************************************/
/*               DO NOT MODIFY THIS HEADER                      */
/* MOOSE - Multiphysics Object Oriented Simulation Environment  */
/*                                                              */
/*           (c) 2010 Battelle Energy Alliance, LLC             */
/*                   ALL RIGHTS RESERVED                        */
/*                                                              */
/*          Prepared by Battelle Energy Alliance, LLC           */
/*            Under Contract No. DE-AC07-05ID14517              */
/*            With the U. S. Department of Energy               */
/*                                                              */
/*            See COPYRIGHT for full restrictions               */
/****************************************************************/
#include "MultiApp.h"

// Moose
#include "AppFactory.h"
#include "SetupInterface.h"
#include "Executioner.h"
#include "UserObject.h"
#include "FEProblem.h"
#include "Output.h"
#include "AppFactory.h"

// libMesh
#include "libmesh/mesh_tools.h"

#include <iostream>
#include <iomanip>

template<>
InputParameters validParams<MultiApp>()
{
  InputParameters params = validParams<MooseObject>();

  params.addPrivateParam<bool>("use_displaced_mesh", false);

  std::ostringstream app_types_strings;

  registeredMooseAppIterator it = AppFactory::instance()->registeredObjectsBegin();
  while(it != AppFactory::instance()->registeredObjectsEnd())
  {
    app_types_strings << it->first;
    ++it;
    if(it != AppFactory::instance()->registeredObjectsEnd())
      app_types_strings<< ", ";
  }

  MooseEnum app_types_options(app_types_strings.str());

  params.addRequiredParam<MooseEnum>("app_type", app_types_options, "The type of application to build.");
  params.addRequiredParam<std::vector<Real> >("positions", "The positions of the App locations.  Each set of 3 values will represent a Point.");
  params.addRequiredParam<std::vector<std::string> >("input_files", "The input file for each App.  If this parameter only contains one input file it will be used for all of the Apps.");

  params.addPrivateParam<MPI_Comm>("_mpi_comm");

  MooseEnum execute_options(SetupInterface::getExecuteOptions());
  execute_options = "timestep_begin";  // set the default

  params.addParam<MooseEnum>("execute_on", execute_options, "Set to (residual|jacobian|timestep|timestep_begin|custom) to execute only at that moment");

  params.addPrivateParam<std::string>("built_by_action", "add_multi_app");

  return params;
}

MultiApp::MultiApp(const std::string & name, InputParameters parameters):
    MooseObject(name, parameters),
    _fe_problem(getParam<FEProblem *>("_fe_problem")),
    _app_type(getParam<MooseEnum>("app_type")),
    _positions_vec(getParam<std::vector<Real> >("positions")),
    _input_files(getParam<std::vector<std::string> >("input_files")),
    _orig_comm(getParam<MPI_Comm>("_mpi_comm")),
    _execute_on(getParam<MooseEnum>("execute_on"))
{
  { // Read the positions out of the vector
    unsigned int num_vec_entries = _positions_vec.size();

    mooseAssert(num_vec_entries % LIBMESH_DIM == 0, "Wrong number of entries in 'positions'");

    _positions.reserve(num_vec_entries / LIBMESH_DIM);

    for(unsigned int i=0; i<num_vec_entries; i+=3)
      _positions.push_back(Point(_positions_vec[i], _positions_vec[i+1], _positions_vec[i+2]));
  }

  _total_num_apps = _positions.size();
  mooseAssert(_input_files.size() == 1 || _positions.size() == _input_files.size(), "Number of positions and input files are not the same!");

  /// Set up our Comm and set the number of apps we're going to be working on
  buildComm();

  MPI_Comm swapped = Moose::swapLibMeshComm(_my_comm);

  _apps.resize(_my_num_apps);

  for(unsigned int i=0; i<_my_num_apps; i++)
  {
    InputParameters app_params = AppFactory::instance()->getValidParams(_app_type);
    MooseApp * app = AppFactory::instance()->create(_app_type, "multi_app", app_params);

    std::ostringstream output_base;

    // Create an output base by taking the output base of the master problem and appending
    // the name of the multiapp + a number to it
    output_base << _fe_problem->out().fileBase()
                << "_" << _name
                << std::setw(_total_num_apps/10)
                << std::setprecision(0)
                << std::setfill('0')
                << std::right
                << _first_local_app + i;

    _apps[i] = app;

    std::string input_file = "";
    if(_input_files.size() == 1) // If only one input file was provide, use it for all the solves
      input_file = _input_files[0];
    else
      input_file = _input_files[_first_local_app+i];

    app->setInputFileName(input_file);
    app->setOutputFileBase(output_base.str());
    app->setupOptions();
    app->runInputFile();
  }

  // Swap back
  Moose::swapLibMeshComm(swapped);
}

MultiApp::~MultiApp()
{
  for(unsigned int i=0; i<_my_num_apps; i++)
    delete _apps[i];
}

Executioner *
MultiApp::getExecutioner(unsigned int app)
{
  return _apps[globalAppToLocal(app)]->getExecutioner();
}

MeshTools::BoundingBox
MultiApp::getBoundingBox(unsigned int app)
{
  FEProblem * problem = appProblem(app);

  MPI_Comm swapped = Moose::swapLibMeshComm(_my_comm);

  MooseMesh & mesh = problem->mesh();
  MeshTools::BoundingBox bbox = MeshTools::bounding_box(mesh);

  Moose::swapLibMeshComm(swapped);

  return bbox;
}

FEProblem *
MultiApp::appProblem(unsigned int app)
{
  unsigned int local_app = globalAppToLocal(app);

  FEProblem * problem = dynamic_cast<FEProblem *>(&_apps[local_app]->getExecutioner()->problem());
  mooseAssert(problem, "Not an FEProblem!");

  return problem;
}

const UserObject &
MultiApp::appUserObjectBase(unsigned int app, const std::string & name)
{
  return appProblem(app)->getUserObjectBase(name);
}

Real
MultiApp::appPostprocessorValue(unsigned int app, const std::string & name)
{
  return appProblem(app)->getPostprocessorValue(name);
}

bool
MultiApp::hasLocalApp(unsigned int global_app)
{
  if(global_app >= _first_local_app && global_app <= _first_local_app + (_my_num_apps-1))
    return true;

  return false;
}

void
MultiApp::buildComm()
{
  MPI_Comm_size(_orig_comm, (int*)&_orig_num_procs);
  MPI_Comm_rank(_orig_comm, (int*)&_orig_rank);

  // If we have more apps than processors then we're just going to divide up the work
  if(_total_num_apps >= _orig_num_procs)
  {
    _my_comm = MPI_COMM_SELF;
    _my_rank = 0;

    _my_num_apps = _total_num_apps/_orig_num_procs;
    _first_local_app = _my_num_apps * _orig_rank;

    // The last processor will pick up any extra apps
    if(_orig_rank == _orig_num_procs - 1)
      _my_num_apps += _total_num_apps % _orig_num_procs;

    return;
  }

  // In this case we need to divide up the processors that are going to work on each app
  int rank;
  MPI_Comm_rank(_orig_comm, &rank);
//  sleep(rank);

  int procs_per_app = _orig_num_procs / _total_num_apps;
  int my_app = rank / procs_per_app;
  int procs_for_my_app = procs_per_app;

  if((unsigned int) my_app >= _total_num_apps-1) // The last app will gain any left-over procs
  {
    my_app = _total_num_apps - 1;
    procs_for_my_app += _orig_num_procs % _total_num_apps;
  }

  // Only one app here
  _first_local_app = my_app;
  _my_num_apps = 1;

  std::vector<int> ranks_in_my_group(procs_for_my_app);

  // Add all the processors in that are in my group
  for(int i=0; i<procs_for_my_app; i++)
    ranks_in_my_group[i] = (my_app * procs_per_app) + i;

  MPI_Group orig_group, new_group;

  // Extract the original group handle
  MPI_Comm_group(_orig_comm, &orig_group);

  // Create a group
  MPI_Group_incl(orig_group, procs_for_my_app, &ranks_in_my_group[0], &new_group);

  // Create new communicator
  MPI_Comm_create(_orig_comm, new_group, &_my_comm);
  MPI_Comm_rank(_my_comm, (int*)&_my_rank);
}

unsigned int
MultiApp::globalAppToLocal(unsigned int global_app)
{
  if(global_app >= _first_local_app && global_app <= _first_local_app + (_my_num_apps-1))
    return global_app-_first_local_app;

  std::cout<<_first_local_app<<" "<<global_app<<std::endl;
  mooseError("Invalid global_app!");
}

<commit_msg>allow reading positions from a file ref #1845<commit_after>/****************************************************************/
/*               DO NOT MODIFY THIS HEADER                      */
/* MOOSE - Multiphysics Object Oriented Simulation Environment  */
/*                                                              */
/*           (c) 2010 Battelle Energy Alliance, LLC             */
/*                   ALL RIGHTS RESERVED                        */
/*                                                              */
/*          Prepared by Battelle Energy Alliance, LLC           */
/*            Under Contract No. DE-AC07-05ID14517              */
/*            With the U. S. Department of Energy               */
/*                                                              */
/*            See COPYRIGHT for full restrictions               */
/****************************************************************/
#include "MultiApp.h"

// Moose
#include "AppFactory.h"
#include "SetupInterface.h"
#include "Executioner.h"
#include "UserObject.h"
#include "FEProblem.h"
#include "Output.h"
#include "AppFactory.h"

// libMesh
#include "libmesh/mesh_tools.h"

#include <iostream>
#include <iomanip>
#include <iterator>
#include <fstream>
#include <vector>
#include <algorithm>

template<>
InputParameters validParams<MultiApp>()
{
  InputParameters params = validParams<MooseObject>();

  params.addPrivateParam<bool>("use_displaced_mesh", false);

  std::ostringstream app_types_strings;

  registeredMooseAppIterator it = AppFactory::instance()->registeredObjectsBegin();
  while(it != AppFactory::instance()->registeredObjectsEnd())
  {
    app_types_strings << it->first;
    ++it;
    if(it != AppFactory::instance()->registeredObjectsEnd())
      app_types_strings<< ", ";
  }

  MooseEnum app_types_options(app_types_strings.str());

  params.addRequiredParam<MooseEnum>("app_type", app_types_options, "The type of application to build.");
  params.addParam<std::vector<Real> >("positions", "The positions of the App locations.  Each set of 3 values will represent a Point.  Either this must be supplied or 'positions_file'");
  params.addParam<FileName>("positions_file", "A filename that should be looked in for positions. Each set of 3 values in that file will represent a Point.  Either this must be supplied or 'positions'");

  params.addRequiredParam<std::vector<std::string> >("input_files", "The input file for each App.  If this parameter only contains one input file it will be used for all of the Apps.");

  params.addPrivateParam<MPI_Comm>("_mpi_comm");

  MooseEnum execute_options(SetupInterface::getExecuteOptions());
  execute_options = "timestep_begin";  // set the default

  params.addParam<MooseEnum>("execute_on", execute_options, "Set to (residual|jacobian|timestep|timestep_begin|custom) to execute only at that moment");

  params.addPrivateParam<std::string>("built_by_action", "add_multi_app");

  return params;
}

MultiApp::MultiApp(const std::string & name, InputParameters parameters):
    MooseObject(name, parameters),
    _fe_problem(getParam<FEProblem *>("_fe_problem")),
    _app_type(getParam<MooseEnum>("app_type")),
    _input_files(getParam<std::vector<std::string> >("input_files")),
    _orig_comm(getParam<MPI_Comm>("_mpi_comm")),
    _execute_on(getParam<MooseEnum>("execute_on"))
{
  if(isParamValid("positions"))
    _positions_vec = getParam<std::vector<Real> >("positions");
  else if(isParamValid("positions_file"))
  {
    // Read the file on the root processor then broadcast it
    if(libMesh::processor_id() == 0)
    {
      std::ifstream is(getParam<FileName>("positions_file").c_str());
      std::istream_iterator<Real> begin(is), end;
      _positions_vec.insert(_positions_vec.begin(), begin, end);
    }
    unsigned int num_values = _positions_vec.size();

    Parallel::broadcast(num_values);

    _positions_vec.resize(num_values);

    Parallel::broadcast(_positions_vec);
    std::cerr<<_positions_vec.size()<<std::endl;
  }
  else
    mooseError("Must supply either 'positions' or 'positions_file' for MultiApp "<<_name);

  { // Read the positions out of the vector
    unsigned int num_vec_entries = _positions_vec.size();

    mooseAssert(num_vec_entries % LIBMESH_DIM == 0, "Wrong number of entries in 'positions'");

    _positions.reserve(num_vec_entries / LIBMESH_DIM);

    for(unsigned int i=0; i<num_vec_entries; i+=3)
      _positions.push_back(Point(_positions_vec[i], _positions_vec[i+1], _positions_vec[i+2]));
  }

  _total_num_apps = _positions.size();
  mooseAssert(_input_files.size() == 1 || _positions.size() == _input_files.size(), "Number of positions and input files are not the same!");

  /// Set up our Comm and set the number of apps we're going to be working on
  buildComm();

  MPI_Comm swapped = Moose::swapLibMeshComm(_my_comm);

  _apps.resize(_my_num_apps);

  for(unsigned int i=0; i<_my_num_apps; i++)
  {
    InputParameters app_params = AppFactory::instance()->getValidParams(_app_type);
    MooseApp * app = AppFactory::instance()->create(_app_type, "multi_app", app_params);

    std::ostringstream output_base;

    // Create an output base by taking the output base of the master problem and appending
    // the name of the multiapp + a number to it
    output_base << _fe_problem->out().fileBase()
                << "_" << _name
                << std::setw(_total_num_apps/10)
                << std::setprecision(0)
                << std::setfill('0')
                << std::right
                << _first_local_app + i;

    _apps[i] = app;

    std::string input_file = "";
    if(_input_files.size() == 1) // If only one input file was provide, use it for all the solves
      input_file = _input_files[0];
    else
      input_file = _input_files[_first_local_app+i];

    app->setInputFileName(input_file);
    app->setOutputFileBase(output_base.str());
    app->setupOptions();
    app->runInputFile();
  }

  // Swap back
  Moose::swapLibMeshComm(swapped);
}

MultiApp::~MultiApp()
{
  for(unsigned int i=0; i<_my_num_apps; i++)
    delete _apps[i];
}

Executioner *
MultiApp::getExecutioner(unsigned int app)
{
  return _apps[globalAppToLocal(app)]->getExecutioner();
}

MeshTools::BoundingBox
MultiApp::getBoundingBox(unsigned int app)
{
  FEProblem * problem = appProblem(app);

  MPI_Comm swapped = Moose::swapLibMeshComm(_my_comm);

  MooseMesh & mesh = problem->mesh();
  MeshTools::BoundingBox bbox = MeshTools::bounding_box(mesh);

  Moose::swapLibMeshComm(swapped);

  return bbox;
}

FEProblem *
MultiApp::appProblem(unsigned int app)
{
  unsigned int local_app = globalAppToLocal(app);

  FEProblem * problem = dynamic_cast<FEProblem *>(&_apps[local_app]->getExecutioner()->problem());
  mooseAssert(problem, "Not an FEProblem!");

  return problem;
}

const UserObject &
MultiApp::appUserObjectBase(unsigned int app, const std::string & name)
{
  return appProblem(app)->getUserObjectBase(name);
}

Real
MultiApp::appPostprocessorValue(unsigned int app, const std::string & name)
{
  return appProblem(app)->getPostprocessorValue(name);
}

bool
MultiApp::hasLocalApp(unsigned int global_app)
{
  if(global_app >= _first_local_app && global_app <= _first_local_app + (_my_num_apps-1))
    return true;

  return false;
}

void
MultiApp::buildComm()
{
  MPI_Comm_size(_orig_comm, (int*)&_orig_num_procs);
  MPI_Comm_rank(_orig_comm, (int*)&_orig_rank);

  // If we have more apps than processors then we're just going to divide up the work
  if(_total_num_apps >= _orig_num_procs)
  {
    _my_comm = MPI_COMM_SELF;
    _my_rank = 0;

    _my_num_apps = _total_num_apps/_orig_num_procs;
    _first_local_app = _my_num_apps * _orig_rank;

    // The last processor will pick up any extra apps
    if(_orig_rank == _orig_num_procs - 1)
      _my_num_apps += _total_num_apps % _orig_num_procs;

    return;
  }

  // In this case we need to divide up the processors that are going to work on each app
  int rank;
  MPI_Comm_rank(_orig_comm, &rank);
//  sleep(rank);

  int procs_per_app = _orig_num_procs / _total_num_apps;
  int my_app = rank / procs_per_app;
  int procs_for_my_app = procs_per_app;

  if((unsigned int) my_app >= _total_num_apps-1) // The last app will gain any left-over procs
  {
    my_app = _total_num_apps - 1;
    procs_for_my_app += _orig_num_procs % _total_num_apps;
  }

  // Only one app here
  _first_local_app = my_app;
  _my_num_apps = 1;

  std::vector<int> ranks_in_my_group(procs_for_my_app);

  // Add all the processors in that are in my group
  for(int i=0; i<procs_for_my_app; i++)
    ranks_in_my_group[i] = (my_app * procs_per_app) + i;

  MPI_Group orig_group, new_group;

  // Extract the original group handle
  MPI_Comm_group(_orig_comm, &orig_group);

  // Create a group
  MPI_Group_incl(orig_group, procs_for_my_app, &ranks_in_my_group[0], &new_group);

  // Create new communicator
  MPI_Comm_create(_orig_comm, new_group, &_my_comm);
  MPI_Comm_rank(_my_comm, (int*)&_my_rank);
}

unsigned int
MultiApp::globalAppToLocal(unsigned int global_app)
{
  if(global_app >= _first_local_app && global_app <= _first_local_app + (_my_num_apps-1))
    return global_app-_first_local_app;

  std::cout<<_first_local_app<<" "<<global_app<<std::endl;
  mooseError("Invalid global_app!");
}

<|endoftext|>


<commit_before>#include "PetscSupport.h"

#ifdef LIBMESH_HAVE_PETSC

#include "Moose.h"
#include "MooseSystem.h"
#include "Executioner.h"

//libMesh Includes
#include "libmesh_common.h"
#include "equation_systems.h"
#include "nonlinear_implicit_system.h"
#include "linear_implicit_system.h"
#include "sparse_matrix.h"
#include "petsc_vector.h"
#include "petsc_matrix.h"
#include "petsc_linear_solver.h"
#include "petsc_preconditioner.h"

//PETSc includes
#include <petsc.h>
#include <petscsnes.h>
#include <petscksp.h>
#include <private/kspimpl.h>
#include <private/snesimpl.h>

namespace Moose
{
  namespace PetscSupport
  {
    /** The following functionality is encapsulated in the Moose ExecutionBlock but
     * still needed by Pronghorn for the time being
     */
    void petscParseOptions(GetPot & input_file)
    {
      // Set PETSC options:
      int num_petsc_opt = input_file.vector_variable_size("Execution/petsc_options");
      for(int i=0;i<num_petsc_opt;i++)
      {
        std::string petsc_opts = input_file("Execution/petsc_options","", i);
        PetscOptionsSetValue(petsc_opts.c_str(),PETSC_NULL);
      } // end of i-LOOP...

      int num_petsc_opt0 = input_file.vector_variable_size("Execution/petsc_options_iname");
      int num_petsc_opt1 = input_file.vector_variable_size("Execution/petsc_options_value");
      if(num_petsc_opt0 != num_petsc_opt1)
      {
        printf("Error in reading petsc_options_xxxxx\n");
        libmesh_error();
      }
      for(int i=0;i<num_petsc_opt0;i++)
      {
        std::string   petsc_opts_iname = input_file("Execution/petsc_options_iname", "", i);
        std::string   petsc_opts_value = input_file("Execution/petsc_options_value", "", i);
        PetscOptionsSetValue(petsc_opts_iname.c_str(),petsc_opts_value.c_str());
      } // end of i-LOOP...
    }

    PetscErrorCode  petscConverged(KSP ksp,PetscInt n,PetscReal rnorm,KSPConvergedReason *reason,void *dummy)
    {
      MooseSystem *moose_system = (MooseSystem *) dummy;      // C strikes

      *reason = KSP_CONVERGED_ITERATING;

      //If it's the beginning of a new set of iterations, reset last_rnorm
      if(!n)
        moose_system->_last_rnorm = 1e99;

      PetscReal norm_diff = std::fabs(rnorm - moose_system->_last_rnorm);

      if(norm_diff < moose_system->_l_abs_step_tol)
      {
        *reason = KSP_CONVERGED_RTOL;
        return(0);
      }

      moose_system->_last_rnorm = rnorm;

      // From here, we want the default behavior of the KSPDefaultConverged
      // test, but we don't want PETSc to die in that function with a
      // CHKERRQ call... therefore we Push/Pop a different error handler
      // and then call KSPDefaultConverged().  Finally, if we hit the
      // max iteration count, we want to set KSP_CONVERGED_ITS.
      PetscPushErrorHandler(PetscReturnErrorHandler,/* void* ctx= */PETSC_NULL);

      // As of PETSc 3.0.0, you must call KSPDefaultConverged with a
      // non-NULL context pointer which must be created with
      // KSPDefaultConvergedCreate(), and destroyed with
      // KSPDefaultConvergedDestroy().
      /*PetscErrorCode ierr = */
      KSPDefaultConverged(ksp, n, rnorm, reason, dummy);

      // Pop the Error handler we pushed on the stack to go back
      // to default PETSc error handling behavior.
      PetscPopErrorHandler();

      // If we hit max its then we consider that converged
      if (n >= ksp->max_it) *reason = KSP_CONVERGED_ITS;
      return 0;
    }

    PetscErrorCode petscNonlinearConverged(SNES snes,PetscInt it,PetscReal xnorm,PetscReal pnorm,PetscReal fnorm,SNESConvergedReason *reason,void * dummy)
    {
      MooseSystem *moose_system = (MooseSystem *) dummy;      // C strikes

      // unused
      // TransientNonlinearImplicitSystem * system = dynamic_cast<TransientNonlinearImplicitSystem *>(&_equation_system->get_system("NonlinearSystem"));

      //  for(unsigned int var = 0; var < system->n_vars(); var++)
      //    std::cout<<var<<": "<<system->calculate_norm(*system->rhs,var,DISCRETE_L2)<<std::endl;

      *reason = SNES_CONVERGED_ITERATING;

      if (!it)
      {
        /* set parameter for default relative tolerance convergence test */
        snes->ttol = fnorm*snes->rtol;
        moose_system->_initial_residual = fnorm;
      }
      if (fnorm != fnorm)
      {
        PetscInfo(snes,"Failed to converged, function norm is NaN\n");
        *reason = SNES_DIVERGED_FNORM_NAN;
      }
      else if (fnorm < snes->abstol)
      {
        PetscInfo2(snes,"Converged due to function norm %G < %G\n",fnorm,snes->abstol);
        *reason = SNES_CONVERGED_FNORM_ABS;
      }
      else if (snes->nfuncs >= snes->max_funcs)
      {
        PetscInfo2(snes,"Exceeded maximum number of function evaluations: %D > %D\n",snes->nfuncs,snes->max_funcs);
        *reason = SNES_DIVERGED_FUNCTION_COUNT;
      }
      else if(fnorm >= moose_system->_initial_residual * (1.0/snes->rtol))
      {
        PetscInfo2(snes,"Nonlinear solve was blowing up!",snes->nfuncs,snes->max_funcs);
        *reason = SNES_DIVERGED_LS_FAILURE;
      }

      if (it && !*reason)
      {
        if (fnorm <= snes->ttol)
        {
          PetscInfo2(snes,"Converged due to function norm %G < %G (relative tolerance)\n",fnorm,snes->ttol);
          *reason = SNES_CONVERGED_FNORM_RELATIVE;
        }
        else if (pnorm < snes->xtol*xnorm)
        {
          PetscInfo3(snes,"Converged due to small update length: %G < %G * %G\n",pnorm,snes->xtol,xnorm);
          *reason = SNES_CONVERGED_PNORM_RELATIVE;
        }
      }
      return(0);
    }

    /**
     * Called at the beginning of every nonlinear step (before Jacobian is formed)
     */
    PetscErrorCode petscNewtonUpdate(SNES snes, PetscInt step)
    {
      void *ctx = NULL;
      SNESGetApplicationContext(snes, &ctx);

      if (ctx != NULL)
      {
        Executioner *exec = (Executioner *) ctx;    // C strikes again

        exec->updateNewtonStep();
        exec->onNewtonUpdate();
      }

      return 0;
    }

    void petscSetDefaults(MooseSystem &moose_system, Executioner *executioner)
    {
      PetscNonlinearSolver<Number> * petsc_solver = dynamic_cast<PetscNonlinearSolver<Number> *>(moose_system.getNonlinearSystem()->nonlinear_solver.get());
      SNES snes = petsc_solver->snes();
      KSP ksp;
      SNESGetKSP(snes, &ksp);
      KSPSetPreconditionerSide(ksp, PC_RIGHT);
      SNESSetMaxLinearSolveFailures(snes, 1000000);

      SNESLineSearchSetPostCheck(snes, dampedCheck, moose_system.getEquationSystems());

#if PETSC_VERSION_LESS_THAN(3,0,0)
      KSPSetConvergenceTest(ksp, petscConverged, &moose_system);
      SNESSetConvergenceTest(snes, petscNonlinearConverged, &moose_system);
#else

      // In 3.0.0, the context pointer must actually be used, and the
      // final argument to KSPSetConvergenceTest() is a pointer to a
      // routine for destroying said private data context.  In this case,
      // we use the default context provided by PETSc in addition to
      // a few other tests.
      {
        PetscErrorCode ierr = KSPSetConvergenceTest(ksp,
                                                    petscConverged,
                                                    &moose_system,
                                                    PETSC_NULL);
        CHKERRABORT(libMesh::COMM_WORLD,ierr);
      }

#endif

      SNESSetUpdate(snes, petscNewtonUpdate);
      SNESSetApplicationContext(snes, (void *) executioner);

      /*
        PC pc;
        KSPGetPC(ksp,&pc);
        PCSetType(pc,PCSHELL);
        PCShellSetSetUp(pc,MatrixFreePreconditionerSetup);
        PCShellSetApply(pc,MatrixFreePreconditioner);
      */
    }

//    PetscErrorCode petscPhysicsBasedLineSearch(SNES snes,void *lsctx,Vec x,Vec /*f*/,Vec g,Vec y,Vec w, PetscReal /*fnorm*/,PetscReal *ynorm,PetscReal *gnorm,PetscTruth *flag)
    PetscErrorCode dampedCheck(SNES snes, Vec x, Vec y, Vec w, void *lsctx, PetscTruth * changed_y, PetscTruth * changed_w)
    {
      //w = updated solution = x+ scaling*y
      //x = current solution
      //y = updates.
      // for simple newton use w = x-y

      int ierr;
      Real damping = 1.0;

      EquationSystems * equation_systems = static_cast<EquationSystems *>(lsctx);

      MooseSystem * moose_system = equation_systems->parameters.get<MooseSystem *>("moose_system");

      MeshBase & mesh = equation_systems->get_mesh();

      TransientNonlinearImplicitSystem& system =
        equation_systems->get_system<TransientNonlinearImplicitSystem> ("NonlinearSystem");

      unsigned int sys = system.number();

      //create PetscVector
      PetscVector<Number>  update_vec_x(x);
      PetscVector<Number>  update_vec_y(y);
      PetscVector<Number>  update_vec_w(w);

      damping = moose_system->compute_damping(update_vec_w, update_vec_y);

      if(damping != 1.0)
      {
        VecScale(y, damping);
        *changed_y = PETSC_TRUE;
      }

      return(ierr);
    }
  }
}

#endif //LIBMESH_HAVE_PETSC
<commit_msg>small change<commit_after>#include "PetscSupport.h"

#ifdef LIBMESH_HAVE_PETSC

#include "Moose.h"
#include "MooseSystem.h"
#include "Executioner.h"

//libMesh Includes
#include "libmesh_common.h"
#include "equation_systems.h"
#include "nonlinear_implicit_system.h"
#include "linear_implicit_system.h"
#include "sparse_matrix.h"
#include "petsc_vector.h"
#include "petsc_matrix.h"
#include "petsc_linear_solver.h"
#include "petsc_preconditioner.h"

//PETSc includes
#include <petsc.h>
#include <petscsnes.h>
#include <petscksp.h>
#include <private/kspimpl.h>
#include <private/snesimpl.h>

namespace Moose
{
  namespace PetscSupport
  {
    /** The following functionality is encapsulated in the Moose ExecutionBlock but
     * still needed by Pronghorn for the time being
     */
    void petscParseOptions(GetPot & input_file)
    {
      // Set PETSC options:
      int num_petsc_opt = input_file.vector_variable_size("Execution/petsc_options");
      for(int i=0;i<num_petsc_opt;i++)
      {
        std::string petsc_opts = input_file("Execution/petsc_options","", i);
        PetscOptionsSetValue(petsc_opts.c_str(),PETSC_NULL);
      } // end of i-LOOP...

      int num_petsc_opt0 = input_file.vector_variable_size("Execution/petsc_options_iname");
      int num_petsc_opt1 = input_file.vector_variable_size("Execution/petsc_options_value");
      if(num_petsc_opt0 != num_petsc_opt1)
      {
        printf("Error in reading petsc_options_xxxxx\n");
        libmesh_error();
      }
      for(int i=0;i<num_petsc_opt0;i++)
      {
        std::string   petsc_opts_iname = input_file("Execution/petsc_options_iname", "", i);
        std::string   petsc_opts_value = input_file("Execution/petsc_options_value", "", i);
        PetscOptionsSetValue(petsc_opts_iname.c_str(),petsc_opts_value.c_str());
      } // end of i-LOOP...
    }

    PetscErrorCode  petscConverged(KSP ksp,PetscInt n,PetscReal rnorm,KSPConvergedReason *reason,void *dummy)
    {
      MooseSystem *moose_system = (MooseSystem *) dummy;      // C strikes

      *reason = KSP_CONVERGED_ITERATING;

      //If it's the beginning of a new set of iterations, reset last_rnorm
      if(!n)
        moose_system->_last_rnorm = 1e99;

      PetscReal norm_diff = std::fabs(rnorm - moose_system->_last_rnorm);

      if(norm_diff < moose_system->_l_abs_step_tol)
      {
        *reason = KSP_CONVERGED_RTOL;
        return(0);
      }

      moose_system->_last_rnorm = rnorm;

      // From here, we want the default behavior of the KSPDefaultConverged
      // test, but we don't want PETSc to die in that function with a
      // CHKERRQ call... therefore we Push/Pop a different error handler
      // and then call KSPDefaultConverged().  Finally, if we hit the
      // max iteration count, we want to set KSP_CONVERGED_ITS.
      PetscPushErrorHandler(PetscReturnErrorHandler,/* void* ctx= */PETSC_NULL);

      // As of PETSc 3.0.0, you must call KSPDefaultConverged with a
      // non-NULL context pointer which must be created with
      // KSPDefaultConvergedCreate(), and destroyed with
      // KSPDefaultConvergedDestroy().
      /*PetscErrorCode ierr = */
      KSPDefaultConverged(ksp, n, rnorm, reason, dummy);

      // Pop the Error handler we pushed on the stack to go back
      // to default PETSc error handling behavior.
      PetscPopErrorHandler();

      // If we hit max its then we consider that converged
      if (n >= ksp->max_it) *reason = KSP_CONVERGED_ITS;
      return 0;
    }

    PetscErrorCode petscNonlinearConverged(SNES snes,PetscInt it,PetscReal xnorm,PetscReal pnorm,PetscReal fnorm,SNESConvergedReason *reason,void * dummy)
    {
      MooseSystem *moose_system = (MooseSystem *) dummy;      // C strikes

      // unused
      // TransientNonlinearImplicitSystem * system = dynamic_cast<TransientNonlinearImplicitSystem *>(&_equation_system->get_system("NonlinearSystem"));

      //  for(unsigned int var = 0; var < system->n_vars(); var++)
      //    std::cout<<var<<": "<<system->calculate_norm(*system->rhs,var,DISCRETE_L2)<<std::endl;

      *reason = SNES_CONVERGED_ITERATING;

      if (!it)
      {
        /* set parameter for default relative tolerance convergence test */
        snes->ttol = fnorm*snes->rtol;
        moose_system->_initial_residual = fnorm;
      }
      if (fnorm != fnorm)
      {
        PetscInfo(snes,"Failed to converged, function norm is NaN\n");
        *reason = SNES_DIVERGED_FNORM_NAN;
      }
      else if (fnorm < snes->abstol)
      {
        PetscInfo2(snes,"Converged due to function norm %G < %G\n",fnorm,snes->abstol);
        *reason = SNES_CONVERGED_FNORM_ABS;
      }
      else if (snes->nfuncs >= snes->max_funcs)
      {
        PetscInfo2(snes,"Exceeded maximum number of function evaluations: %D > %D\n",snes->nfuncs,snes->max_funcs);
        *reason = SNES_DIVERGED_FUNCTION_COUNT;
      }
      else if(fnorm >= moose_system->_initial_residual * (1.0/snes->rtol))
      {
        PetscInfo2(snes,"Nonlinear solve was blowing up!",snes->nfuncs,snes->max_funcs);
        *reason = SNES_DIVERGED_LS_FAILURE;
      }

      if (it && !*reason)
      {
        if (fnorm <= snes->ttol)
        {
          PetscInfo2(snes,"Converged due to function norm %G < %G (relative tolerance)\n",fnorm,snes->ttol);
          *reason = SNES_CONVERGED_FNORM_RELATIVE;
        }
        else if (pnorm < snes->xtol*xnorm)
        {
          PetscInfo3(snes,"Converged due to small update length: %G < %G * %G\n",pnorm,snes->xtol,xnorm);
          *reason = SNES_CONVERGED_PNORM_RELATIVE;
        }
      }
      return(0);
    }

    /**
     * Called at the beginning of every nonlinear step (before Jacobian is formed)
     */
    PetscErrorCode petscNewtonUpdate(SNES snes, PetscInt step)
    {
      void *ctx = NULL;
      SNESGetApplicationContext(snes, &ctx);

      if (ctx != NULL)
      {
        Executioner *exec = (Executioner *) ctx;    // C strikes again

        exec->updateNewtonStep();
        exec->onNewtonUpdate();
      }

      return 0;
    }

    void petscSetDefaults(MooseSystem &moose_system, Executioner *executioner)
    {
      PetscNonlinearSolver<Number> * petsc_solver = dynamic_cast<PetscNonlinearSolver<Number> *>(moose_system.getNonlinearSystem()->nonlinear_solver.get());
      SNES snes = petsc_solver->snes();
      KSP ksp;
      SNESGetKSP(snes, &ksp);
      KSPSetPreconditionerSide(ksp, PC_RIGHT);
      SNESSetMaxLinearSolveFailures(snes, 1000000);

      SNESLineSearchSetPostCheck(snes, dampedCheck, moose_system.getEquationSystems());

#if PETSC_VERSION_LESS_THAN(3,0,0)
      KSPSetConvergenceTest(ksp, petscConverged, &moose_system);
      SNESSetConvergenceTest(snes, petscNonlinearConverged, &moose_system);
#else

      // In 3.0.0, the context pointer must actually be used, and the
      // final argument to KSPSetConvergenceTest() is a pointer to a
      // routine for destroying said private data context.  In this case,
      // we use the default context provided by PETSc in addition to
      // a few other tests.
      {
        PetscErrorCode ierr = KSPSetConvergenceTest(ksp,
                                                    petscConverged,
                                                    &moose_system,
                                                    PETSC_NULL);
        CHKERRABORT(libMesh::COMM_WORLD,ierr);
      }

#endif

      SNESSetUpdate(snes, petscNewtonUpdate);
      SNESSetApplicationContext(snes, (void *) executioner);

      /*
        PC pc;
        KSPGetPC(ksp,&pc);
        PCSetType(pc,PCSHELL);
        PCShellSetSetUp(pc,MatrixFreePreconditionerSetup);
        PCShellSetApply(pc,MatrixFreePreconditioner);
      */
    }

//    PetscErrorCode petscPhysicsBasedLineSearch(SNES snes,void *lsctx,Vec x,Vec /*f*/,Vec g,Vec y,Vec w, PetscReal /*fnorm*/,PetscReal *ynorm,PetscReal *gnorm,PetscTruth *flag)
    PetscErrorCode dampedCheck(SNES snes, Vec x, Vec y, Vec w, void *lsctx, PetscTruth * changed_y, PetscTruth * changed_w)
    {
      //w = updated solution = x+ scaling*y
      //x = current solution
      //y = updates.
      // for simple newton use w = x-y

      int ierr = 0;
      Real damping = 1.0;

      EquationSystems * equation_systems = static_cast<EquationSystems *>(lsctx);

      MooseSystem * moose_system = equation_systems->parameters.get<MooseSystem *>("moose_system");

      MeshBase & mesh = equation_systems->get_mesh();

      TransientNonlinearImplicitSystem& system =
        equation_systems->get_system<TransientNonlinearImplicitSystem> ("NonlinearSystem");

      unsigned int sys = system.number();

      //create PetscVector
      PetscVector<Number>  update_vec_x(x);
      PetscVector<Number>  update_vec_y(y);
      PetscVector<Number>  update_vec_w(w);

      damping = moose_system->compute_damping(update_vec_w, update_vec_y);

      if(damping != 1.0)
      {
        VecScale(y, damping);
        *changed_y = PETSC_TRUE;
      }

      return(ierr);
    }
  }
}

#endif //LIBMESH_HAVE_PETSC
<|endoftext|>