<commit_before>/*******************************************************************************
 Infomap software package for multi-level network clustering
 Copyright (c) 2013, 2014 Daniel Edler, Anton Eriksson, Martin Rosvall

 This file is part of the Infomap software package.
 See file LICENSE_AGPLv3.txt for full license details.
 For more information, see <http://www.mapequation.org>
 ******************************************************************************/

#include "Config.h"
#include "ProgramInterface.h"
#include "SafeFile.h"
#include "../utils/FileURI.h"
#include "../utils/Log.h"
#include <vector>
#include <stdexcept>

namespace infomap {

constexpr int FlowModel::undirected;
constexpr int FlowModel::directed;
constexpr int FlowModel::undirdir;
constexpr int FlowModel::outdirdir;
constexpr int FlowModel::rawdir;

Config::Config(const std::string& flags, bool isCLI) : isCLI(isCLI)
{
  ProgramInterface api("Infomap",
                       "Implementation of the Infomap clustering algorithm based on the Map Equation (see www.mapequation.org)",
                       INFOMAP_VERSION);

  api.setGroups({ "Input", "Algorithm", "Accuracy", "Output" });

  bool deprecated_includeSelfLinks = false;
  std::vector<std::string> optionalOutputDir; // Used if !isCLI
  // --------------------- Input options ---------------------
  if (isCLI) {
    api.addNonOptionArgument(networkFile, "network_file", "File containing the network data. Assumes a link list format if no Pajek formatted heading.", "Input");
  } else {
    api.addOptionArgument(networkFile, "input", "File containing the network data. Assumes a link list format if no Pajek formatted heading.", ArgType::path, "Input");
  }

  api.addOptionArgument(skipAdjustBipartiteFlow, "skip-adjust-bipartite-flow", "Skip distributing all flow from the bipartite nodes to the primary nodes.", "Input", true);

  api.addOptionArgument(bipartiteTeleportation, "bipartite-teleportation", "Teleport like the bipartite flow instead of two-step (unipartite) teleportation.", "Input", true);

  api.addOptionArgument(weightThreshold, "weight-threshold", "Limit the number of links to read from the network. Ignore links with less weight than the threshold.", ArgType::number, "Input", true);

  api.addOptionArgument(deprecated_includeSelfLinks, 'k', "include-self-links", "DEPRECATED. Include self links by default now, exclude with --no-self-links.", "Input", true);

  api.addOptionArgument(noSelfLinks, "no-self-links", "Exclude self links in the input network.", "Input", true);

  api.addOptionArgument(nodeLimit, "node-limit", "Limit the number of nodes to read from the network. Ignore links connected to ignored nodes.", ArgType::integer, "Input", true);

  api.addOptionArgument(matchableMultilayerIds, "matchable-multilayer-ids", "Construct state ids from node and layer ids that are consistent across networks for the same max number of layers. Set to at least the largest layer id among networks to match.", ArgType::integer, "Input", true);

  api.addOptionArgument(clusterDataFile, 'c', "cluster-data", "Provide an initial two-level (clu format) or multi-layer (tree format) solution.", ArgType::path, "Input");

  api.addOptionArgument(assignToNeighbouringModule, "assign-to-neighbouring-module", "Assign nodes without module assignments (from --cluster-data) to the module assignment of a neighbouring node if possible.", "Input", true);

  api.addOptionArgument(metaDataFile, "meta-data", "Provide meta data (clu format) that should be encoded.", ArgType::path, "Input", true);

  api.addOptionArgument(metaDataRate, "meta-data-rate", "Metadata encoding rate. Default is to encode each step.", ArgType::number, "Input", true);

  api.addOptionArgument(unweightedMetaData, "meta-data-unweighted", "Don't weight meta data by node flow.", "Input", true);

  api.addOptionArgument(noInfomap, "no-infomap", "Don't run the optimizer. Useful to calculate codelength of provided cluster data or to print non-modular statistics.", "Input");

  // --------------------- Output options ---------------------

  api.addOptionArgument(outName, "out-name", "Name for the output files, e.g. [output_directory]/[out-name].tree", ArgType::string, "Output", true);

  api.addOptionArgument(noFileOutput, '0', "no-file-output", "Don't write output to file.", "Output", true);

  api.addOptionArgument(printTree, "tree", "Write a tree file with the modular hierarchy. Automatically enabled if no other output is specified.", "Output");

  api.addOptionArgument(printFlowTree, "ftree", "Write a ftree file with the modular hierarchy including aggregated links between (nested) modules. (Used by Network Navigator)", "Output");

  api.addOptionArgument(printClu, "clu", "Write a clu file with the top cluster ids for each node.", "Output");

  api.addOptionArgument(cluLevel, "clu-level", "For clu output, print modules at specified depth from root. Use -1 for bottom level modules.", ArgType::integer, "Output", true);

  api.addOptionArgument(outputFormats, 'o', "output", "Comma-separated output formats without spaces, e.g. -o clu,tree,ftree. Options: clu, tree, ftree, newick, json, csv, network, states, flow.", ArgType::list, "Output", true);

  api.addOptionArgument(hideBipartiteNodes, "hide-bipartite-nodes", "Project bipartite solution to unipartite.", "Output", true);

  // --------------------- Core algorithm options ---------------------
  api.addOptionArgument(twoLevel, '2', "two-level", "Optimize a two-level partition of the network. Default is multi-level.", "Algorithm");

  std::string flowModelArg;

  api.addOptionArgument(flowModelArg, 'f', "flow-model", "Specify flow model. Options: undirected, directed, undirdir, outdirdir, rawdir.", ArgType::option, "Algorithm");

  api.addOptionArgument(directed, 'd', "directed", "Assume directed links. Shorthand for '--flow-model directed'.", "Algorithm");

  api.addOptionArgument(recordedTeleportation, 'e', "recorded-teleportation", "If teleportation is used to calculate the flow, also record it when minimizing codelength.", "Algorithm", true);

  api.addOptionArgument(useNodeWeightsAsFlow, "use-node-weights-as-flow", "Use node weights (from api or after names in Pajek format) as flow, normalized to sum to 1", "Algorithm", true);

  api.addOptionArgument(teleportToNodes, "to-nodes", "Teleport to nodes instead of to links, assuming uniform node weights if no such input data.", "Algorithm", true);

  api.addOptionArgument(teleportationProbability, 'p', "teleportation-probability", "Probability of teleporting to a random node or link.", ArgType::probability, "Algorithm", true);

  api.addOptionArgument(regularized, "regularized", "Effectively add a fully connected Bayesian prior network to not overfit due to missing links. Implies recorded teleportation", "Algorithm", true);

  api.addOptionArgument(regularizationStrength, "regularization-strength", "Adjust relative strength of Bayesian prior network with this multiplier.", ArgType::number, "Algorithm", true);

  api.addOptionArgument(entropyBiasCorrection, "entropy-corrected", "Correct for negative entropy bias in small samples (many modules).", "Algorithm", true);

  api.addOptionArgument(entropyBiasCorrectionMultiplier, "entropy-correction-strength", "Increase or decrease the default entropy correction with this factor.", ArgType::number, "Algorithm", true);

  api.addOptionArgument(markovTime, "markov-time", "Scales link flow to change the cost of moving between modules. Higher values results in fewer modules.", ArgType::number, "Algorithm", true);

  api.addOptionArgument(preferredNumberOfModules, "preferred-number-of-modules", "Penalize solutions the more they differ from this number.", ArgType::integer, "Algorithm", true);

  api.addOptionArgument(multilayerRelaxRate, "multilayer-relax-rate", "Probability to relax the constraint to move only in the current layer.", ArgType::probability, "Algorithm", true);

  api.addOptionArgument(multilayerRelaxLimit, "multilayer-relax-limit", "Number of neighboring layers in each direction to relax to. If negative, relax to any layer.", ArgType::integer, "Algorithm", true);

  api.addOptionArgument(multilayerRelaxLimitUp, "multilayer-relax-limit-up", "Number of neighboring layers with higher id to relax to. If negative, relax to any layer.", ArgType::integer, "Algorithm", true);

  api.addOptionArgument(multilayerRelaxLimitDown, "multilayer-relax-limit-down", "Number of neighboring layers with lower id to relax to. If negative, relax to any layer.", ArgType::integer, "Algorithm", true);

  api.addOptionArgument(multilayerRelaxByJensenShannonDivergence, "multilayer-relax-by-jsd", "Relax proportional to the out-link similarity measured by the Jensen-Shannon divergence.", "Algorithm", true);

  // --------------------- Performance and accuracy options ---------------------
  api.addOptionArgument(seedToRandomNumberGenerator, 's', "seed", "A seed (integer) to the random number generator for reproducible results.", ArgType::integer, "Accuracy");

  api.addOptionArgument(numTrials, 'N', "num-trials", "Number of outer-most loops to run before picking the best solution.", ArgType::integer, "Accuracy");

  api.addOptionArgument(coreLoopLimit, 'M', "core-loop-limit", "Limit the number of loops that tries to move each node into the best possible module.", ArgType::integer, "Accuracy", true);

  api.addOptionArgument(levelAggregationLimit, 'L', "core-level-limit", "Limit the number of times the core loops are reapplied on existing modular network to search bigger structures.", ArgType::integer, "Accuracy", true);

  api.addOptionArgument(tuneIterationLimit, 'T', "tune-iteration-limit", "Limit the number of main iterations in the two-level partition algorithm. 0 means no limit.", ArgType::integer, "Accuracy", true);

  api.addOptionArgument(minimumCodelengthImprovement, "core-loop-codelength-threshold", "Minimum codelength threshold for accepting a new solution in core loop.", ArgType::number, "Accuracy", true);

  api.addOptionArgument(minimumRelativeTuneIterationImprovement, "tune-iteration-relative-threshold", "Set codelength improvement threshold of each new tune iteration to 'f' times the initial two-level codelength.", ArgType::number, "Accuracy", true);

  api.addIncrementalOptionArgument(fastHierarchicalSolution, 'F', "fast-hierarchical-solution", "Find top modules fast. Use -FF to keep all fast levels. Use -FFF to skip recursive part.", "Accuracy", true);

  api.addOptionArgument(preferModularSolution, "prefer-modular-solution", "Prefer modular solutions even if they are worse than putting all nodes in one module.", "Accuracy", true);

  api.addOptionArgument(innerParallelization, "inner-parallelization", "Parallelize the inner-most loop for greater speed. This may give some accuracy tradeoff.", "Accuracy", true);

  api.addOptionalNonOptionArguments(optionalOutputDir, "out_directory", "Directory to write the results to.", "Output");

  api.addIncrementalOptionArgument(verbosity, 'v', "verbose", "Verbose output on the console. Add additional 'v' flags to increase verbosity up to -vvv.", "Output");

  api.addOptionArgument(silent, "silent", "No output on the console.", "Output");

  api.parseArgs(flags);

  if (deprecated_includeSelfLinks) {
    throw std::runtime_error("The --include-self-links flag is deprecated to include self links by default. Use --no-loops to exclude.");
  }

  if (!optionalOutputDir.empty())
    outDirectory = optionalOutputDir[0];

  if (!isCLI && outDirectory.empty())
    noFileOutput = true;

  if (!noFileOutput && outDirectory.empty() && isCLI) {
    throw std::runtime_error("Missing out_directory");
  }

  if (flowModelArg == "directed" || directed) {
    setFlowModel(FlowModel::directed);
  } else if (flowModelArg == "undirected") {
    setFlowModel(FlowModel::undirected);
  } else if (flowModelArg == "undirdir") {
    setFlowModel(FlowModel::undirdir);
  } else if (flowModelArg == "outdirdir") {
    setFlowModel(FlowModel::outdirdir);
  } else if (flowModelArg == "rawdir") {
    setFlowModel(FlowModel::rawdir);
  } else if (!flowModelArg.empty()) {
    throw std::runtime_error("Unrecognized flow model");
  }

  if (regularized) {
    recordedTeleportation = true;
  }

  if (*--outDirectory.end() != '/')
    outDirectory.append("/");

  if (haveOutput() && !isDirectoryWritable(outDirectory))
    throw std::runtime_error(io::Str() << "Can't write to directory '" << outDirectory << "'. Check that the directory exists and that you have write permissions.");

  if (outName.empty()) {
    outName = !networkFile.empty() ? FileURI(networkFile).getName() : "no-name";
  }

  parsedString = flags;
  parsedOptions = api.getUsedOptionArguments();

  adaptDefaults();

  Log::init(verbosity, silent, verboseNumberPrecision);
}

void Config::adaptDefaults()
{
  auto outputs = io::split(outputFormats, ',');
  for (std::string& o : outputs) {
    if (o == "clu") {
      printClu = true;
    } else if (o == "tree") {
      printTree = true;
    } else if (o == "ftree") {
      printFlowTree = true;
    } else if (o == "newick") {
      printNewick = true;
    } else if (o == "json") {
      printJson = true;
    } else if (o == "csv") {
      printCsv = true;
    } else if (o == "network") {
      printPajekNetwork = true;
    } else if (o == "flow") {
      printFlowNetwork = true;
    } else if (o == "states") {
      printStateNetwork = true;
    } else {
      throw std::runtime_error(io::Str() << "Unrecognized output format: '" << o << "'.");
    }
  }

  // Of no output format specified, use tree as default (if not used as a library).
  if (isCLI && !haveModularResultOutput()) {
    printTree = true;
  }
}

std::ostream& operator<<(std::ostream& out, FlowModel f)
{
  return out << flowModelToString(f);
}

} // namespace infomap
<commit_msg>fix: Using deprecated option --include-self-links references missing option --no-loops<commit_after>/*******************************************************************************
 Infomap software package for multi-level network clustering
 Copyright (c) 2013, 2014 Daniel Edler, Anton Eriksson, Martin Rosvall

 This file is part of the Infomap software package.
 See file LICENSE_AGPLv3.txt for full license details.
 For more information, see <http://www.mapequation.org>
 ******************************************************************************/

#include "Config.h"
#include "ProgramInterface.h"
#include "SafeFile.h"
#include "../utils/FileURI.h"
#include "../utils/Log.h"
#include <vector>
#include <stdexcept>

namespace infomap {

constexpr int FlowModel::undirected;
constexpr int FlowModel::directed;
constexpr int FlowModel::undirdir;
constexpr int FlowModel::outdirdir;
constexpr int FlowModel::rawdir;

Config::Config(const std::string& flags, bool isCLI) : isCLI(isCLI)
{
  ProgramInterface api("Infomap",
                       "Implementation of the Infomap clustering algorithm based on the Map Equation (see www.mapequation.org)",
                       INFOMAP_VERSION);

  api.setGroups({ "Input", "Algorithm", "Accuracy", "Output" });

  bool deprecated_includeSelfLinks = false;
  std::vector<std::string> optionalOutputDir; // Used if !isCLI
  // --------------------- Input options ---------------------
  if (isCLI) {
    api.addNonOptionArgument(networkFile, "network_file", "File containing the network data. Assumes a link list format if no Pajek formatted heading.", "Input");
  } else {
    api.addOptionArgument(networkFile, "input", "File containing the network data. Assumes a link list format if no Pajek formatted heading.", ArgType::path, "Input");
  }

  api.addOptionArgument(skipAdjustBipartiteFlow, "skip-adjust-bipartite-flow", "Skip distributing all flow from the bipartite nodes to the primary nodes.", "Input", true);

  api.addOptionArgument(bipartiteTeleportation, "bipartite-teleportation", "Teleport like the bipartite flow instead of two-step (unipartite) teleportation.", "Input", true);

  api.addOptionArgument(weightThreshold, "weight-threshold", "Limit the number of links to read from the network. Ignore links with less weight than the threshold.", ArgType::number, "Input", true);

  api.addOptionArgument(deprecated_includeSelfLinks, 'k', "include-self-links", "DEPRECATED. Include self links by default now, exclude with --no-self-links.", "Input", true);

  api.addOptionArgument(noSelfLinks, "no-self-links", "Exclude self links in the input network.", "Input", true);

  api.addOptionArgument(nodeLimit, "node-limit", "Limit the number of nodes to read from the network. Ignore links connected to ignored nodes.", ArgType::integer, "Input", true);

  api.addOptionArgument(matchableMultilayerIds, "matchable-multilayer-ids", "Construct state ids from node and layer ids that are consistent across networks for the same max number of layers. Set to at least the largest layer id among networks to match.", ArgType::integer, "Input", true);

  api.addOptionArgument(clusterDataFile, 'c', "cluster-data", "Provide an initial two-level (clu format) or multi-layer (tree format) solution.", ArgType::path, "Input");

  api.addOptionArgument(assignToNeighbouringModule, "assign-to-neighbouring-module", "Assign nodes without module assignments (from --cluster-data) to the module assignment of a neighbouring node if possible.", "Input", true);

  api.addOptionArgument(metaDataFile, "meta-data", "Provide meta data (clu format) that should be encoded.", ArgType::path, "Input", true);

  api.addOptionArgument(metaDataRate, "meta-data-rate", "Metadata encoding rate. Default is to encode each step.", ArgType::number, "Input", true);

  api.addOptionArgument(unweightedMetaData, "meta-data-unweighted", "Don't weight meta data by node flow.", "Input", true);

  api.addOptionArgument(noInfomap, "no-infomap", "Don't run the optimizer. Useful to calculate codelength of provided cluster data or to print non-modular statistics.", "Input");

  // --------------------- Output options ---------------------

  api.addOptionArgument(outName, "out-name", "Name for the output files, e.g. [output_directory]/[out-name].tree", ArgType::string, "Output", true);

  api.addOptionArgument(noFileOutput, '0', "no-file-output", "Don't write output to file.", "Output", true);

  api.addOptionArgument(printTree, "tree", "Write a tree file with the modular hierarchy. Automatically enabled if no other output is specified.", "Output");

  api.addOptionArgument(printFlowTree, "ftree", "Write a ftree file with the modular hierarchy including aggregated links between (nested) modules. (Used by Network Navigator)", "Output");

  api.addOptionArgument(printClu, "clu", "Write a clu file with the top cluster ids for each node.", "Output");

  api.addOptionArgument(cluLevel, "clu-level", "For clu output, print modules at specified depth from root. Use -1 for bottom level modules.", ArgType::integer, "Output", true);

  api.addOptionArgument(outputFormats, 'o', "output", "Comma-separated output formats without spaces, e.g. -o clu,tree,ftree. Options: clu, tree, ftree, newick, json, csv, network, states, flow.", ArgType::list, "Output", true);

  api.addOptionArgument(hideBipartiteNodes, "hide-bipartite-nodes", "Project bipartite solution to unipartite.", "Output", true);

  // --------------------- Core algorithm options ---------------------
  api.addOptionArgument(twoLevel, '2', "two-level", "Optimize a two-level partition of the network. Default is multi-level.", "Algorithm");

  std::string flowModelArg;

  api.addOptionArgument(flowModelArg, 'f', "flow-model", "Specify flow model. Options: undirected, directed, undirdir, outdirdir, rawdir.", ArgType::option, "Algorithm");

  api.addOptionArgument(directed, 'd', "directed", "Assume directed links. Shorthand for '--flow-model directed'.", "Algorithm");

  api.addOptionArgument(recordedTeleportation, 'e', "recorded-teleportation", "If teleportation is used to calculate the flow, also record it when minimizing codelength.", "Algorithm", true);

  api.addOptionArgument(useNodeWeightsAsFlow, "use-node-weights-as-flow", "Use node weights (from api or after names in Pajek format) as flow, normalized to sum to 1", "Algorithm", true);

  api.addOptionArgument(teleportToNodes, "to-nodes", "Teleport to nodes instead of to links, assuming uniform node weights if no such input data.", "Algorithm", true);

  api.addOptionArgument(teleportationProbability, 'p', "teleportation-probability", "Probability of teleporting to a random node or link.", ArgType::probability, "Algorithm", true);

  api.addOptionArgument(regularized, "regularized", "Effectively add a fully connected Bayesian prior network to not overfit due to missing links. Implies recorded teleportation", "Algorithm", true);

  api.addOptionArgument(regularizationStrength, "regularization-strength", "Adjust relative strength of Bayesian prior network with this multiplier.", ArgType::number, "Algorithm", true);

  api.addOptionArgument(entropyBiasCorrection, "entropy-corrected", "Correct for negative entropy bias in small samples (many modules).", "Algorithm", true);

  api.addOptionArgument(entropyBiasCorrectionMultiplier, "entropy-correction-strength", "Increase or decrease the default entropy correction with this factor.", ArgType::number, "Algorithm", true);

  api.addOptionArgument(markovTime, "markov-time", "Scales link flow to change the cost of moving between modules. Higher values results in fewer modules.", ArgType::number, "Algorithm", true);

  api.addOptionArgument(preferredNumberOfModules, "preferred-number-of-modules", "Penalize solutions the more they differ from this number.", ArgType::integer, "Algorithm", true);

  api.addOptionArgument(multilayerRelaxRate, "multilayer-relax-rate", "Probability to relax the constraint to move only in the current layer.", ArgType::probability, "Algorithm", true);

  api.addOptionArgument(multilayerRelaxLimit, "multilayer-relax-limit", "Number of neighboring layers in each direction to relax to. If negative, relax to any layer.", ArgType::integer, "Algorithm", true);

  api.addOptionArgument(multilayerRelaxLimitUp, "multilayer-relax-limit-up", "Number of neighboring layers with higher id to relax to. If negative, relax to any layer.", ArgType::integer, "Algorithm", true);

  api.addOptionArgument(multilayerRelaxLimitDown, "multilayer-relax-limit-down", "Number of neighboring layers with lower id to relax to. If negative, relax to any layer.", ArgType::integer, "Algorithm", true);

  api.addOptionArgument(multilayerRelaxByJensenShannonDivergence, "multilayer-relax-by-jsd", "Relax proportional to the out-link similarity measured by the Jensen-Shannon divergence.", "Algorithm", true);

  // --------------------- Performance and accuracy options ---------------------
  api.addOptionArgument(seedToRandomNumberGenerator, 's', "seed", "A seed (integer) to the random number generator for reproducible results.", ArgType::integer, "Accuracy");

  api.addOptionArgument(numTrials, 'N', "num-trials", "Number of outer-most loops to run before picking the best solution.", ArgType::integer, "Accuracy");

  api.addOptionArgument(coreLoopLimit, 'M', "core-loop-limit", "Limit the number of loops that tries to move each node into the best possible module.", ArgType::integer, "Accuracy", true);

  api.addOptionArgument(levelAggregationLimit, 'L', "core-level-limit", "Limit the number of times the core loops are reapplied on existing modular network to search bigger structures.", ArgType::integer, "Accuracy", true);

  api.addOptionArgument(tuneIterationLimit, 'T', "tune-iteration-limit", "Limit the number of main iterations in the two-level partition algorithm. 0 means no limit.", ArgType::integer, "Accuracy", true);

  api.addOptionArgument(minimumCodelengthImprovement, "core-loop-codelength-threshold", "Minimum codelength threshold for accepting a new solution in core loop.", ArgType::number, "Accuracy", true);

  api.addOptionArgument(minimumRelativeTuneIterationImprovement, "tune-iteration-relative-threshold", "Set codelength improvement threshold of each new tune iteration to 'f' times the initial two-level codelength.", ArgType::number, "Accuracy", true);

  api.addIncrementalOptionArgument(fastHierarchicalSolution, 'F', "fast-hierarchical-solution", "Find top modules fast. Use -FF to keep all fast levels. Use -FFF to skip recursive part.", "Accuracy", true);

  api.addOptionArgument(preferModularSolution, "prefer-modular-solution", "Prefer modular solutions even if they are worse than putting all nodes in one module.", "Accuracy", true);

  api.addOptionArgument(innerParallelization, "inner-parallelization", "Parallelize the inner-most loop for greater speed. This may give some accuracy tradeoff.", "Accuracy", true);

  api.addOptionalNonOptionArguments(optionalOutputDir, "out_directory", "Directory to write the results to.", "Output");

  api.addIncrementalOptionArgument(verbosity, 'v', "verbose", "Verbose output on the console. Add additional 'v' flags to increase verbosity up to -vvv.", "Output");

  api.addOptionArgument(silent, "silent", "No output on the console.", "Output");

  api.parseArgs(flags);

  if (deprecated_includeSelfLinks) {
    throw std::runtime_error("The --include-self-links flag is deprecated to include self links by default. Use --no-self-links to exclude.");
  }

  if (!optionalOutputDir.empty())
    outDirectory = optionalOutputDir[0];

  if (!isCLI && outDirectory.empty())
    noFileOutput = true;

  if (!noFileOutput && outDirectory.empty() && isCLI) {
    throw std::runtime_error("Missing out_directory");
  }

  if (flowModelArg == "directed" || directed) {
    setFlowModel(FlowModel::directed);
  } else if (flowModelArg == "undirected") {
    setFlowModel(FlowModel::undirected);
  } else if (flowModelArg == "undirdir") {
    setFlowModel(FlowModel::undirdir);
  } else if (flowModelArg == "outdirdir") {
    setFlowModel(FlowModel::outdirdir);
  } else if (flowModelArg == "rawdir") {
    setFlowModel(FlowModel::rawdir);
  } else if (!flowModelArg.empty()) {
    throw std::runtime_error("Unrecognized flow model");
  }

  if (regularized) {
    recordedTeleportation = true;
  }

  if (*--outDirectory.end() != '/')
    outDirectory.append("/");

  if (haveOutput() && !isDirectoryWritable(outDirectory))
    throw std::runtime_error(io::Str() << "Can't write to directory '" << outDirectory << "'. Check that the directory exists and that you have write permissions.");

  if (outName.empty()) {
    outName = !networkFile.empty() ? FileURI(networkFile).getName() : "no-name";
  }

  parsedString = flags;
  parsedOptions = api.getUsedOptionArguments();

  adaptDefaults();

  Log::init(verbosity, silent, verboseNumberPrecision);
}

void Config::adaptDefaults()
{
  auto outputs = io::split(outputFormats, ',');
  for (std::string& o : outputs) {
    if (o == "clu") {
      printClu = true;
    } else if (o == "tree") {
      printTree = true;
    } else if (o == "ftree") {
      printFlowTree = true;
    } else if (o == "newick") {
      printNewick = true;
    } else if (o == "json") {
      printJson = true;
    } else if (o == "csv") {
      printCsv = true;
    } else if (o == "network") {
      printPajekNetwork = true;
    } else if (o == "flow") {
      printFlowNetwork = true;
    } else if (o == "states") {
      printStateNetwork = true;
    } else {
      throw std::runtime_error(io::Str() << "Unrecognized output format: '" << o << "'.");
    }
  }

  // Of no output format specified, use tree as default (if not used as a library).
  if (isCLI && !haveModularResultOutput()) {
    printTree = true;
  }
}

std::ostream& operator<<(std::ostream& out, FlowModel f)
{
  return out << flowModelToString(f);
}

} // namespace infomap
<|endoftext|>


<commit_before>#include"all_defines.hpp"

#include"aio.hpp"

#include<unistd.h>
#include<errno.h>
#include<signal.h>
#include<fcntl.h>

// used for error reporting before aborting
#include<iostream>

/*
POSIX-based asynchronous I/O and signal handling

Note that we don't program to the letter of the POSIX
specs: AFAIK no actual OS conforms perfectly to POSIX.
Preferably we try to be somewhat more resilient and
try to be more defensive when making system calls.
*/

/*self-pipe trick with SIGCHLD*/
static int sigchld_wr, sigchld_rd;
/*PLANNED (not yet implemented)
When compiled single_threaded, signal handlers also
write to sigchld_wr, in order to (hopefully!)
interrupt pending select()/poll()'s.  We don't depend
on this too much, because we will still raise flags
somewhere - we only depend on it to interrupt our
calls.
When compiled without single_threaded, signals are
blocked on all threads, then at initialization we
start a "hidden" thread whose sole purpose is to
wait for signals.
*/

/*SIGCHLD Known issues:
On Linux < 2.4, each thread's child processes belonged
only to that thread; this meant (1) SIGCHLD gets sent
exactly to the thread which launched that process, and
nobody else, and (2) a thread couldn't waitpid() on any
other thread's child process.  Both behaviors are not
POSIX compliant.  I also can't think of a way to handle
those properly, at least not with the fact that the
events system in `hl` may be launched from any of N
worker threads.

For Linux >= 2.4, a thread could now waitpid() on other
thread's child processes.  However, AFAIK (can't find
references regarding exact behavior) SIGCHLD is still
sent to the launching thread.

For Linux >= 2.6, we have NPTL, which is supposedly
correctly POSIX.

The code here *should* work on Linux 2.4, assuming that
write() from multiple threads simultaneously gets
properly handled (locked!!) by the OS.
*/

static void sigchld_handler(int) {
	ssize_t rv;
	char buf[1]; /*exact data doesn't matter*/
	do {
		errno = 0;
		rv = write(sigchld_wr, buf, 1);
		/*our signal installer actually
		masks out all other signals for
		this handler, so we shouldn't get
		EINTR, but better safe than
		sorry...
		*/
	} while(rv < 0 && errno == EINTR);
	/*All other errors get ignored!*/
	/*Errors:
	EAGAIN
		- we *shouldn't* mark sigchld_wr as nonblocking
	EBADF
		- pretty bad problem, means we failed to init,
		but we *do* check the pipe FD's at init time
		before we even install this sighandler.
	EFAULT
		- nasty, means buf is segfaultable, meaning we
		probably can't return anyway.
	EFBIG
		- not writing to a file, just a pipe.
	EINTR
		- handled by restarting the call.
	EINVAL
		- pretty bad problem, means we failed to init
	EIO
		- not writing to a file, just a pipe.
	ENOSPC
		- that's OK, we only care that there's data
		on the pipe to wake up the select() call
	EPIPE
		- will only happen when we're cleaning up
		and closing everything, so won't matter if
		signal is lost, we're already exiting.
	*/
}

/*FD_CLOEXEC Known Issues:
Normally FD's will remain open across fork() and exec() calls.
This is useful when piping.  Unfortunately, we rarely even
want to use piping; almost definitely we will pipe only a few
FD's.

Thus, in the general case, when acquiring FD's from the OS,
we must very soon force them to FD_CLOEXEC.  However, this
has problems in a multithreaded implementation which fork()s.
It's possible for one thread to fork()+exec() while another
thread has just, say, called socket().  In this case the
returned socket will *not* have FD_CLOEXEC and will remain
open in the child process, reducing the number of useable
FD's in the process, as well as other weird stuff like tape
drives not rewinding...

This can be solved in many ways:
1.  Ignore CLOEXEC.  After fork() and before exec(), close
everything except for any redirections we want.  This means
about sysconf(_SC_OPEN_MAX) calls to close().  Now suppose
_SC_OPEN_MAX is 65,536...
2.  Like above.  But we just keep closing until we get N
consecutive EBADF failures for close(), where N is some big
number.  Hackish solution which depends on the probability
that FD numbers will skip at most N consecutive values.
3.  Keep track of the largest ever FD we ever see from any
source.  Just loop up to that instead of
sysconf(_SC_OPEN_MAX), which should be faster because the
largest FD will be much smaller than _SC_OPEN_MAX.  However
when multithreaded we have to rely on locks to keep track
of the largest seen FD anyway, so....
4.  Protect all open()+fcntl()/socket()+fcntl/pipe()+fcntl(),
as well as fork(), with a mutex.  While fork() will also
duplicate the mutex in the child, we do an exec() soon
anwyay, so the child's mutex copy gets implicitly deleted.

#4 above seems the best solution but it *might* mean that
some LIBC and other third-party library routines can't be
safely used (since they would internally open some FD's,
which we can't protect with a mutex).
*/
static void force_cloexec(int fd) {
	int flag;
	do {
		errno = 0;
		flag = fcntl(fd, F_GETFD);
	} while(flag < 0 && errno == EINTR);
	if(flag < 0) {
		std::cerr << "cloexec: error during getting of flag"
			<< std::endl;
		exit(1);
	}
	long lflag = ((long) (flag)) | FD_CLOEXEC;
	int rv;
	do {
		errno = 0;
		rv = fcntl(fd, F_SETFD, lflag);
	} while(rv < 0 && errno == EINTR);
	if(flag < 0) {
		std::cerr << "cloexec: error during setting of flag"
			<< std::endl;
		exit(1);
	}
}

<commit_msg>src/ios_posix.cpp: Added initialization<commit_after>#include"all_defines.hpp"

#include"aio.hpp"

#include<unistd.h>
#include<errno.h>
#include<signal.h>
#include<fcntl.h>

// used for error reporting before aborting
#include<iostream>

/*
POSIX-based asynchronous I/O and signal handling

Note that we don't program to the letter of the POSIX
specs: AFAIK no actual OS conforms perfectly to POSIX.
Preferably we try to be somewhat more resilient and
try to be more defensive when making system calls.
*/

/*self-pipe trick with SIGCHLD*/
static int sigchld_wr, sigchld_rd;
/*PLANNED (not yet implemented)
When compiled single_threaded, signal handlers also
write to sigchld_wr, in order to (hopefully!)
interrupt pending select()/poll()'s.  We don't depend
on this too much, because we will still raise flags
somewhere - we only depend on it to interrupt our
calls.
When compiled without single_threaded, signals are
blocked on all threads, then at initialization we
start a "hidden" thread whose sole purpose is to
wait for signals.
*/

/*SIGCHLD Known issues:
On Linux < 2.4, each thread's child processes belonged
only to that thread; this meant (1) SIGCHLD gets sent
exactly to the thread which launched that process, and
nobody else, and (2) a thread couldn't waitpid() on any
other thread's child process.  Both behaviors are not
POSIX compliant.  I also can't think of a way to handle
those properly, at least not with the fact that the
events system in `hl` may be launched from any of N
worker threads.

For Linux >= 2.4, a thread could now waitpid() on other
thread's child processes.  However, AFAIK (can't find
references regarding exact behavior) SIGCHLD is still
sent to the launching thread.

For Linux >= 2.6, we have NPTL, which is supposedly
correctly POSIX.

The code here *should* work on Linux 2.4, assuming that
write() from multiple threads simultaneously gets
properly handled (locked!!) by the OS.
*/

static void sigchld_handler(int) {
	ssize_t rv;
	char buf[1]; /*exact data doesn't matter*/
	do {
		errno = 0;
		rv = write(sigchld_wr, buf, 1);
		/*our signal installer actually
		masks out all other signals for
		this handler, so we shouldn't get
		EINTR, but better safe than
		sorry...
		*/
	} while(rv < 0 && errno == EINTR);
	/*All other errors get ignored!*/
	/*Errors:
	EAGAIN
		- we *shouldn't* mark sigchld_wr as nonblocking
	EBADF
		- pretty bad problem, means we failed to init,
		but we *do* check the pipe FD's at init time
		before we even install this sighandler.
	EFAULT
		- nasty, means buf is segfaultable, meaning we
		probably can't return anyway.
	EFBIG
		- not writing to a file, just a pipe.
	EINTR
		- handled by restarting the call.
	EINVAL
		- pretty bad problem, means we failed to init
	EIO
		- not writing to a file, just a pipe.
	ENOSPC
		- that's OK, we only care that there's data
		on the pipe to wake up the select() call
	EPIPE
		- will only happen when we're cleaning up
		and closing everything, so won't matter if
		signal is lost, we're already exiting.
	*/
}

/*empty signal handler, used for e.g. SIGPIPE*/
static void nihilistic_handler(int) { return; }

/*FD_CLOEXEC Known Issues:
Normally FD's will remain open across fork() and exec() calls.
This is useful when piping.  Unfortunately, we rarely even
want to use piping; almost definitely we will pipe only a few
FD's.

Thus, in the general case, when acquiring FD's from the OS,
we must very soon force them to FD_CLOEXEC.  However, this
has problems in a multithreaded implementation which fork()s.
It's possible for one thread to fork()+exec() while another
thread has just, say, called socket().  In this case the
returned socket will *not* have FD_CLOEXEC and will remain
open in the child process, reducing the number of useable
FD's in the process, as well as other weird stuff like tape
drives not rewinding...

This can be solved in many ways:
1.  Ignore CLOEXEC.  After fork() and before exec(), close
everything except for any redirections we want.  This means
about sysconf(_SC_OPEN_MAX) calls to close().  Now suppose
_SC_OPEN_MAX is 65,536...
2.  Like above.  But we just keep closing until we get N
consecutive EBADF failures for close(), where N is some big
number.  Hackish solution which depends on the probability
that FD numbers will skip at most N consecutive values.
3.  Keep track of the largest ever FD we ever see from any
source.  Just loop up to that instead of
sysconf(_SC_OPEN_MAX), which should be faster because the
largest FD will be much smaller than _SC_OPEN_MAX.  However
when multithreaded we have to rely on locks to keep track
of the largest seen FD anyway, so....
4.  Protect all open()+fcntl()/socket()+fcntl/pipe()+fcntl(),
as well as fork(), with a mutex.  While fork() will also
duplicate the mutex in the child, we do an exec() soon
anwyay, so the child's mutex copy gets implicitly deleted.

#4 above seems the best solution but it *might* mean that
some LIBC and other third-party library routines can't be
safely used (since they would internally open some FD's,
which we can't protect with a mutex).
*/
static void force_cloexec(int fd) {
	int flag;
	do {
		errno = 0;
		flag = fcntl(fd, F_GETFD);
	} while(flag < 0 && errno == EINTR);
	if(flag < 0) {
		std::cerr << "cloexec: error during getting of flag"
			<< std::endl;
		exit(1);
	}
	long lflag = ((long) (flag)) | FD_CLOEXEC;
	int rv;
	do {
		errno = 0;
		rv = fcntl(fd, F_SETFD, lflag);
	} while(rv < 0 && errno == EINTR);
	if(flag < 0) {
		std::cerr << "cloexec: error during setting of flag"
			<< std::endl;
		exit(1);
	}
}

void aio_initialize(void) {
	int rv;

	/*---------------------------------------------------------------------
	setup self-pipe
	---------------------------------------------------------------------*/
	int pipefds[2];
	errno = 0;
	rv = pipe(pipefds);
	if(rv < 0) {
		if(errno == ENFILE) {
			std::cerr << "aio_initialize: too many open "
				<< "files during self-pipe "
				<< "initialization, oh noes!"
				<< std::endl;
		} else {
			std::cerr << "aio_initialize: Unexpected error "
				<< "during self-pipe initialization"
				<< std::endl;
		}
		exit(1);
	}
	force_cloexec(pipefds[0]);
	force_cloexec(pipefds[1]);
	sigchld_rd = pipefds[0]; sigchld_wr = pipefds[1];

	/*---------------------------------------------------------------------
	setup SIGCHLD and SIGPIPE
	---------------------------------------------------------------------*/
	struct sigaction sa;
	/*SIGCHLD*/
	sigfillset(&sa.sa_mask); /*block everything inside handler*/
	sa.sa_flags = 0;
	sa.sa_handler = &sigchld_handler;
	rv = sigaction(SIGCHLD, &sa, 0);
	if(rv < 0) {
		std::cerr << "aio_initialize: Unexpected error during "
			<< "SIGCHLD handler setup."
			<< std::endl;
		exit(1);
	}
	/*SIGPIPE*/
	sigfillset(&sa.sa_mask);
	sa.sa_flags = 0;
	sa.sa_handler = &nihilistic_handler;
	rv = sigaction(SIGPIPE, &sa, 0);
	if(rv < 0) {
		std::cerr << "aio_initialize: Unexpected error during "
			<< "SIGPIPE handler setup."
			<< std::endl;
		exit(1);
	}

}

void aio_deinitialize(void) {
	/*TODO: figure out what to clean up, if any*/
}

<|endoftext|>


<commit_before>// Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
#ifndef __RAPICORN_CORE_OBJECTS_HH__
#define __RAPICORN_CORE_OBJECTS_HH__

#include <rcore/utilities.hh>
#include <rcore/thread.hh>
#include <rcore/aidaprops.hh>

namespace Rapicorn {

// == VirtualTypeid ==
class VirtualTypeid {
protected:
  virtual      ~VirtualTypeid      ();
public:
  String        typeid_name        ();
  static String cxx_demangle       (const char *mangled_identifier);
};

// == ClassDoctor (used for private class copies) ==
#ifdef  __RAPICORN_BUILD__
class ClassDoctor;
#else
class ClassDoctor {};
#endif

// == Deletable ==
/**
 * Deletable is a virtual base class that can be derived from (usually with
 * public virtual) to ensure an object has a vtable and a virtual destructor.
 * Also, it allows deletion hooks to be called during the objects destructor,
 * by deriving from Rapicorn::Deletable::DeletionHook. No extra per-object space is
 * consumed to allow deletion hooks, which makes Deletable a suitable base
 * type for classes that may or may not need this feature (e.g. objects that
 * can but often aren't used for signal handler connections).
 */
struct Deletable : public virtual VirtualTypeid {
  /**
   * DeletionHook is the base implementation class for hooks which are hooked
   * up into the deletion phase of a Rapicorn::Deletable.
   */
  class DeletionHook {
    DeletionHook    *prev;
    DeletionHook    *next;
    friend class Deletable;
  protected:
    virtual     ~DeletionHook          (); /* { if (deletable) deletable_remove_hook (deletable); deletable = NULL; } */
    virtual void monitoring_deletable  (Deletable &deletable) = 0;
    virtual void dismiss_deletable     () = 0;
  public:
    explicit     DeletionHook          () : prev (NULL), next (NULL) {}
    bool         deletable_add_hook    (void      *any)              { return false; }
    bool         deletable_add_hook    (Deletable *deletable);
    bool         deletable_remove_hook (void      *any)              { return false; }
    bool         deletable_remove_hook (Deletable *deletable);
  };
private:
  void           add_deletion_hook     (DeletionHook *hook);
  void           remove_deletion_hook  (DeletionHook *hook);
protected:
  void           invoke_deletion_hooks ();
  virtual       ~Deletable             ();
};

// == DataListContainer ==
/**
 * By using a DataKey, DataListContainer objects allow storage and retrieval of custom data members in a typesafe fashion.
 * The custom data members will initially default to DataKey::fallback and are deleted by the DataListContainer destructor.
 * Example: @snippet rcore/tests/datalist.cc DataListContainer-EXAMPLE
 */
class DataListContainer {
  DataList data_list;
public: /// @name Accessing custom data members
  /// Assign @a data to the custom keyed data member, deletes any previously set data.
  template<typename Type> inline void set_data    (DataKey<Type> *key, Type data) { data_list.set (key, data); }
  /// Retrieve contents of the custom keyed data member, returns DataKey::fallback if nothing was set.
  template<typename Type> inline Type get_data    (DataKey<Type> *key) const      { return data_list.get (key); }
  /// Swap @a data with the current contents of the custom keyed data member, returns the current contents.
  template<typename Type> inline Type swap_data   (DataKey<Type> *key, Type data) { return data_list.swap (key, data); }
  /// Removes and returns the current contents of the custom keyed data member without deleting it.
  template<typename Type> inline Type swap_data   (DataKey<Type> *key)            { return data_list.swap (key); }
  /// Delete the current contents of the custom keyed data member, invokes DataKey::destroy.
  template<typename Type> inline void delete_data (DataKey<Type> *key)            { data_list.del (key); }
};

// == BaseObject ==
class BaseObject : public virtual Deletable, public virtual Aida::PropertyHostInterface {
protected:
  class                    InterfaceMatcher;
  template<class C>  class InterfaceMatch;
  virtual void                 dispose   ();
public:
};
typedef Aida::PropertyList PropertyList; // import PropertyList from Aida namespace
typedef Aida::Property     Property;     // import Property from Aida namespace
class NullInterface : std::exception {};

struct BaseObject::InterfaceMatcher {
  explicit      InterfaceMatcher (const String &ident) : ident_ (ident), match_found_ (false) {}
  bool          done             () const { return match_found_; }
  virtual  bool match            (BaseObject *object, const String &ident = String()) = 0;
  RAPICORN_CLASS_NON_COPYABLE (InterfaceMatcher);
protected:
  const String &ident_;
  bool          match_found_;
};

template<class C>
struct BaseObject::InterfaceMatch : BaseObject::InterfaceMatcher {
  typedef C&    Result;
  explicit      InterfaceMatch  (const String &ident) : InterfaceMatcher (ident), instance_ (NULL) {}
  C&            result          (bool may_throw) const;
  virtual bool  match           (BaseObject *obj, const String &ident);
protected:
  C            *instance_;
};
template<class C>
struct BaseObject::InterfaceMatch<C&> : InterfaceMatch<C> {
  explicit      InterfaceMatch  (const String &ident) : InterfaceMatch<C> (ident) {}
};
template<class C>
struct BaseObject::InterfaceMatch<C*> : InterfaceMatch<C> {
  typedef C*    Result;
  explicit      InterfaceMatch  (const String &ident) : InterfaceMatch<C> (ident) {}
  C*            result          (bool may_throw) const { return InterfaceMatch<C>::instance_; }
};

template<class C> bool
BaseObject::InterfaceMatch<C>::match (BaseObject *obj, const String &ident)
{
  if (!instance_)
    {
      const String &id = ident_;
      if (id.empty() || id == ident)
        {
          instance_ = dynamic_cast<C*> (obj);
          match_found_ = instance_ != NULL;
        }
    }
  return match_found_;
}

template<class C> C&
BaseObject::InterfaceMatch<C>::result (bool may_throw) const
{
  if (!this->instance_ && may_throw)
    throw NullInterface();
  return *this->instance_;
}

// == ReferenceCountable ==
class ReferenceCountable : public virtual BaseObject {
  volatile mutable uint32 ref_field;
  static const uint32     FLOATING_FLAG = 1 << 31;
  inline uint32 ref_get    () const                             { return Lib::atomic_load (&ref_field); }
  inline bool   ref_cas    (uint32 oldv, uint32 newv) const     { return __sync_bool_compare_and_swap (&ref_field, oldv, newv); }
  inline void   fast_ref   () const;
  inline void   fast_unref () const;
  void          real_unref () const;
  RAPICORN_CLASS_NON_COPYABLE (ReferenceCountable);
protected:
  virtual      ~ReferenceCountable ();
  virtual void  delete_this        ();
  virtual void  pre_finalize       ();
  virtual void  finalize           ();
  inline uint32 ref_count          () const                     { return ref_get() & ~FLOATING_FLAG; }
public:
  bool     floating           () const                          { return 0 != (ref_get() & FLOATING_FLAG); }
  void     ref                () const                          { fast_ref(); }
  void     ref_sink           () const;
  bool     finalizing         () const                          { return ref_count() < 1; }
  void     unref              () const                          { fast_unref(); }
  void     ref_diag           (const char *msg = NULL) const;
  explicit ReferenceCountable (uint allow_stack_magic = 0);
  template<class Obj> static Obj& ref      (Obj &obj) { obj.ref();       return obj; }
  template<class Obj> static Obj* ref      (Obj *obj) { obj->ref();      return obj; }
  template<class Obj> static Obj& ref_sink (Obj &obj) { obj.ref_sink();  return obj; }
  template<class Obj> static Obj* ref_sink (Obj *obj) { obj->ref_sink(); return obj; }
  template<class Obj> static void unref    (Obj &obj) { obj.unref(); }
  template<class Obj> static void unref    (Obj *obj) { obj->unref(); }
};
template<class Obj> static Obj& ref      (Obj &obj) { obj.ref();       return obj; }
template<class Obj> static Obj* ref      (Obj *obj) { obj->ref();      return obj; }
template<class Obj> static Obj& ref_sink (Obj &obj) { obj.ref_sink();  return obj; }
template<class Obj> static Obj* ref_sink (Obj *obj) { obj->ref_sink(); return obj; }
template<class Obj> static void unref    (Obj &obj) { obj.unref(); }
template<class Obj> static void unref    (Obj *obj) { obj->unref(); }

// == Implementation Details ==
inline void
ReferenceCountable::fast_ref () const
{
  // fast-path: use one atomic op and deferred checks
  uint32 old_ref = __sync_fetch_and_add (&ref_field, 1);
  uint32 new_ref = old_ref + 1;                       // ...and_add (,1)
  RAPICORN_ASSERT (old_ref & ~FLOATING_FLAG);         // check dead objects
  RAPICORN_ASSERT (new_ref & ~FLOATING_FLAG);         // check overflow
}

inline void
ReferenceCountable::fast_unref () const
{
  RAPICORN_CFENCE;
  uint32 old_ref = ref_field; // skip read-barrier for fast-path
  if (RAPICORN_LIKELY (old_ref & ~(FLOATING_FLAG | 1)) && // old_ref >= 2
      RAPICORN_LIKELY (ref_cas (old_ref, old_ref - 1)))
    return; // trying fast-path with single atomic op
  real_unref();
}

} // Rapicorn

#endif // __RAPICORN_CORE_OBJECTS_HH__
<commit_msg>RCORE: BaseObject: derive from ReferenceCountable (not vice versa) and Aida::ImplicitBase<commit_after>// Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
#ifndef __RAPICORN_CORE_OBJECTS_HH__
#define __RAPICORN_CORE_OBJECTS_HH__

#include <rcore/utilities.hh>
#include <rcore/thread.hh>
#include <rcore/aidaprops.hh>

namespace Rapicorn {

// == VirtualTypeid ==
class VirtualTypeid {
protected:
  virtual      ~VirtualTypeid      ();
public:
  String        typeid_name        ();
  static String cxx_demangle       (const char *mangled_identifier);
};

// == ClassDoctor (used for private class copies) ==
#ifdef  __RAPICORN_BUILD__
class ClassDoctor;
#else
class ClassDoctor {};
#endif

// == Deletable ==
/**
 * Deletable is a virtual base class that can be derived from (usually with
 * public virtual) to ensure an object has a vtable and a virtual destructor.
 * Also, it allows deletion hooks to be called during the objects destructor,
 * by deriving from Rapicorn::Deletable::DeletionHook. No extra per-object space is
 * consumed to allow deletion hooks, which makes Deletable a suitable base
 * type for classes that may or may not need this feature (e.g. objects that
 * can but often aren't used for signal handler connections).
 */
struct Deletable : public virtual VirtualTypeid {
  /**
   * DeletionHook is the base implementation class for hooks which are hooked
   * up into the deletion phase of a Rapicorn::Deletable.
   */
  class DeletionHook {
    DeletionHook    *prev;
    DeletionHook    *next;
    friend class Deletable;
  protected:
    virtual     ~DeletionHook          (); /* { if (deletable) deletable_remove_hook (deletable); deletable = NULL; } */
    virtual void monitoring_deletable  (Deletable &deletable) = 0;
    virtual void dismiss_deletable     () = 0;
  public:
    explicit     DeletionHook          () : prev (NULL), next (NULL) {}
    bool         deletable_add_hook    (void      *any)              { return false; }
    bool         deletable_add_hook    (Deletable *deletable);
    bool         deletable_remove_hook (void      *any)              { return false; }
    bool         deletable_remove_hook (Deletable *deletable);
  };
private:
  void           add_deletion_hook     (DeletionHook *hook);
  void           remove_deletion_hook  (DeletionHook *hook);
protected:
  void           invoke_deletion_hooks ();
  virtual       ~Deletable             ();
};

// == DataListContainer ==
/**
 * By using a DataKey, DataListContainer objects allow storage and retrieval of custom data members in a typesafe fashion.
 * The custom data members will initially default to DataKey::fallback and are deleted by the DataListContainer destructor.
 * Example: @snippet rcore/tests/datalist.cc DataListContainer-EXAMPLE
 */
class DataListContainer {
  DataList data_list;
public: /// @name Accessing custom data members
  /// Assign @a data to the custom keyed data member, deletes any previously set data.
  template<typename Type> inline void set_data    (DataKey<Type> *key, Type data) { data_list.set (key, data); }
  /// Retrieve contents of the custom keyed data member, returns DataKey::fallback if nothing was set.
  template<typename Type> inline Type get_data    (DataKey<Type> *key) const      { return data_list.get (key); }
  /// Swap @a data with the current contents of the custom keyed data member, returns the current contents.
  template<typename Type> inline Type swap_data   (DataKey<Type> *key, Type data) { return data_list.swap (key, data); }
  /// Removes and returns the current contents of the custom keyed data member without deleting it.
  template<typename Type> inline Type swap_data   (DataKey<Type> *key)            { return data_list.swap (key); }
  /// Delete the current contents of the custom keyed data member, invokes DataKey::destroy.
  template<typename Type> inline void delete_data (DataKey<Type> *key)            { data_list.del (key); }
};

// == ReferenceCountable ==
class ReferenceCountable : public virtual Deletable {
  volatile mutable uint32 ref_field;
  static const uint32     FLOATING_FLAG = 1 << 31;
  inline uint32 ref_get    () const                             { return Lib::atomic_load (&ref_field); }
  inline bool   ref_cas    (uint32 oldv, uint32 newv) const     { return __sync_bool_compare_and_swap (&ref_field, oldv, newv); }
  inline void   fast_ref   () const;
  inline void   fast_unref () const;
  void          real_unref () const;
  RAPICORN_CLASS_NON_COPYABLE (ReferenceCountable);
protected:
  virtual      ~ReferenceCountable ();
  virtual void  delete_this        ();
  virtual void  pre_finalize       ();
  virtual void  finalize           ();
  inline uint32 ref_count          () const                     { return ref_get() & ~FLOATING_FLAG; }
public:
  bool     floating           () const                          { return 0 != (ref_get() & FLOATING_FLAG); }
  void     ref                () const                          { fast_ref(); }
  void     ref_sink           () const;
  bool     finalizing         () const                          { return ref_count() < 1; }
  void     unref              () const                          { fast_unref(); }
  void     ref_diag           (const char *msg = NULL) const;
  explicit ReferenceCountable (uint allow_stack_magic = 0);
  template<class Obj> static Obj& ref      (Obj &obj) { obj.ref();       return obj; }
  template<class Obj> static Obj* ref      (Obj *obj) { obj->ref();      return obj; }
  template<class Obj> static Obj& ref_sink (Obj &obj) { obj.ref_sink();  return obj; }
  template<class Obj> static Obj* ref_sink (Obj *obj) { obj->ref_sink(); return obj; }
  template<class Obj> static void unref    (Obj &obj) { obj.unref(); }
  template<class Obj> static void unref    (Obj *obj) { obj->unref(); }
};
template<class Obj> static Obj& ref      (Obj &obj) { obj.ref();       return obj; }
template<class Obj> static Obj* ref      (Obj *obj) { obj->ref();      return obj; }
template<class Obj> static Obj& ref_sink (Obj &obj) { obj.ref_sink();  return obj; }
template<class Obj> static Obj* ref_sink (Obj *obj) { obj->ref_sink(); return obj; }
template<class Obj> static void unref    (Obj &obj) { obj.unref(); }
template<class Obj> static void unref    (Obj *obj) { obj->unref(); }

// == BaseObject ==
class BaseObject : public virtual ReferenceCountable, public virtual Aida::ImplicitBase {
protected:
  class                    InterfaceMatcher;
  template<class C>  class InterfaceMatch;
  virtual void                 dispose   ();
public:
};
typedef Aida::PropertyList PropertyList; // import PropertyList from Aida namespace
typedef Aida::Property     Property;     // import Property from Aida namespace
class NullInterface : std::exception {};

struct BaseObject::InterfaceMatcher {
  explicit      InterfaceMatcher (const String &ident) : ident_ (ident), match_found_ (false) {}
  bool          done             () const { return match_found_; }
  virtual  bool match            (BaseObject *object, const String &ident = String()) = 0;
  RAPICORN_CLASS_NON_COPYABLE (InterfaceMatcher);
protected:
  const String &ident_;
  bool          match_found_;
};

template<class C>
struct BaseObject::InterfaceMatch : BaseObject::InterfaceMatcher {
  typedef C&    Result;
  explicit      InterfaceMatch  (const String &ident) : InterfaceMatcher (ident), instance_ (NULL) {}
  C&            result          (bool may_throw) const;
  virtual bool  match           (BaseObject *obj, const String &ident);
protected:
  C            *instance_;
};
template<class C>
struct BaseObject::InterfaceMatch<C&> : InterfaceMatch<C> {
  explicit      InterfaceMatch  (const String &ident) : InterfaceMatch<C> (ident) {}
};
template<class C>
struct BaseObject::InterfaceMatch<C*> : InterfaceMatch<C> {
  typedef C*    Result;
  explicit      InterfaceMatch  (const String &ident) : InterfaceMatch<C> (ident) {}
  C*            result          (bool may_throw) const { return InterfaceMatch<C>::instance_; }
};

template<class C> bool
BaseObject::InterfaceMatch<C>::match (BaseObject *obj, const String &ident)
{
  if (!instance_)
    {
      const String &id = ident_;
      if (id.empty() || id == ident)
        {
          instance_ = dynamic_cast<C*> (obj);
          match_found_ = instance_ != NULL;
        }
    }
  return match_found_;
}

template<class C> C&
BaseObject::InterfaceMatch<C>::result (bool may_throw) const
{
  if (!this->instance_ && may_throw)
    throw NullInterface();
  return *this->instance_;
}

// == Implementation Details ==
inline void
ReferenceCountable::fast_ref () const
{
  // fast-path: use one atomic op and deferred checks
  uint32 old_ref = __sync_fetch_and_add (&ref_field, 1);
  uint32 new_ref = old_ref + 1;                       // ...and_add (,1)
  RAPICORN_ASSERT (old_ref & ~FLOATING_FLAG);         // check dead objects
  RAPICORN_ASSERT (new_ref & ~FLOATING_FLAG);         // check overflow
}

inline void
ReferenceCountable::fast_unref () const
{
  RAPICORN_CFENCE;
  uint32 old_ref = ref_field; // skip read-barrier for fast-path
  if (RAPICORN_LIKELY (old_ref & ~(FLOATING_FLAG | 1)) && // old_ref >= 2
      RAPICORN_LIKELY (ref_cas (old_ref, old_ref - 1)))
    return; // trying fast-path with single atomic op
  real_unref();
}

} // Rapicorn

#endif // __RAPICORN_CORE_OBJECTS_HH__
<|endoftext|>


<commit_before>/*
  Copyright (c) 2014
  Author: Jeff Weisberg <jaw @ solvemedia.com>
  Created: 2014-Mar-27 14:55 (EDT)
  Function: todo state transitions

*/

#define CURRENT_SUBSYSTEM	'j'

#include "defs.h"
#include "diag.h"
#include "config.h"
#include "misc.h"
#include "network.h"
#include "runmode.h"
#include "thread.h"
#include "lock.h"
#include "peers.h"
#include "queued.h"
#include "job.h"

#include <unistd.h>

#include "mrmagoo.pb.h"
#include "std_reply.pb.h"

#define IO_TIMEOUT		30

#define XFERMAX			48
#define TODORETRYDELAY		15
#define TODORETRYQUICK	 	5
#define SERVERTASKMAX		10
#define SERVERXFERMAX		20
#define SERVERTASKDELAY		2

void
Job::derunning_x(ToDo *t){
    _running.remove(t);
}

void
Job::enrunning_x(ToDo *t){
    _pending.remove(t);
    _running.push_back(t);
}

void
Job::depending_x(ToDo *t){
    _pending.remove(t);
}

void
ToDo::retry_or_abort(bool did_timeout){

    if( _job->_n_fails > _job->current_width() * TODOMAXFAIL / 2){
        _job->abort();
        return;
    }

    if( ++_tries >= TODOMAXFAIL ){
        const char *serv = _job->_servers[_serveridx]->name.c_str();

        // cannot replace a map task
        // only replace if the failure is likely because the server is down
        if( _job->_stepno && (did_timeout || !peerdb->is_it_up(serv) )){
            maybe_replace(1);
        }else{
            _job->abort();
        }
        return;
    }

    // retry
    int delay = (did_timeout ? TODORETRYDELAY * _tries : TODORETRYQUICK);
    _delay_until = lr_now() + delay/2 + random_n( delay/2 ) + random_n( delay/2 );

    pend();
}

int
XferToDo::maybe_replace(bool import){
    // wait until the task fails, and replace that
    return 0;
}

int
TaskToDo::maybe_replace(bool important){

    if( replace() )
        return 1;

    if( important ) _job->abort();

    return 0;
}

void
TaskToDo::failed(bool did_timeout){

    if( _state != JOB_TODO_STATE_RUNNING ) return;

    _job->kvetch("task %s failed", _xid.c_str());
    _job->derunning_x(this);
    _job->_servers[ _serveridx ]->_n_task_running --;
    _job->_servers[ _serveridx ]->_n_fails ++;
    _job->_n_task_running --;
    _job->_n_fails ++;
    _state = JOB_TODO_STATE_FINISHED;

    TaskToDo *alt = _replaces ? _replaces : _replacedby;
    if( alt ){
        // if there is an alternate task still running, let it finish
        if( ! alt->is_finished() ) return;
    }

    retry_or_abort(did_timeout);
}

void
XferToDo::failed(bool did_timeout){

    _job->kvetch("xfer %s failed file %s host %s - %s", _xid.c_str(), _g.filename().c_str(),
                 _job->_servers[_serveridx]->name.c_str(), _g.location(0).c_str());

    _job->derunning_x(this);
    _job->_servers[ _serveridx ]->_n_xfer_running --;
    _job->_servers[ _peeridx   ]->_n_xfer_peering --;
    _job->_servers[ _serveridx ]->_n_fails ++;
    _job->_n_xfer_running --;
    _job->_n_fails ++;
    _state = JOB_TODO_STATE_FINISHED;

    retry_or_abort(did_timeout);
}

void
ToDo::timedout(void){

    _job->kvetch("task %s timed out", _xid.c_str());

    abort();
    failed(1);
}

static void *
todo_start(void *x){
    ToDo *t = (ToDo*)x;
    t->start();
}

int
ToDo::start_check(void){
    // are we good to go?
    if( _job->_n_threads >= JOBMAXTHREAD ) return 0;
    if( _delay_until > lr_now() ) return 0;
    return 1;
}

void
ToDo::start_common(void){

    _state = JOB_TODO_STATE_RUNNING;
    _last_status = lr_now();
    _job->_n_threads ++;
    _job->_pending.remove(this);
    _job->_running.push_back(this);
}

int
TaskToDo::maybe_start(void){

    Server *svr = _job->_servers[ _serveridx ];

    // too much running?
    if( svr->_n_task_running >= SERVERTASKMAX ) return 0;
    if( svr->_last_task + SERVERTASKDELAY > lr_now() ) return 0;
    if( ! start_check() ) return 0;

    // check prereqs
    if( !_prerequisite.empty() ){
        for(list<ToDo*>::iterator it=_prerequisite.begin(); it !=_prerequisite.end(); it++){
            ToDo *t = *it;

            if( ! t->is_finished() ) return 0;
        }

        _prerequisite.clear();
    }

    // ok, let's start....

    // we could create these at task construction time, but deferring until here
    // saves us effort if the job is aborted
    if( !_tries )
        create_deles();

    _job->inform("starting task %s - %s on %s", _xid.c_str(), _g.phase().c_str(),
                 _job->_servers[ _serveridx ]->name.c_str());

    svr->_n_task_running ++;
    svr->_last_task = lr_now();
    _job->_n_task_running ++;
    _job->_n_tasks_run ++;

    _run_start   = lr_now();
    start_common();

    DEBUG("started");
    start_thread(todo_start, (void*)(ToDo*)this);
    return 1;
}

int
XferToDo::maybe_start(void){

    // too much running?
    if( _job->_servers[ _serveridx ]->_n_xfer_running >= SERVERXFERMAX ) return 0;
    if( _job->_servers[ _peeridx   ]->_n_xfer_peering >= SERVERXFERMAX ) return 0;
    if( _job->_n_xfer_running >= XFERMAX ) return 0;
    if( ! start_check() ) return 0;

    _job->inform("starting xfer %s - %s : %s", _xid.c_str(), _g.filename().c_str(),
                 _job->_servers[ _serveridx ]->name.c_str());

    _job->_servers[ _serveridx ]->_n_xfer_running ++;
    _job->_servers[ _peeridx   ]->_n_xfer_peering ++;
    _job->_n_xfer_running ++;
    _job->_n_xfers_run ++;
    start_common();

    DEBUG("started");
    start_thread(todo_start, (void*)(ToDo*)this);
    return 1;
}

void
Job::thread_done(void){

    _lock.w_lock();
    _n_threads--;
    _lock.w_unlock();

    try_to_do_something( _n_threads ? 0 : 1 );
}

int
TaskToDo::start(void){

    if( ! make_request( _job->_servers[_serveridx], PHMT_MR_TASKCREATE, &_g, IO_TIMEOUT ) ){
        string status = "FAILED";
        _job->update( &_xid, &status, 0, 0 );
    }

    _job->thread_done();
}

int
XferToDo::start(void){

    if( ! make_request( _job->_servers[_serveridx], PHMT_MR_FILEXFER, &_g, IO_TIMEOUT ) ){
        string status = "FAILED";
        _job->update( &_xid, &status, 0, 0 );
    }

    _job->thread_done();
}

void
TaskToDo::abort(void){
    DEBUG("abort task");
    ACPMRMTaskAbort req;

    if( _state != JOB_TODO_STATE_RUNNING ) return;

    req.set_jobid( _job->_id );
    req.set_taskid( _xid.c_str() );

    // udp fire+forget
    toss_request(udp4_fd, _job->_servers[ _serveridx ], PHMT_MR_TASKABORT, &req );
}

void
TaskToDo::discard(void){

    DEBUG("discard %s %d", _xid.c_str(), _state);
    if( _state == JOB_TODO_STATE_PENDING ){
        _job->depending_x(this);
        _state = JOB_TODO_STATE_FINISHED;
        return;
    }

    if( _state == JOB_TODO_STATE_RUNNING ){
        _job->derunning_x(this);
        _state = JOB_TODO_STATE_FINISHED;
        _job->_servers[ _serveridx ]->_n_task_running --;
        _job->_n_task_running --;
        return;
    }
}

// remove task from run list, send abort request (tcp)
void
TaskToDo::cancel(void){
    ACPMRMTaskAbort req;

    int prevstate = _state;

    discard();

    if( prevstate != JOB_TODO_STATE_RUNNING ) return;

    req.set_jobid( _job->_id );
    req.set_taskid( _xid.c_str() );

    make_request( _job->_servers[_serveridx], PHMT_MR_TASKABORT, &req, IO_TIMEOUT );
}

// same, but udp
void
TaskToDo::cancel_light(void){
    ACPMRMTaskAbort req;

    int prevstate = _state;

    discard();

    if( prevstate != JOB_TODO_STATE_RUNNING ) return;

    req.set_jobid( _job->_id );
    req.set_taskid( _xid.c_str() );

    toss_request(udp4_fd, _job->_servers[ _serveridx ], PHMT_MR_TASKABORT, &req );
}

void
XferToDo::abort(void){
    DEBUG("abort xfer");
    // there is no abort method, just wait...
}
void
XferToDo::cancel(void){
    DEBUG("cancel xfer");
    // there is no cancel method, just wait...
}

void
TaskToDo::finished(int amount){

    _job->inform("task %s finished", _xid.c_str());
    _job->derunning_x(this);

    if( _state == JOB_TODO_STATE_RUNNING ){
        _job->_servers[ _serveridx ]->_n_task_running --;
        _job->_n_task_running --;
    }
    _state = JOB_TODO_STATE_FINISHED;

    // so how slow was it?
    if( amount ){
        _run_time = amount;
    }else{
        _run_time = lr_now() - _run_start;
    }

    _job->_task_run_time += _run_time;

    create_xfers();

    // if this was replaced, abort the replacement (and vv)
    if( _replacedby ) _replacedby->cancel_light();
    if( _replaces   ) _replaces->cancel_light();

}

void
XferToDo::finished(int amount){

    _job->derunning_x(this);
    _state = JOB_TODO_STATE_FINISHED;
    _job->_servers[ _serveridx ]->_n_xfer_running --;
    _job->_servers[ _peeridx   ]->_n_xfer_peering --;
    _job->_n_xfer_running --;

    // tally up file xfer sizes
    _job->_plan[ _job->_stepno ]->_xfer_size += amount;
    _job->_plan[ _job->_stepno ]->_n_xfers_run ++;

}

XferToDo::XferToDo(Job *j, const string *name, int src, int dst){

    unique( &_xid );
    _job         = j;
    _serveridx   = dst;
    _peeridx     = src;
    _state       = JOB_TODO_STATE_PENDING;
    _tries       = 0;
    _delay_until = 0;

    _g.set_jobid(   j->_id );
    _g.set_console( j->_g.console().c_str() );
    _g.set_master(  myipandport.c_str() );
    _g.set_copyid(  _xid.c_str() );
    _g.set_filename( name->c_str() );
    _g.add_location( _job->_servers[src]->name.c_str() );

}

void
TaskToDo::create_xfers(void){

    // files from last step can stay where they are
    if( _job->_stepno == _job->_plan.size() - 1 ) return;

    // for all outfiles
    // new Xfer -> job pend

    int nserv = _job->_servers.size();
    int noutf = _g.outfile_size();

    for(int i=0; i<noutf; i++){
        int dst = i % nserv;

        // if file will be processed on this server, we do not need to copy it
        // instead, make a backup copy on another server
        if( _serveridx == dst ) dst = (i+1) % nserv;

        // just one server?
        if( _serveridx == dst ) continue;

        XferToDo *x = new XferToDo(_job, &_g.outfile(i), _serveridx, dst);
        _job->_pending.push_back(x);
        _job->_xfers.push_back(x);
    }
}

<commit_msg>verbosity<commit_after>/*
  Copyright (c) 2014
  Author: Jeff Weisberg <jaw @ solvemedia.com>
  Created: 2014-Mar-27 14:55 (EDT)
  Function: todo state transitions

*/

#define CURRENT_SUBSYSTEM	'j'

#include "defs.h"
#include "diag.h"
#include "config.h"
#include "misc.h"
#include "network.h"
#include "runmode.h"
#include "thread.h"
#include "lock.h"
#include "peers.h"
#include "queued.h"
#include "job.h"

#include <unistd.h>

#include "mrmagoo.pb.h"
#include "std_reply.pb.h"

#define IO_TIMEOUT		30

#define XFERMAX			48
#define TODORETRYDELAY		15
#define TODORETRYQUICK	 	5
#define SERVERTASKMAX		10
#define SERVERXFERMAX		20
#define SERVERTASKDELAY		2

void
Job::derunning_x(ToDo *t){
    _running.remove(t);
}

void
Job::enrunning_x(ToDo *t){
    _pending.remove(t);
    _running.push_back(t);
}

void
Job::depending_x(ToDo *t){
    _pending.remove(t);
}

void
ToDo::retry_or_abort(bool did_timeout){

    if( _job->_n_fails > _job->current_width() * TODOMAXFAIL / 2){
        _job->abort();
        return;
    }

    if( ++_tries >= TODOMAXFAIL ){
        const char *serv = _job->_servers[_serveridx]->name.c_str();

        // cannot replace a map task
        // only replace if the failure is likely because the server is down
        if( _job->_stepno && (did_timeout || !peerdb->is_it_up(serv) )){
            maybe_replace(1);
        }else{
            _job->abort();
        }
        return;
    }

    // retry
    int delay = (did_timeout ? TODORETRYDELAY * _tries : TODORETRYQUICK);
    _delay_until = lr_now() + delay/2 + random_n( delay/2 ) + random_n( delay/2 );

    pend();
}

int
XferToDo::maybe_replace(bool import){
    // wait until the task fails, and replace that
    return 0;
}

int
TaskToDo::maybe_replace(bool important){

    if( replace() )
        return 1;

    if( important ) _job->abort();

    return 0;
}

void
TaskToDo::failed(bool did_timeout){

    if( _state != JOB_TODO_STATE_RUNNING ) return;

    _job->kvetch("task %s failed on %s", _xid.c_str(), _job->_servers[_serveridx]->name.c_str());
    _job->derunning_x(this);
    _job->_servers[ _serveridx ]->_n_task_running --;
    _job->_servers[ _serveridx ]->_n_fails ++;
    _job->_n_task_running --;
    _job->_n_fails ++;
    _state = JOB_TODO_STATE_FINISHED;

    TaskToDo *alt = _replaces ? _replaces : _replacedby;
    if( alt ){
        // if there is an alternate task still running, let it finish
        if( ! alt->is_finished() ) return;
    }

    retry_or_abort(did_timeout);
}

void
XferToDo::failed(bool did_timeout){

    _job->kvetch("xfer %s failed file %s host %s - %s", _xid.c_str(), _g.filename().c_str(),
                 _job->_servers[_serveridx]->name.c_str(), _g.location(0).c_str());

    _job->derunning_x(this);
    _job->_servers[ _serveridx ]->_n_xfer_running --;
    _job->_servers[ _peeridx   ]->_n_xfer_peering --;
    _job->_servers[ _serveridx ]->_n_fails ++;
    _job->_n_xfer_running --;
    _job->_n_fails ++;
    _state = JOB_TODO_STATE_FINISHED;

    retry_or_abort(did_timeout);
}

void
ToDo::timedout(void){

    _job->kvetch("task %s timed out", _xid.c_str());

    abort();
    failed(1);
}

static void *
todo_start(void *x){
    ToDo *t = (ToDo*)x;
    t->start();
}

int
ToDo::start_check(void){
    // are we good to go?
    if( _job->_n_threads >= JOBMAXTHREAD ) return 0;
    if( _delay_until > lr_now() ) return 0;
    return 1;
}

void
ToDo::start_common(void){

    _state = JOB_TODO_STATE_RUNNING;
    _last_status = lr_now();
    _job->_n_threads ++;
    _job->_pending.remove(this);
    _job->_running.push_back(this);
}

int
TaskToDo::maybe_start(void){

    Server *svr = _job->_servers[ _serveridx ];

    // too much running?
    if( svr->_n_task_running >= SERVERTASKMAX ) return 0;
    if( svr->_last_task + SERVERTASKDELAY > lr_now() ) return 0;
    if( ! start_check() ) return 0;

    // check prereqs
    if( !_prerequisite.empty() ){
        for(list<ToDo*>::iterator it=_prerequisite.begin(); it !=_prerequisite.end(); it++){
            ToDo *t = *it;

            if( ! t->is_finished() ) return 0;
        }

        _prerequisite.clear();
    }

    // ok, let's start....

    // we could create these at task construction time, but deferring until here
    // saves us effort if the job is aborted
    if( !_tries )
        create_deles();

    _job->inform("starting task %s - %s on %s", _xid.c_str(), _g.phase().c_str(),
                 _job->_servers[ _serveridx ]->name.c_str());

    svr->_n_task_running ++;
    svr->_last_task = lr_now();
    _job->_n_task_running ++;
    _job->_n_tasks_run ++;

    _run_start   = lr_now();
    start_common();

    DEBUG("started");
    start_thread(todo_start, (void*)(ToDo*)this);
    return 1;
}

int
XferToDo::maybe_start(void){

    // too much running?
    if( _job->_servers[ _serveridx ]->_n_xfer_running >= SERVERXFERMAX ) return 0;
    if( _job->_servers[ _peeridx   ]->_n_xfer_peering >= SERVERXFERMAX ) return 0;
    if( _job->_n_xfer_running >= XFERMAX ) return 0;
    if( ! start_check() ) return 0;

    _job->inform("starting xfer %s - %s : %s", _xid.c_str(), _g.filename().c_str(),
                 _job->_servers[ _serveridx ]->name.c_str());

    _job->_servers[ _serveridx ]->_n_xfer_running ++;
    _job->_servers[ _peeridx   ]->_n_xfer_peering ++;
    _job->_n_xfer_running ++;
    _job->_n_xfers_run ++;
    start_common();

    DEBUG("started");
    start_thread(todo_start, (void*)(ToDo*)this);
    return 1;
}

void
Job::thread_done(void){

    _lock.w_lock();
    _n_threads--;
    _lock.w_unlock();

    try_to_do_something( _n_threads ? 0 : 1 );
}

int
TaskToDo::start(void){

    if( ! make_request( _job->_servers[_serveridx], PHMT_MR_TASKCREATE, &_g, IO_TIMEOUT ) ){
        string status = "FAILED";
        _job->update( &_xid, &status, 0, 0 );
    }

    _job->thread_done();
}

int
XferToDo::start(void){

    if( ! make_request( _job->_servers[_serveridx], PHMT_MR_FILEXFER, &_g, IO_TIMEOUT ) ){
        string status = "FAILED";
        _job->update( &_xid, &status, 0, 0 );
    }

    _job->thread_done();
}

void
TaskToDo::abort(void){
    DEBUG("abort task");
    ACPMRMTaskAbort req;

    if( _state != JOB_TODO_STATE_RUNNING ) return;

    req.set_jobid( _job->_id );
    req.set_taskid( _xid.c_str() );

    // udp fire+forget
    toss_request(udp4_fd, _job->_servers[ _serveridx ], PHMT_MR_TASKABORT, &req );
}

void
TaskToDo::discard(void){

    DEBUG("discard %s %d", _xid.c_str(), _state);
    if( _state == JOB_TODO_STATE_PENDING ){
        _job->depending_x(this);
        _state = JOB_TODO_STATE_FINISHED;
        return;
    }

    if( _state == JOB_TODO_STATE_RUNNING ){
        _job->derunning_x(this);
        _state = JOB_TODO_STATE_FINISHED;
        _job->_servers[ _serveridx ]->_n_task_running --;
        _job->_n_task_running --;
        return;
    }
}

// remove task from run list, send abort request (tcp)
void
TaskToDo::cancel(void){
    ACPMRMTaskAbort req;

    int prevstate = _state;

    discard();

    if( prevstate != JOB_TODO_STATE_RUNNING ) return;

    req.set_jobid( _job->_id );
    req.set_taskid( _xid.c_str() );

    make_request( _job->_servers[_serveridx], PHMT_MR_TASKABORT, &req, IO_TIMEOUT );
}

// same, but udp
void
TaskToDo::cancel_light(void){
    ACPMRMTaskAbort req;

    int prevstate = _state;

    discard();

    if( prevstate != JOB_TODO_STATE_RUNNING ) return;

    req.set_jobid( _job->_id );
    req.set_taskid( _xid.c_str() );

    toss_request(udp4_fd, _job->_servers[ _serveridx ], PHMT_MR_TASKABORT, &req );
}

void
XferToDo::abort(void){
    DEBUG("abort xfer");
    // there is no abort method, just wait...
}
void
XferToDo::cancel(void){
    DEBUG("cancel xfer");
    // there is no cancel method, just wait...
}

void
TaskToDo::finished(int amount){

    _job->inform("task %s finished", _xid.c_str());
    _job->derunning_x(this);

    if( _state == JOB_TODO_STATE_RUNNING ){
        _job->_servers[ _serveridx ]->_n_task_running --;
        _job->_n_task_running --;
    }
    _state = JOB_TODO_STATE_FINISHED;

    // so how slow was it?
    if( amount ){
        _run_time = amount;
    }else{
        _run_time = lr_now() - _run_start;
    }

    _job->_task_run_time += _run_time;

    create_xfers();

    // if this was replaced, abort the replacement (and vv)
    if( _replacedby ) _replacedby->cancel_light();
    if( _replaces   ) _replaces->cancel_light();

}

void
XferToDo::finished(int amount){

    _job->derunning_x(this);
    _state = JOB_TODO_STATE_FINISHED;
    _job->_servers[ _serveridx ]->_n_xfer_running --;
    _job->_servers[ _peeridx   ]->_n_xfer_peering --;
    _job->_n_xfer_running --;

    // tally up file xfer sizes
    _job->_plan[ _job->_stepno ]->_xfer_size += amount;
    _job->_plan[ _job->_stepno ]->_n_xfers_run ++;

}

XferToDo::XferToDo(Job *j, const string *name, int src, int dst){

    unique( &_xid );
    _job         = j;
    _serveridx   = dst;
    _peeridx     = src;
    _state       = JOB_TODO_STATE_PENDING;
    _tries       = 0;
    _delay_until = 0;

    _g.set_jobid(   j->_id );
    _g.set_console( j->_g.console().c_str() );
    _g.set_master(  myipandport.c_str() );
    _g.set_copyid(  _xid.c_str() );
    _g.set_filename( name->c_str() );
    _g.add_location( _job->_servers[src]->name.c_str() );

}

void
TaskToDo::create_xfers(void){

    // files from last step can stay where they are
    if( _job->_stepno == _job->_plan.size() - 1 ) return;

    // for all outfiles
    // new Xfer -> job pend

    int nserv = _job->_servers.size();
    int noutf = _g.outfile_size();

    for(int i=0; i<noutf; i++){
        int dst = i % nserv;

        // if file will be processed on this server, we do not need to copy it
        // instead, make a backup copy on another server
        if( _serveridx == dst ) dst = (i+1) % nserv;

        // just one server?
        if( _serveridx == dst ) continue;

        XferToDo *x = new XferToDo(_job, &_g.outfile(i), _serveridx, dst);
        _job->_pending.push_back(x);
        _job->_xfers.push_back(x);
    }
}

<|endoftext|>


<commit_before>#include "video.h"
#include "misc.h"

#include <iostream>

using namespace std;

Video::Video(string imgFolder, string imgPrefix, int w, int h) {
	imageFolder = imgFolder;
	imagePrefix = imgPrefix;

	width = to_string(w);
	height = to_string(h);
}

void Video::ClearImageFolder() {
	system( ("rm " + imageFolder + "*.ppm").c_str() );
}

void Video::Build(string outputFName, int frameCount) {
	outputFileName = outputFName;
	int framerate = 60;

	//string command = "/usr/local/bin/ffmpeg ";
	string command = "avconv ";
	//command += "-loglevel panic ";
	command += "-r " + to_string(framerate) + " ";
	command += "-s " + width + "x" + height + " ";
	command += "-i " + imageFolder + imagePrefix + "%0" + to_string(NumberLength(frameCount) - 1) + "d.ppm ";
	command += "-vcodec libx264 -crf 25 -pix_fmt yuv420p ";
	command += "-y ";
	command += outputFileName;

	system( command.c_str() );
}

void Video::Open() {
	system( ("open " + outputFileName).c_str() );
}
<commit_msg>Actually fixed video compilation issue<commit_after>#include "video.h"
#include "misc.h"

#include <iostream>

using namespace std;

Video::Video(string imgFolder, string imgPrefix, int w, int h) {
	imageFolder = imgFolder;
	imagePrefix = imgPrefix;

	width = to_string(w);
	height = to_string(h);
}

void Video::ClearImageFolder() {
	system( ("rm " + imageFolder + "*.ppm").c_str() );
}

void Video::Build(string outputFName, int frameCount) {
	outputFileName = outputFName;
	int framerate = 60;

	//string command = "/usr/local/bin/ffmpeg ";
	string command = "avconv ";
	//command += "-loglevel panic ";
	command += "-r " + to_string(framerate) + " ";
	command += "-s " + width + "x" + height + " ";
	command += "-i " + imageFolder + imagePrefix + "%0" + to_string(NumberLength(frameCount - 1)) + "d.ppm ";
	command += "-vcodec libx264 -crf 25 -pix_fmt yuv420p ";
	command += "-y ";
	command += outputFileName;

	system( command.c_str() );
}

void Video::Open() {
	system( ("open " + outputFileName).c_str() );
}
<|endoftext|>


<commit_before>/*
 * Copyright 2009-2019 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <string>
#include <votca/csg/bead.h>
#include <votca/csg/map.h>
#include <votca/csg/topology.h>
#include <votca/tools/matrix.h>
#include <votca/tools/tokenizer.h>
#include <votca/tools/vec.h>

namespace votca {
namespace csg {
class Molecule;
}  // namespace csg
namespace tools {
class Property;
}  // namespace tools
}  // namespace votca

namespace votca {
namespace csg {

using namespace std;

Map::~Map() {
  vector<BeadMap *>::iterator iter;
  for (iter = _maps.begin(); iter != _maps.end(); ++iter) delete (*iter);
  _maps.clear();
}

void Map::Apply() {
  vector<BeadMap *>::iterator iter;
  for (iter = _maps.begin(); iter != _maps.end(); ++iter) (*iter)->Apply();
}

void Map_Sphere::Initialize(Molecule *in, Bead *out, Property *opts_bead,
                            Property *opts_map) {
  BeadMap::Initialize(in, out, opts_bead, opts_map);

  vector<string> beads;
  vector<double> weights;
  vector<double> fweights;

  // get the beads
  string s(_opts_bead->get("beads").value());
  Tokenizer tok_beads(s, " \n\t");
  tok_beads.ToVector(beads);

  // get vector of weights
  Tokenizer tok_weights(_opts_map->get("weights").value(), " \n\t");
  tok_weights.ConvertToVector<double>(weights);

  // check weather weights and # beads matches
  if (beads.size() != weights.size())
    throw runtime_error(
        string("number of subbeads in " + opts_bead->get("name").as<string>() +
               " and number of weights in map " +
               opts_map->get("name").as<string>() + " do not match"));

  // normalize the weights
  double norm = 1. / std::accumulate(weights.begin(), weights.end(), 0.);

  transform(weights.begin(), weights.end(), weights.begin(),
            bind2nd(multiplies<double>(), norm));
  // get the d vector if exists or initialize same as weights
  vector<double> d;
  if (_opts_map->exists("d")) {
    Tokenizer tok_weights(_opts_map->get("d").value(), " \n\t");
    tok_weights.ConvertToVector(d);
    // normalize d coefficients
    norm = 1. / std::accumulate(d.begin(), d.end(), 0.);
    transform(d.begin(), d.end(), d.begin(),
              bind2nd(multiplies<double>(), norm));
  } else {
    // initialize force-weights with weights
    d.resize(weights.size());
    copy(weights.begin(), weights.end(), d.begin());
  }

  // check weather number of d coeffs is correct
  if (beads.size() != d.size()) {
    throw runtime_error(
        string("number of subbeads in " + opts_bead->get("name").as<string>() +
               " and number of d-coefficients in map " +
               opts_map->get("name").as<string>() + " do not match"));
  }

  fweights.resize(weights.size());
  // calculate force weights by d_i/w_i
  for (size_t i = 0; i < weights.size(); ++i) {
    if (weights[i] == 0 && d[i] != 0) {
      throw runtime_error(
          "A d coefficient is nonzero while weights is zero in mapping " +
          opts_map->get("name").as<string>());
    }
    if (weights[i] != 0)
      fweights[i] = d[i] / weights[i];
    else
      fweights[i] = 0;
  }

  for (size_t i = 0; i < beads.size(); ++i) {
    int iin = in->getBeadByName(beads[i]);
    if (iin < 0)
      throw std::runtime_error(
          string("mapping error: molecule " + beads[i] + " does not exist"));
    AddElem(in->getBead(iin), weights[i], fweights[i]);
  }
}

void Map_Sphere::Apply() {
  vector<element_t>::iterator iter;
  vec cg(0., 0., 0.), f(0., 0., 0.), vel(0., 0., 0.);
  bool bPos, bVel, bF;
  bPos = bVel = bF = false;
  _out->ClearParentBeads();

  // the following is needed for pbc treatment
  Topology *top = _out->getParent();
  double max_dist = 0.5 * top->ShortestBoxSize();
  vec r0 = vec(0, 0, 0);
  string name0;
  int id0 = 0;
  if (_matrix.size() > 0) {
    if (_matrix.front()._in->HasPos()) {
      r0 = _matrix.front()._in->getPos();
      name0 = _matrix.front()._in->getName();
      id0 = _matrix.front()._in->getId();
    }
  }

  double M = 0;

  for (iter = _matrix.begin(); iter != _matrix.end(); ++iter) {
    Bead *bead = iter->_in;
    _out->AddParentBead(bead->getId());
    M += bead->getMass();
    if (bead->HasPos()) {
      vec r = top->BCShortestConnection(r0, bead->getPos());
      if (abs(r) > max_dist) {
        cout << r0 << " " << bead->getPos() << endl;
        throw std::runtime_error(
            "coarse-grained bead is bigger than half the box \n (atoms " +
            name0 + " (id " + boost::lexical_cast<string>(id0 + 1) + ")" +
            ", " + bead->getName() + " (id " +
            boost::lexical_cast<string>(bead->getId() + 1) + ")" +
            +" , molecule " +
            boost::lexical_cast<string>(bead->getMolecule()->getId() + 1) +
            ")");
      }
      cg += (*iter)._weight * (r + r0);
      bPos = true;
    }
    if (bead->HasVel()) {
      vel += (*iter)._weight * bead->getVel();
      bVel = true;
    }
    if (bead->HasF()) {
      f += (*iter)._force_weight * bead->getF();
      bF = true;
    }
  }
  _out->setMass(M);
  if (bPos) _out->setPos(cg);
  if (bVel) _out->setVel(vel);
  if (bF) _out->setF(f);
}

/// \todo implement this function
void Map_Ellipsoid::Apply() {
  vector<element_t>::iterator iter;
  vec cg(0., 0., 0.), c(0., 0., 0.), f(0., 0., 0.), vel(0., 0., 0.);
  matrix m(0.);
  bool bPos, bVel, bF;
  bPos = bVel = bF = false;

  // the following is needed for pbc treatment
  Topology *top = _out->getParent();
  double max_dist = 0.5 * top->ShortestBoxSize();
  vec r0 = vec(0, 0, 0);
  if (_matrix.size() > 0) {
    if (_matrix.front()._in->HasPos()) {
      r0 = _matrix.front()._in->getPos();
    }
  }

  int n;
  n = 0;
  _out->ClearParentBeads();
  for (iter = _matrix.begin(); iter != _matrix.end(); ++iter) {
    Bead *bead = iter->_in;
    _out->AddParentBead(bead->getId());
    if (bead->HasPos()) {
      vec r = top->BCShortestConnection(r0, bead->getPos());
      if (abs(r) > max_dist)
        throw std::runtime_error(
            "coarse-grained bead is bigger than half the box");
      cg += (*iter)._weight * (r + r0);
      bPos = true;
    }
    if (bead->HasVel() == true) {
      vel += (*iter)._weight * bead->getVel();
      bVel = true;
    }
    if (bead->HasF()) {
      /// \todo fix me, right calculation should be F_i = m_cg / sum(w_i) *
      /// sum(w_i/m_i*F_i)
      // f += (*iter)._weight * _in->getBeadF((*iter)._in);
      f += (*iter)._force_weight * bead->getF();
      bF = true;
    }

    if ((*iter)._weight > 0 && bead->HasPos()) {
      c += bead->getPos();
      n++;
    }
  }

  if (bPos) _out->setPos(cg);
  if (bVel) _out->setVel(vel);
  if (bF) _out->setF(f);

  if (!_matrix[0]._in->HasPos()) {
    _out->setU(vec(1.0, 0, 0));
    _out->setV(vec(.0, 1, 0));
    _out->setW(vec(.0, 0, 1));
    return;
  }

  // calculate the tensor of gyration
  c = c / (double)n;
  for (iter = _matrix.begin(); iter != _matrix.end(); ++iter) {
    if ((*iter)._weight == 0) continue;
    Bead *bead = iter->_in;
    vec v = bead->getPos() - c;
    // v = vec(1, 0.5, 0) * 0.*(drand48()-0.5)
    //    + vec(0.5, -1, 0) * (drand48()-0.5)
    //    + vec(0, 0, 1) * (drand48()-0.5);

    // Normalize the tensor with 1/number_of_atoms_per_bead
    m[0][0] += v.getX() * v.getX() / (double)_matrix.size();
    m[0][1] += v.getX() * v.getY() / (double)_matrix.size();
    m[0][2] += v.getX() * v.getZ() / (double)_matrix.size();
    m[1][1] += v.getY() * v.getY() / (double)_matrix.size();
    m[1][2] += v.getY() * v.getZ() / (double)_matrix.size();
    m[2][2] += v.getZ() * v.getZ() / (double)_matrix.size();
  }
  m[1][0] = m[0][1];
  m[2][0] = m[0][2];
  m[2][1] = m[1][2];

  // calculate the eigenvectors
  matrix::eigensystem_t es;
  m.SolveEigensystem(es);

  // vec eigenv1=es.eigenvecs[0];
  // vec eigenv2=es.eigenvecs[1];
  // vec eigenv3=es.eigenvecs[2];

  /*    _out->seteigenvec1(eigenv1);
      _out->seteigenvec2(eigenv2);
      _out->seteigenvec3(eigenv3);
    */

  vec u = es.eigenvecs[0];
  vec v = _matrix[1]._in->getPos() - _matrix[0]._in->getPos();
  v.normalize();

  _out->setV(v);

  vec w = _matrix[2]._in->getPos() - _matrix[0]._in->getPos();
  w.normalize();

  if ((v ^ w) * u < 0) u = vec(0., 0., 0.) - u;
  _out->setU(u);

  // write out w
  w = u ^ v;
  w.normalize();
  _out->setW(w);

  // out.BeadV(_out) = v;

  // out.BeadW(_out) = es.eigenvecs[2];
}

}  // namespace csg
}  // namespace votca
<commit_msg>Added numeric include<commit_after>/*
 * Copyright 2009-2019 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <numeric>
#include <string>
#include <votca/csg/bead.h>
#include <votca/csg/map.h>
#include <votca/csg/topology.h>
#include <votca/tools/matrix.h>
#include <votca/tools/tokenizer.h>
#include <votca/tools/vec.h>

namespace votca {
namespace csg {
class Molecule;
}  // namespace csg
namespace tools {
class Property;
}  // namespace tools
}  // namespace votca

namespace votca {
namespace csg {

using namespace std;

Map::~Map() {
  vector<BeadMap *>::iterator iter;
  for (iter = _maps.begin(); iter != _maps.end(); ++iter) delete (*iter);
  _maps.clear();
}

void Map::Apply() {
  vector<BeadMap *>::iterator iter;
  for (iter = _maps.begin(); iter != _maps.end(); ++iter) (*iter)->Apply();
}

void Map_Sphere::Initialize(Molecule *in, Bead *out, Property *opts_bead,
                            Property *opts_map) {
  BeadMap::Initialize(in, out, opts_bead, opts_map);

  vector<string> beads;
  vector<double> weights;
  vector<double> fweights;

  // get the beads
  string s(_opts_bead->get("beads").value());
  Tokenizer tok_beads(s, " \n\t");
  tok_beads.ToVector(beads);

  // get vector of weights
  Tokenizer tok_weights(_opts_map->get("weights").value(), " \n\t");
  tok_weights.ConvertToVector<double>(weights);

  // check weather weights and # beads matches
  if (beads.size() != weights.size())
    throw runtime_error(
        string("number of subbeads in " + opts_bead->get("name").as<string>() +
               " and number of weights in map " +
               opts_map->get("name").as<string>() + " do not match"));

  // normalize the weights
  double norm = 1. / std::accumulate(weights.begin(), weights.end(), 0.);

  transform(weights.begin(), weights.end(), weights.begin(),
            bind2nd(multiplies<double>(), norm));
  // get the d vector if exists or initialize same as weights
  vector<double> d;
  if (_opts_map->exists("d")) {
    Tokenizer tok_weights(_opts_map->get("d").value(), " \n\t");
    tok_weights.ConvertToVector(d);
    // normalize d coefficients
    norm = 1. / std::accumulate(d.begin(), d.end(), 0.);
    transform(d.begin(), d.end(), d.begin(),
              bind2nd(multiplies<double>(), norm));
  } else {
    // initialize force-weights with weights
    d.resize(weights.size());
    copy(weights.begin(), weights.end(), d.begin());
  }

  // check weather number of d coeffs is correct
  if (beads.size() != d.size()) {
    throw runtime_error(
        string("number of subbeads in " + opts_bead->get("name").as<string>() +
               " and number of d-coefficients in map " +
               opts_map->get("name").as<string>() + " do not match"));
  }

  fweights.resize(weights.size());
  // calculate force weights by d_i/w_i
  for (size_t i = 0; i < weights.size(); ++i) {
    if (weights[i] == 0 && d[i] != 0) {
      throw runtime_error(
          "A d coefficient is nonzero while weights is zero in mapping " +
          opts_map->get("name").as<string>());
    }
    if (weights[i] != 0)
      fweights[i] = d[i] / weights[i];
    else
      fweights[i] = 0;
  }

  for (size_t i = 0; i < beads.size(); ++i) {
    int iin = in->getBeadByName(beads[i]);
    if (iin < 0)
      throw std::runtime_error(
          string("mapping error: molecule " + beads[i] + " does not exist"));
    AddElem(in->getBead(iin), weights[i], fweights[i]);
  }
}

void Map_Sphere::Apply() {
  vector<element_t>::iterator iter;
  vec cg(0., 0., 0.), f(0., 0., 0.), vel(0., 0., 0.);
  bool bPos, bVel, bF;
  bPos = bVel = bF = false;
  _out->ClearParentBeads();

  // the following is needed for pbc treatment
  Topology *top = _out->getParent();
  double max_dist = 0.5 * top->ShortestBoxSize();
  vec r0 = vec(0, 0, 0);
  string name0;
  int id0 = 0;
  if (_matrix.size() > 0) {
    if (_matrix.front()._in->HasPos()) {
      r0 = _matrix.front()._in->getPos();
      name0 = _matrix.front()._in->getName();
      id0 = _matrix.front()._in->getId();
    }
  }

  double M = 0;

  for (iter = _matrix.begin(); iter != _matrix.end(); ++iter) {
    Bead *bead = iter->_in;
    _out->AddParentBead(bead->getId());
    M += bead->getMass();
    if (bead->HasPos()) {
      vec r = top->BCShortestConnection(r0, bead->getPos());
      if (abs(r) > max_dist) {
        cout << r0 << " " << bead->getPos() << endl;
        throw std::runtime_error(
            "coarse-grained bead is bigger than half the box \n (atoms " +
            name0 + " (id " + boost::lexical_cast<string>(id0 + 1) + ")" +
            ", " + bead->getName() + " (id " +
            boost::lexical_cast<string>(bead->getId() + 1) + ")" +
            +" , molecule " +
            boost::lexical_cast<string>(bead->getMolecule()->getId() + 1) +
            ")");
      }
      cg += (*iter)._weight * (r + r0);
      bPos = true;
    }
    if (bead->HasVel()) {
      vel += (*iter)._weight * bead->getVel();
      bVel = true;
    }
    if (bead->HasF()) {
      f += (*iter)._force_weight * bead->getF();
      bF = true;
    }
  }
  _out->setMass(M);
  if (bPos) _out->setPos(cg);
  if (bVel) _out->setVel(vel);
  if (bF) _out->setF(f);
}

/// \todo implement this function
void Map_Ellipsoid::Apply() {
  vector<element_t>::iterator iter;
  vec cg(0., 0., 0.), c(0., 0., 0.), f(0., 0., 0.), vel(0., 0., 0.);
  matrix m(0.);
  bool bPos, bVel, bF;
  bPos = bVel = bF = false;

  // the following is needed for pbc treatment
  Topology *top = _out->getParent();
  double max_dist = 0.5 * top->ShortestBoxSize();
  vec r0 = vec(0, 0, 0);
  if (_matrix.size() > 0) {
    if (_matrix.front()._in->HasPos()) {
      r0 = _matrix.front()._in->getPos();
    }
  }

  int n;
  n = 0;
  _out->ClearParentBeads();
  for (iter = _matrix.begin(); iter != _matrix.end(); ++iter) {
    Bead *bead = iter->_in;
    _out->AddParentBead(bead->getId());
    if (bead->HasPos()) {
      vec r = top->BCShortestConnection(r0, bead->getPos());
      if (abs(r) > max_dist)
        throw std::runtime_error(
            "coarse-grained bead is bigger than half the box");
      cg += (*iter)._weight * (r + r0);
      bPos = true;
    }
    if (bead->HasVel() == true) {
      vel += (*iter)._weight * bead->getVel();
      bVel = true;
    }
    if (bead->HasF()) {
      /// \todo fix me, right calculation should be F_i = m_cg / sum(w_i) *
      /// sum(w_i/m_i*F_i)
      // f += (*iter)._weight * _in->getBeadF((*iter)._in);
      f += (*iter)._force_weight * bead->getF();
      bF = true;
    }

    if ((*iter)._weight > 0 && bead->HasPos()) {
      c += bead->getPos();
      n++;
    }
  }

  if (bPos) _out->setPos(cg);
  if (bVel) _out->setVel(vel);
  if (bF) _out->setF(f);

  if (!_matrix[0]._in->HasPos()) {
    _out->setU(vec(1.0, 0, 0));
    _out->setV(vec(.0, 1, 0));
    _out->setW(vec(.0, 0, 1));
    return;
  }

  // calculate the tensor of gyration
  c = c / (double)n;
  for (iter = _matrix.begin(); iter != _matrix.end(); ++iter) {
    if ((*iter)._weight == 0) continue;
    Bead *bead = iter->_in;
    vec v = bead->getPos() - c;
    // v = vec(1, 0.5, 0) * 0.*(drand48()-0.5)
    //    + vec(0.5, -1, 0) * (drand48()-0.5)
    //    + vec(0, 0, 1) * (drand48()-0.5);

    // Normalize the tensor with 1/number_of_atoms_per_bead
    m[0][0] += v.getX() * v.getX() / (double)_matrix.size();
    m[0][1] += v.getX() * v.getY() / (double)_matrix.size();
    m[0][2] += v.getX() * v.getZ() / (double)_matrix.size();
    m[1][1] += v.getY() * v.getY() / (double)_matrix.size();
    m[1][2] += v.getY() * v.getZ() / (double)_matrix.size();
    m[2][2] += v.getZ() * v.getZ() / (double)_matrix.size();
  }
  m[1][0] = m[0][1];
  m[2][0] = m[0][2];
  m[2][1] = m[1][2];

  // calculate the eigenvectors
  matrix::eigensystem_t es;
  m.SolveEigensystem(es);

  // vec eigenv1=es.eigenvecs[0];
  // vec eigenv2=es.eigenvecs[1];
  // vec eigenv3=es.eigenvecs[2];

  /*    _out->seteigenvec1(eigenv1);
      _out->seteigenvec2(eigenv2);
      _out->seteigenvec3(eigenv3);
    */

  vec u = es.eigenvecs[0];
  vec v = _matrix[1]._in->getPos() - _matrix[0]._in->getPos();
  v.normalize();

  _out->setV(v);

  vec w = _matrix[2]._in->getPos() - _matrix[0]._in->getPos();
  w.normalize();

  if ((v ^ w) * u < 0) u = vec(0., 0., 0.) - u;
  _out->setU(u);

  // write out w
  w = u ^ v;
  w.normalize();
  _out->setW(w);

  // out.BeadV(_out) = v;

  // out.BeadW(_out) = es.eigenvecs[2];
}

}  // namespace csg
}  // namespace votca
<|endoftext|>